eivd Framework sous Linux Bastien Andrès Projet de Diplôme Framework sous Linux

Cahier des charges Donnée de base Le but du projet est la conception et la réalisation ’un Framewok destiné à être utilisé pour l’enseignement. Différentes approches de solution sont à envisager, dont une solution basée sur une librairie graphique (, LessTif, Gtk, etc.) ou utilisant un Framework complexe existant (, wxWindows) ou encore .

Cahier des charges Le Framework est destiné à servir d’illustration d’une conception orientée objet et pour l’introduction du concept dans le cadre d’un cours et laboratoire; la complexité en est en conséquence réduite. Les différentes options sont à considérer:

Utilisation d’une librairie graphique comme LessTif ou pour la gestion de l’interface utilisateur;

Possibilité d’utiliser un Framework existant du domaine publique pour la réalisation du Framework désiré;

Ajout de la fonctionnalité du multi−fenêtrage à un Framework à fenêtre unique (LAF);

Adaptation du Framework mis à disposition par Java (réduction de sa complexité).

Page 1/112 eivd Framework sous Linux Bastien Andrès Table des matières 1.Résumé...... 7 2.Introduction...... 8 2.1.Définition de ’Framework’...... 8 3.Techniques...... 9 3.1.Langages et outils...... 9 3.1.1....... 9 3.1.1.1.Pointeurs de fonctions...... 9 3.1.1.2.Fonctions ’Callback’...... 9 3.1.1.3.Modèle objet en C...... 9 3.1.1.4.Macros...... 10 3.1.1.5.Gestion de la mémoire...... 10 3.1.1.6.Informations relatives à l’application...... 10 3.1.2.C++...... 10 3.1.2.1.Contrôle des types à la compilation...... 10 3.1.2.2.’Templates’...... 10 3.1.3.Java...... 11 3.1.3.1.’Garbage Collector’...... 11 3.1.3.2.Threads...... 11 3.1.3.3.’Reflection’...... 11 3.1.4.Compilation...... 11 3.1.4.1.Scripts...... 11 3.1.4.2.Makefile...... 12 3.1.4.3.Préprocessing...... 12 3.2.Concepts...... 12 3.2.1.Modèle ’Model−View−Controller’...... 12 3.2.2.Dualité Application−Document...... 13 3.2.3.Programmation événementielle...... 13 3.2.4.Programmation par composants...... 13 3.3.Gestion Graphique...... 13 3.3.1.Régions...... 13 3.3.2.Contexte graphique...... 14 3.3.3.Programmation graphique événementielle...... 14 3.3.4.’Window Manager’...... 14 3.3.5.Modèles d’application multi−fenêtres...... 15 3.3.6.’Xwindow’...... 15 3.3.7.Répartition des tâches d’affichage (X−WM−Lib−App)...... 15 3.4.Technique des librairies graphiques...... 15 3.4.1.Concepts...... 15 3.4.1.1.Description de la composition graphique...... 15 3.4.1.2.’Layout Management’...... 16 3.4.1.3.’Top Level’...... 16 3.4.1.4.Utilisation des contextes graphiques...... 16 3.4.2.Déroulement standard d’une application...... 16 3.4.2.1.Introduction...... 16 3.4.2.2.Création des objets...... 16 3.4.2.3.Assignation des propriétés...... 17 3.4.2.4.Composition...... 17 3.4.2.5.Réalisation...... 17 3.4.2.6.Lancement du processus événementiel...... 17 3.4.2.7.Schéma de traitement des événements...... 17 3.4.2.8.Mise à jour de l’état de l’application...... 17

Page 2/112 eivd Framework sous Linux Bastien Andrès 3.4.2.9.Terminaison de l’application...... 17 3.5.Librairies graphiques ’standards’ sous Linux...... 18 3.5.1.Introduction...... 18 3.5.2.Motif / Lesstif...... 18 3.5.3.Gtk+ / Gtk−− / libsigc++...... 18 3.5.3.1.Gtk...... 18 3.5.3.2.Gtk+...... 19 3.5.3.3.Glib...... 19 3.5.3.4.Gdk...... 19 3.5.3.5.Gtk−−...... 19 3.5.3.6.libsigc++...... 19 3.5.3.7.Gnome...... 20 3.5.4.QT...... 20 3.5.5.Comparaison...... 20 3.5.6.Conclusion...... 20 3.6.Fonctionnalités graphiques de Java...... 21 3.6.1.Interface AWT...... 21 3.6.2.Interface ...... 21 3.6.3.Comparaison et interactions de Swing et de l’AWT...... 21 3.6.4.Composants (Widgets)...... 21 3.6.5.’Layout Management’...... 21 3.6.6.Absence de ’Layout Mangement’...... 22 3.6.7.Window...... 22 3.6.8.Evénements...... 22 3.6.8.1.Events...... 22 3.6.8.2.Listeners...... 22 3.6.8.3.Action...... 23 3.6.8.4.Menus...... 23 3.6.8.5.Contexte Graphique...... 23 3.6.8.6.Threads & Swing...... 23 4.Concepts développés...... 24 4.1.Méthode de description de l’interface graphique...... 24 4.2.Modèle de programmation par redirection des événements...... 24 5.Réalisations...... 25 5.1.Ajout de la fonctionnalité de multi−fenêtrage...... 25 5.2.Framework en Java...... 25 5.2.1.Introduction...... 25 5.2.2.Techniques développées...... 25 5.2.3.Méthode de démarrage de l’application...... 25 5.2.4.Définition de l’interface graphique...... 27 5.2.5.Mise en oeuvre des événements Swing...... 28 5.3.Framework en GTK−−...... 32 5.3.1.Introduction...... 32 5.3.2.Référencement...... 32 5.3.3.Schéma de définition des traite−événements...... 32 5.3.4.Hiérarchie des composants du Framework...... 33 5.3.5.Mécanisme de définition par dérivation de la méthode ’assign()’...... 34 5.3.6.Structure de l’application utilisateur...... 34 5.4.Simplification d’un Framework existant...... 34 5.5.Comparaisons des solutions...... 34 6.Conclusion...... 35 7.Bibliographie...... 36 7.1.Références bibliographiques...... 36

Page 3/112 eivd Framework sous Linux Bastien Andrès 7.2.Références WWW...... 36 7.2.1.C / C++...... 36 7.2.2.Callback, signal/Slot et Messages...... 36 7.2.3.X Windows et XFree86...... 36 7.2.4.OSF/Motif et LessTif...... 36 7.2.5.Gtk+ / Gtk−−...... 37 7.2.6.FOX...... 37 7.2.7.JX...... 37 7.2.8.Java...... 37 8.Annexes...... 38 8.1.Projet de semestre...... 38 8.1.1.Introduction...... 38 8.1.2.Introduction des notions principales...... 38 8.1.3.Travail de recherche...... 39 8.1.3.1.Frameworks disponibles sous Linux...... 39 8.1.3.2.Description des principaux frameworks ’libres’ disponibles sous Linux...... 39 8.1.4.Description du framework ’V’...... 40 8.1.5.Mise en place de la plate−forme...... 41 8.1.6.Installations spécifiques...... 41 8.1.7.Apprentissage du travail sur la plate−forme Linux...... 42 8.1.8.Étude des mécanismes de programmation...... 42 8.1.9.Réalisation d’une ébauche de framework...... 42 8.1.10.Conclusions...... 42 8.2.Journal de Projet...... 43 8.2.1.Semaine du 2 au 6 octobre...... 43 8.2.2.Semaine du 9 au 13 octobre...... 43 8.2.3.Semaine du 16 au 20 octobre...... 43 8.2.4.Semaine du 23 au 27 octobre...... 43 8.2.5.Semaine du 30 octobre au 3 novembre...... 43 8.2.6.Semaine du 6 au 10 novembre...... 44 8.2.7.Semaine du 13 au 17 novembre...... 44 8.2.8.Semaine du 20 au 24 novembre...... 44 8.2.9.Semaine du 27 novembre au 1er décembre...... 44 8.2.10.Semaine du 4 au 8 décembre...... 44 8.2.11.Semaine du 11 au 15 décembre...... 44 8.2.12.Semaine du 18 au 21 décembre...... 45 8.3.Documentation Utilisateur...... 45 8.3.1.Utilisation du Framework en Java...... 45 8.3.2.Utilisation du Framework en Gtk−−...... 46 8.4.Implémentation du Framework en Java...... 47 8.4.1.Introduction...... 47 8.4.2.Classes...... 47 8.4.3.Héritage...... 47 8.4.4.Description...... 48 8.4.4.1.JfApplication...... 48 8.4.4.2.JfAppDef...... 48 8.4.4.3.JfTarget...... 48 8.4.4.4.JfLayout...... 48 8.4.4.5.JfFrame...... 49 8.4.4.6.JfComponent...... 49 8.4.4.7.JfPane...... 49 8.4.4.8.JfButton...... 50 8.4.4.9.JfTextField...... 50

Page 4/112 eivd Framework sous Linux Bastien Andrès 8.4.4.10.JfMenuBar...... 50 8.4.4.11.JfMenu...... 50 8.4.4.12.JfMenuItem...... 50 8.4.5.Agrégation...... 51 8.4.6.Construction...... 51 8.4.7.Dérivation...... 52 8.5.Implémentation du Framework en GTK−−...... 52 8.5.1.Introduction...... 52 8.5.2.Classes...... 52 8.5.3.Héritage...... 52 8.5.4.Schéma d’initialisation des composants...... 53 8.5.5.Description...... 53 8.5.5.1.GfMain...... 54 8.5.5.2.GfUtil...... 54 8.5.5.3.GfApplication...... 54 8.5.5.4.GfObject...... 54 8.5.5.5.GfWidget...... 54 8.5.5.6.GfContainer...... 55 8.5.5.7.GfBin...... 55 8.5.5.8.GfButton...... 55 8.5.5.9.GfWindow...... 55 8.5.5.10.GfEventButton...... 56 8.5.5.11.GfEventWindow...... 56 8.5.5.12.Gf Action...... 56 8.5.5.13.GfButtonAction...... 56 8.5.5.14.GfWindowAction...... 56 8.5.5.15.GfApp...... 56 8.6.Listing du code réalisé...... 57 8.6.1.Framework en Java...... 57 8.6.1.1.JfApplication...... 57 8.6.1.2.JfAppDef...... 59 8.6.1.3.JfTarget...... 60 8.6.1.4.JfLayout...... 62 8.6.1.5.JfFrame...... 66 8.6.1.6.JfComponent...... 71 8.6.1.7.JfPane...... 72 8.6.1.8.JfButton...... 74 8.6.1.9.JfTextField...... 76 8.6.1.10.JfMenuBar...... 78 8.6.1.11.JfMenuItem...... 80 8.6.1.12.JfMenu...... 82 8.6.1.13.MyApp (exemple d’application)...... 84 8.6.1.14.Makefile du Framework en Java...... 89 8.6.2.Framework en Gtk−−...... 90 8.6.2.1.GfMain...... 90 8.6.2.2.GfUtil...... 90 8.6.2.3.GfApplication...... 91 8.6.2.4.GfObject...... 92 8.6.2.5.GfWidget...... 93 8.6.2.6.GfContainer...... 94 8.6.2.7.GfBin...... 96 8.6.2.8.GfButton...... 97 8.6.2.9.GfWindow...... 99

Page 5/112 eivd Framework sous Linux Bastien Andrès 8.6.2.10.GfEventButton...... 101 8.6.2.11.GfEventWindow...... 103 8.6.2.12.GfAction...... 105 8.6.2.13.GfButtonAction...... 106 8.6.2.14.GfWindowAction...... 107 8.6.2.15.GfApp (Exemple d’application)...... 109 8.6.2.16.Makefile du Framework en Gtk−−...... 111

Page 6/112 eivd Framework sous Linux Bastien Andrès 1. Résumé Ce projet a pour but l’étude et la réalisation d’un Framework à but pédagogique sous Linux. L’utilisateur de ce Framework doit pouvoir appréhender le concept de Framework d’application et celui de programmation événementielle par le biais de la réalisation de courts laboratoires. Le Framework doit donc être simple et d’apprentissage aisé. Le Framework doit fournir des solutions simples aux tâches souvent complexes d’initialisation de l’application et de définition de l’apparence de l’interface graphique. Diverses approches de réalisation sont dans une premier temps évaluées, puis certaines choisies afin de proposer une solution. Le fonctionnement des librairies graphiques (C, C++) est étudié, puis quelques−unes d’entre elles (Motif/LessTif, GTK+, Qt) sont comparées dans l’optique de leur utilisation comme base de l’architecture du Framework. Le langage Java et ses fonctionnalités orientées interface graphique sont évalués comme environnement de programmation. Dans ce cas, le but est de proposer un ensemble de classes constituant un Framework simple réduisant la complexité de l’API de Java. Les environnements choisis, les techniques étudiées et les solutions développées sont validées par la réalisation de prototypes intégrant la mise en oeuvre de ceux−ci.

Page 7/112 eivd Framework sous Linux Bastien Andrès 2. Introduction La programmation d’un Framework nécessite l’étude de nombreux concepts et techniques de programmation. De fait ce document propose dans une première partie un aperçu des modèles utilisés dans ce domaine. Puis, il sera décrit les techniques développées dans le cadre de ce travail. Il sera ensuite expliqué la manière dont le code a été réalisé. Enfin, les diverses solutions seront comparées. 2.1. Définition de ’Framework’ Le premier terme intervenant dans le projet est celui de Framework. Ce terme peut être traduit en français par squelette, échafaudage ou cadre. Ces traductions reflètent bien le concept informatique qu’il décrit. En effet, un Framework est un ensemble de composants constituant un cadre de travail pour la réalisation d’une tâche. Il peut s’agir de tous types de tâches, néanmoins, dans le cadre de ce projet, il s’agira de l’écriture d’applications. Dans ce cas, le Framework met à disposition un squelette autour duquel les applications seront construites et dont il en constituera les fondations. Le Framework fournit alors les structures de base d’une application classique comme l’exécutable principal tandis que le programmeur écrit son code dans des fonctions, méthodes et classes spécifiques à l’application à créer et qui viendront s’assembler sur ses bases. Cette approche est radicalement différente de celle de l’utilisation de librairies, celles−ci étant des outils utilisés par le programme de l’utilisateur, alors que le Framework utilise le code de l’utilisateur pour définir son comportement spécifique. Il est à noter qu’il existe des Frameworks spécialisés dans l’écriture de certains genres d’applications alors que d’autres sont plus généralistes. De plus, le niveau de fonctionnalité fourni par les différents Frameworks varient énormément, nécessitant de la part du programmeur la quantité de travail complémentaire. Dans le cadre de ce projet, il convient de préciser le type de Framework qui est considéré. En effet, celui−ci doit permettre aux étudiants d’appréhender pratiquement l’utilisation d’un Framework. Dans ce but, il est nécessaire qu’il soit simple d’accès et de taille raisonnable afin de permettre une prise en main rapide et l’écriture d’applications simples à but didactique en peu de temps.

Page 8/112 eivd Framework sous Linux Bastien Andrès 3. Techniques 3.1. Langages et outils 3.1.1. C Le langage C est quasiment incontournable, du fait de son efficacité et qu’il s’agit de celui avec lequel est réalisé la base des systèmes d’exploitation. De ce fait, lorsqu’on travaille au niveau des librairies, on y est très vite confronté. De nombreuses librairies graphiques utilisent ce langage d’une part car elles font appel à des fonctions du système d’exploitation, d’autre part car le code réalisé doit être rapide et efficace. 3.1.1.1. Pointeurs de fonctions Le pointeur de fonction est une fonctionnalité très importante du langage C. En effet, il permet de définir par une variable un traitement à effectuer. La modification de la variable entraînera la modification du traitement. Ces pointeurs sont utilisés par la méthode de ’Callback’ et pour simuler le concept de méthode en C. 3.1.1.2. Fonctions ’Callback’ Le mécanisme de ’Callback’ est un usage particulier des pointeurs de fonctions. Il s’agit de définir le traitement à assigner à un événement en provenance du matériel. La partie du code devant faire appel au traitement ne faisant pas partie du code que le programmeur génère mais plutôt de celui des librairies, l’usage de pointeurs de fonctions est nécessaire afin de pouvoir définir le traitement. En effet, sans l’utilisation de pointeur, il serait nécessaire d’éditer le code de la librairie pour y modifier l’appel. Ce qui entraînerait une recompilation de celle−ci. Une extension de ce concept peut être réalisée dans le code utilisateur. En effet, les librairies implémentant des mécanismes complexes de Callback permettent généralement d’utiliser ceux−ci pour réaliser des liaisons entre divers éléments du code utilisateur. Les librairies graphiques font un usage important de ces techniques pour traiter les événements générés par l’utilisateur par le biais des périphériques d’entrée (principalement la souris et le clavier).

3.1.1.3. Modèle objet en C Le langage C n’est pas orienté objet. En effet, sa syntaxe ne permet pas aisément de grouper en des objets les données et les fonctions de traitement apparentés à ces données. De même, les concept fondamentaux d’héritage et de polymorphisme en sont absent. Néanmoins, il est possible d’implémenter ces mécanismes. Pour preuve, le langage C++ est converti en C par le pré−processeur. Par conception, le C++ impose un grand nombre de conventions, certes intéressantes du point de vue de la cohérence du typage, mais contraignantes lorsqu’il s’agit de produire du code rapide et efficace. De même, le schéma de contrôle des dépendances du C++ est fort restrictif et rend l’écriture de référencement croisés fort lourde. Pour ces raisons, le langage C++ n’est pas directement utilisé pour l’implémentation de la plupart des librairies. Le modèle objet étant toutefois désiré, il est alors nécessaire de recréer ses mécanismes en C. La syntaxe obtenue en est néanmoins assez lourde. La partie donnée des objets est créée en utilisant des structures. L’héritage simple peut être implémenté en incluant la structure parente comme premier élément de la structure dérivée. Cela permet le polymorphisme car le pointeur sur une structure parente ou dérivée est alors le même. Les méthodes de ces objets peuvent être réalisées de plusieurs manières. Vu que les méthodes ne peuvent être que des fonctions, l’implémentation en est plus simple et il est possible de rajouter sans problèmes des méthodes aux objets. La liaison des méthodes aux données est réalisée par le biais de pointeurs de fonction insérés dans la structure de données. L’appel d’une méthode peut alors être effectué de plusieurs manières. La première est l’appel direct de la fonction. Celles−ci suivant un schéma de nommage spécifique permettant de définir quelle ’classe’ est concernée par la ’méthode’. La deuxième méthode est l’appel de la fonction

Page 9/112 eivd Framework sous Linux Bastien Andrès en utilisant le pointeur se trouvant dans la structure. La syntaxe se rapproche de celle du C++. Néanmoins, toutes les fonctions destinées à être utilisées comme une méthode ont comme premier argument un pointeur sur l’instance concernée par l’appel. La troisième méthode d’appel d’un traitement utilise le mécanisme de ’signaux’ qui est traité ci−dessous.

3.1.1.4. Macros Afin d’alléger l’écriture des programmes sans en réduire la performance, il est fait appel aux Macros. Celles−ci permettent par exemple de réaliser des ’casting’ de types en gardant une syntaxe agréable. De nombreuses fonctionnalités orientées objet ont recours à des Macros lorsqu’elles sont écrites en C. 3.1.1.5. Gestion de la mémoire La gestion de la mémoire est un point sensible de la programmation, particulièrement en C. En effet, il est quasiment impossible d’écrire une application d’importance sans avoir de problème lié à la gestion de l’allocation et surtout à la libération de mémoire et l’usage des pointeurs. Certaines librairies graphiques proposent alors des méthodes pour la création et la destruction des ’objets’. Du fait qu’après la phase d’initialisation, le contrôle est passé à la librairie, celle−ci peut effectuer une gestion des objets. Il est alors nécessaire d’utiliser les fonctions mises à disposition par la librairie pour la création et la destruction des instances. Les ’objets’ ainsi créés sont alors gérés par la librairie et un contrôle est effectué lors de leur utilisation de manière à éviter des incohérences. Un mécanisme de comptage de références est mis en place afin de pouvoir détruire un objet qui n’est plus référencé. De la sorte, si le programmeur utilise les fonctions de la librairie pour gérer les objets, une grande partie du travail délicat lui est épargné, réduisant ainsi de façon importante les sources d’erreurs. 3.1.1.6. Informations relatives à l’application En relation avec la gestion de la mémoire, un mécanisme est présent dans certaines librairies permettant d’accéder aux informations de gestion de celle−ci. Il devient ainsi possible connaître divers paramètres de l’application pendant son exécution. Il est alors possible d’utiliser ces informations à des fins de déboggage. 3.1.2. C++ Le langage C++ présente une orientation objet. Cette caractéristique en fait un candidat pour la réalisation des librairies graphique destinées à la réalisation d’interfaces utilisateur. En effet, les composants de telles interfaces se prêtent parfaitement à une construction orientée objet. La notion d’héritage permet la spécialisation des composants. Le polymorphisme permet d’insérer dans un conteneur tout type de composant. Néanmoins, le schéma de référencement de ce langage est assez contraignant et force à utiliser des mécanismes complexes lorsqu’il s’agit d’effectuer des actions réciproques entre plusieurs objets, ce qui est fréquent lors de la mise à jour de l’état d’une interface utilisateur. De ce fait, les librairies écrites en C++ fournissent généralement des mécanismes effectuant une partie importante des tâches de communication inter−objets. 3.1.2.1. Contrôle des types à la compilation En C++ le contrôles des types a lieu à la compilation. Ce mécanisme permet de s’assurer de la cohérence des objets utilisés lors d’assignation et d’appel de fonctions. Cela permet d’éviter de nombreuses erreurs. Lors de la mise en oeuvre de mécanismes de communication inter−objets, il est intéressant de pouvoir profiter de cette possibilité. Ainsi, les mécanismes implémentés dans les librairies ont recours à diverses fonctionnalités du langage pour permettre ce contrôle. 3.1.2.2. ’Templates’ Les templates, permettant la création de classes génériques, sont utilisés pour définir des traitements

Page 10/112 eivd Framework sous Linux Bastien Andrès de base. Cela permet de générer des mécanismes de communication utilisables dans de nombreuses situations. En effet, il devient possible de définir des appels de fonctions de plusieurs types. Les traitements peuvent alors être des fonctions statique, des méthodes statiques ou d’instances et le nombre des arguments peut être variable. Moyennant l’utilisation des fonctionnalités fournies par la librairie, tout type de communication devient ainsi possible et laisse un grande marge d’action au programmeur quant à la définition des traitement des événements. 3.1.3. Java Java est un langage fortement orienté objet. Il est portable au niveau du ’bytecode’. Cela en fait un candidat intéressant pour la réalisation d’interfaces graphiques. Ainsi on peut créer une interface utilisateur ayant une apparence fortement similaire indépendamment de la plate−forme utilisée. De fait, ce langage fourni depuis une de ses premières version une API très complète permettant la réalisation d’interfaces graphiques. De nombreuses caractéristiques de ce langage sont intéressantes. Celles−ci sont présentées ci−après dans l’optique de la réalisation d’interface utilisateur. 3.1.3.1. ’Garbage Collector’ Java possède un mécanisme interne de gestion des objets permettant leur destruction automatique lorsqu’ils ne sont plus référencés. Cela offre au programmeur une certaine assurance que les objets seront détruit lorsqu’ils ne seront plus utiles. De même, cela évite une grande partie des erreurs provenant de l’utilisation d’objets déjà détruits. Pour la programmation graphique cela est important car les objets font souvent appel à de nombreux référencements. Ainsi de nombreuses erreurs peuvent être évitées. 3.1.3.2. Threads Java est multi−thread de base. Le concept faisant partie des fondements de ce langage. Ainsi il est très simple de programmer ceux−ci. Lors de l’utilisation des fonctionnalités graphiques fournies par ce langage, plusieurs threads sont automatiquement créés, un de ceux−ci s’occupant de la réaction aux événements utilisateurs et effectuant la tâche d’affichage graphique.

3.1.3.3. ’Reflection’ Le langage possède une API appelée ’Reflection’. Celle−ci permet de travailler avec des objets représentant les composants du langage, tels les classes et les membres de celles−ci, dont les méthodes. Il est alors possible de créer des objets méthodes représentant une méthode particulière. De même, il est possible de lister les membres d’une certaine classe et d’y accéder. De même, il est possible de passer une instance comme ’Objet’ à une méthode, celle−ci pourra à l’aide des fonctions de l’API Reflection de connaître de quelle classe l’objet est instance. Grâce à ces fonctionnalités, il est possible de créer des pointeurs de méthodes et de vérifier la cohérence des types d’arguments. 3.1.4. Compilation La compilation est une étape obligatoire dans la plupart des langages. Si dans le cas de projets de faible ampleur cette étape est triviale, elle peut s’avérer assez complexe lors de grandes réalisations, ou lorsque des librairies particulières sont employées. De ce fait, divers outils existent dans le but de simplifier ou d’automatiser cette tâche. Quelques−uns d’entre−eux, en relation avec la réalisation d’un Framework sont décrits ci−dessous. 3.1.4.1. Scripts Le terme script n’est pas forcément défini avec précision. Dans la plupart des cas, il s’agit de programmes de faible taille, écrits dans un langage interprété, ne nécessitant donc pas de compilation. Ainsi, sous , il est fait un usage très fréquent de ce genre de programme, écrits dans le langage d’un ’shell’ et permettant l’automatisation de tâches répétitives ou courantes. Ceux−ci sont aussi utilisés dans

Page 11/112 eivd Framework sous Linux Bastien Andrès le domaine de la compilation. Il est ainsi possible, pour des projets de faible ampleur de réaliser un script effectuant les contrôles et les appels au compilateur, bien que d’autres outils soient plus adaptés à ces tâches (voir ci−dessous). Par contre, des scripts peuvent être utilisés indirectement. Ainsi la librairie GTK utilise un script afin de générer une partie des arguments de la liste de commande. En effet lors de l’inclusion de nombreux fichiers de librairie, ou lorsque de nombreux paramètres permettent d’adapter la compilation à une certaine configuration sont présent, il est malaisé d’écrire à la main la ligne de commande de compilation.

3.1.4.2. Makefile Un des outils principaux aidant à la compilation est l’utilitaire ’make’. Celui−ci permet d’automatiser et par là de simplifier grandement la tâche de compilation. Sur la base d’un fichier de configuration en relation avec le projet concerné, il effectue l’analyse des dépendances et la compilation des fichiers dans l’ordre requis. Ces fichiers de configuration, les ’makefile’, décrivent les dépendances et les actions à effectuer, tels la compilation ou l’édition de liens. Cet outil est principalement utilisé pour la compilation, mais peut être utilisé dès que des actions sont à réaliser en fonction d’un schéma de dépendances. 3.1.4.3. Préprocessing Afin de simplifier l’écriture des application, certaines librairies étendent le langage de base utilisé. C’est le fait de la librairie Qt. Celle−ci permet l’usage d’une syntaxe spécifique pour réaliser certaines parties complexe du code. Ces éléments supplémentaires de langage seront traités lors d’une étape supplémentaire de compilation, par un préprocesseur appelé le ’Meta Compiler’. Cette étape sera alors obligatoire lors de l’usage de cette librairie. 3.2. Concepts En parallèle avec les divers techniques programmatiques, divers concepts d’ordre plus général régissent la manière dont sont programmées les applications interactives. Ces concepts, introduits plus ou moins récemment sont de plus en plus utilisés car ils simplifient l’écriture d’applications de grande taille et permettent une réutilisation plus aisée de parties de code. 3.2.1. Modèle ’Model−View−Controller’ Le modèle ’Model−View−Controller’, souvent abrégé ’MVC’, propose la séparation de la modélisation des données et des manières dont celles−ci sont traitées. Ce concept n’est pas en contradiction avec le concept objet, mais complémentaire. La méthode est la suivante. Les données à traiter sont analysées et un modèle objet en est déduit. Il est ensuite procédé à la conception de la manière de laquelle les données seront présentées à l’utilisateur, et la méthode qu’il utilisera pour interagir avec elles. La séparation donne lieu à trois entités appelées respectivement le modèle ou document, la vue et le contrôleur. La séparation offre plusieurs avantages. Les données peuvent être modélisées suivant un schéma naturel et suivant une approche objet, sans se soucier des préférences de l’utilisateur quant à leur traitement. Les méthodes de traitement auront trait à la manipulation des données en tant que telles. La préférence du mode de visualisation des données est une question personnelle de chaque utilisateur. Le fait qu’elle n’entre pas en compte dans la modélisation des données est un plus. Ce modèle permet de réaliser plusieurs schémas de visualisation des données, permettant de s’adapter à l’utilisateur. De même, un mode de visualisation pourra plus ou moins facilement être adapté à des données de type similaire. La partie contrôleur est souvent très proche de la partie visualisation, du fait qu’elle interagit aussi avec l’utilisateur. Plusieurs schémas de flux d’information différents existent. Les événements utilisateur du contrôleur pouvant être appliqués vers le modèle directement, ou transiter par la vue qui elle s’occupera de renseigner le modèle d’une modification. Dans tous les cas, le modèle doit être capable d’informer la vue d’une modification.

Page 12/112 eivd Framework sous Linux Bastien Andrès 3.2.2. Dualité Application−Document Un aspect est mis en évidence lors de l’application du modèle ’MVC’. Les actions sont en partie en relation avec les données du modèle et en partie avec la gestion de plusieurs instances du modèle (tel les actions d’ouverture d’un document ou sa sauvegarde). De ce fait la définition de l’interface utilisateur d’une application n’est pas uniquement la réalisation de la vue (ou des vues) du modèle de données mais aussi d’une vue d’un modèle correspondant aux données propres d’un objet ’application’. Or il est fréquent qu’un amalgame soit fait entre la vue des données traitées par l’application et de la vue des données de l’application proprement dite. La conscience de cet aspect me semble important pour la réalisation cohérente d’une application interactive gérant un voire plusieurs types de données spécifiques. 3.2.3. Programmation événementielle Le paradigme de programmation événementielle est la méthode préférentielle de réalisation d’applications interactives. Les actions et changements d’état sont alors considérés comme des événements auquel une réaction est à définir. Le développement d’une application suivant ce paradigme requiert les étapes de recherche des événements pertinents puis de la définition des traitements à effectuer lorsque l’un d’eux se produit. Ces traitements ne dépendent pas entièrement de l’événement qui s’est produit, mais aussi de l’état de l’application à cet instant. Il est donc nécessaire de définir les états possibles de l’application et de stocker l’information y relative de façon à permettre une identification de ceux−ci. Il est possible de simplifier l’analyse des état possibles en la divisant en sous−ensembles lorsque plusieurs événements sont indépendants. Les traitements événementiels auront donc pour tâche de réaliser l’action concernée, déterminée en partie par l’état de l’application, puis de mettre à jour cet état en modifiant les variables le constituant. La mise à jour aura aussi des effets sur la partie graphique de l’application. Ainsi, un changement d’état impliquant l’inutilité d’une action devra désactiver le bouton ou l’entrée de menu permettant de l’invoquer. Cette façon de faire permet de garder une interface utilisateur cohérente. 3.2.4. Programmation par composants Le concept de programmation composant est le suivant. Les objets, pour être réutilisables aisément doivent présenter une interface homogène. Ainsi, les propriétés de ceux−ci devront pouvoir être modifiées ou obtenues par le biais de fonctions de type ’set’ et ’get’ suivant une règle de nommage stricte. De même les événements pertinents que le composant peut générer devront pouvoir être interceptés conformément au mécanisme implémenté dans le langage ou la librairie concerné. S’il s’agit d’un composant graphique, il devra être dérivé d’un composant générique permettant son inclusion dans une interface. Ce concept implique une réalisation cohérente des mécanismes de base autant que des composants particulier de façon à pouvoir utiliser uniformément les composants dans l’environnement considéré. 3.3. Gestion Graphique La gestion des ressources graphiques permettant la réalisation de l’aspect visuel de l’interface utilisateur est un domaine complexe. Il y est fait usage de nombreuses techniques pour répondre à des besoins de modélisation et de réalisation. Sa compréhension nécessite l’apprentissage de quantités de notions et définitions, puis de maintes façons de faire. La plupart des environnements ayant trait à la gestion graphique se ressemblent sous beaucoup d’aspects quant aux solutions utilisées. Il est donc décrit les notions importantes régissant ce domaine, ainsi que les techniques généralement retenues. 3.3.1. Régions Les périphériques de visualisation principaux intervenant dans un système informatique, en particulier l’écran ou l’impression, ont la particularité de constituer une surface de forme rectangulaire. L’impression est néanmois un cas particulier en ce sens qu’elle ne présente pas la propriété

Page 13/112 eivd Framework sous Linux Bastien Andrès d’interactivité. La plupart des techniques présentées dans ce document ont plutôt trait à la gestion de l’écran. Une surface est constituée de points. Ceux−ci constituent l’élément de base de l’affichage. Ensuite vient une notion importante, omniprésente : la surface rectangulaire. En effet, il s’agit du sous ensemble de l’écran le plus facile à décrire et surtout à traiter. Une telle surface est souvent appelée une ’région’. Ainsi, la plupart du temps, une fenêtre est de forme rectangulaire, de même qu’un bouton ou une zone de texte. Une icône aura une forme quelconque mais sa description passera par la définition de la couleur ou de l’absence de couleur d’une zone rectangulaire. Il en va de même lorsqu’on s’intéresse aux mécanismes de rafraîchissement du contenu de l’écran. Le gestionnaire définit des régions, le plus souvent rectangulaires, qui devront être redessinées. 3.3.2. Contexte graphique Le contexte graphique peut être considéré comme une forme particulière de région. Il s’agit de l’abstraction d’une zone de l’écran additionnée des propriétés courantes assignées à l’affichage comme diverses informations concernant l’usage des couleurs ou la police utilisée. Le contexte graphique possède aussi des méthodes permettant d’éditer graphiquement la région. Un contexte graphique peut ainsi être implémenté par un objet. La plupart des environnements graphiques ont recours à une forme de contexte graphique. Lorsque des composants sont utilisés, l’utilisation de contexte graphique n’est pas forcément visible. Par contre, il y est fréquemment fait appel lorsque il s’agit de ’dessiner’ à l’écran; c’est−à−dire réaliser des tracés non standard, ou écrire du texte en un endroit bien particulier. 3.3.3. Programmation graphique événementielle Le concept de programmation événementielle a été énoncé ci−dessus. Celle−ci est fortement utilisée en programmation graphique. Le système graphique sous−jacent génère des événements lorsque l’utilisateur utilise le clavier ou la souris. Ceux−ci sont interceptés par les diverses librairies et transformés en événements plus significatifs à destination de l’application. Ainsi l’appui d’un bouton lorsque le pointeur se trouve à l’intérieur du bouton ’OK’ sera transformé de : ’bouton de la souris pressé à la position x,y’ en : ’bouton ’OK’ sélectionné’. De cette manière, le traitement est partagé. La tâche de la librairie est de trouver quel composant est concerné par l’événement tandis que celle de l’application est de réagir en fonction. Afin de traiter les événements, la librairie possède une boucle principale. Dans un premier temps, celle−ci attend qu’un événement survienne. Puis elle l’analyse afin de déterminer son type et à quel composant elle se rapporte. Ensuite, la méthode de l’application assignée au traitement de l’événement est appelée. En dernier lieu, si des modifications graphiques ont lieu, elle procède à la mise à jour de l’affichage. Enfin, si des conditions de fin d’exécution ne sont pas atteintes, elle boucle sur l’attente d’un nouvel événement. 3.3.4. ’Window Manager’ La ressource écran étant la plupart du temps unique, il est nécessaire de gérer son partage entre les diverses applications. Cela est généralement réalisé par la technique du fenêtrage. Des régions sont assignées aux applications et celles−ci sont responsables de leur contenu. Il est nécessaire qu’une application spéciale règle ce partage selon les désirs de l’utilisateur et les paramètres des applications. C’est la tâche du ’Window Manager’. Celui−ci met à disposition de l’utilisateur le moyen de contrôler, dans les limites imposées par les applications, la position, la taille ainsi que la superposition des fenêtres. De nombreux styles de gestion existent. Certains aspects se retrouvent pourtant dans la majorité des cas. Généralement, un fenêtre est entourée d’un cadre et d’une barre de titre. Ceux−ci sont sous la responsabilité du ’Window Manager’. Ces emplacements sont souvent utilisés pour fournir des fonctions comme le redimenssionnement, le déplacement ou la fermeture de la fenêtre. Ainsi, une fenêtre est composées de deux régions imbriquées. L’une, centrale, est dévolue à l’application pour l’affichage de son contenu; l’autre au ’Window Manager’ à des fins de gestion.

Page 14/112 eivd Framework sous Linux Bastien Andrès 3.3.5. Modèles d’application multi−fenêtres Certaines applications nécessitent plusieurs fenêtres. Il peut s’agir de plusieurs documents ouverts, ou de fenêtres utilitaires comme des dialogues ou des palettes d’outils. Plusieurs méthodes sont utilisées pour permettre à une application d’afficher plusieurs fenêtres. L’une d’entre elle, fréquemment utilisée dans l’environnement consiste à présenter une seule fenêtre du Window Manager et de recréer à l’intérieur un espace de travail pouvant contenir plusieurs fenêtres correspondant à plusieurs documents. Éventuellement, une de ces fenêtre utilisera tout l’espace, celle−ci sera alors décrite comme dans l’état ’maximisé’. Cet état est aussi possible pour une fenêtre principale dans l’espace du Window Manager. Une autre solution consiste à générer plusieurs fenêtres dans cet espace. Les divers documents se partagent alors l’application, mais chaque fenêtre est gérée par le Window Manager. Cette solution est fréquemment rencontrée dans le monde UNIX. Pour ce qui est des fenêtres de dialogue simples, une fenêtre du Window Manager est la plupart du temps utilisé car une méthode simple de réalisation de ce genre de fenêtre est généralement fournie. La première méthode a l’avantage de regrouper les fenêtres relatives à une application dans un espace délimité, mais a l’inconvénient d’encombrer un peu plus l’écran du fait de l’imbrication de deux niveau de fenêtres. La deuxième méthode, par contre, permet de mieux utiliser la surface de l’écran, par contre, certains éléments comme la barre de menu se retrouvent dans chaque fenêtre. 3.3.6. ’Xwindow’ Xwindow est le gestionnaire graphique principal sous UNIX. C’est lui qui se charge de la liaison avec le matériel. Il acquiert les données en provenance des périphériques d’entrées tels que le clavier et la souris et génère les données à destination de la carte graphique. Il dispose d’une interface de programmation permettant l’accès à ses fonctionnalités par des programmes écrits en C (). Les actions de l’utilisateur sont transformées en événements qu’il est possible d’intercepter. De même des fonctions permettent de dessiner à l’écran. Xwindow est une application distribuée; c’est−à−dire que les applications peuvent s’exécuter sur d’autres stations que celles sur laquelle elle sont affichées. Ce mécanisme utilise un protocole spécifique basé sur IP. Xwindow gère les régions graphiques, mais ne connaît pas la notion de fenêtre qu’il lui est donc impossible de gérer. Cette tâche est réalisée par Le ’Window Manager’ précédemment présenté. 3.3.7. Répartition des tâches d’affichage (X−WM−Lib−App) La gestion graphique est donc réalisée par plusieurs acteurs. Xwindow a pour tâche le dialogue avec le matériel. Le Window Manager a celle de découper l’espace en fenêtres et de gérer celles−ci. Les librairies graphiques fournissent divers composants utilisés pour réaliser le contenu de la fenêtre et gèrent ceux−ci. Les applications utilisent les librairies et leurs composants pour créer le contenu des fenêtres. 3.4. Technique des librairies graphiques 3.4.1. Concepts

3.4.1.1. Description de la composition graphique La méthode de définition de la composition d’une interface graphique en utilisant une librairie s’effectue de façon sensiblement similaire quel que soit l’environnement utilisé. Une hiérarchie d’imbrication de composants est réalisée. Certains composants sont de type ’conteneur’ et ont la faculté de pouvoir intégrer des composants. Certains conteneurs sont invisibles, leur seul but consistant à contenir un ou plusieurs composants. La réalisation d’une interface se fait en créant des composants, en leur assignant des valeurs à leurs propriétés et en les ajoutants à des conteneurs. Le conteneur fenêtre sera le composant cadre dans lequel se placeront les autres composants.

Page 15/112 eivd Framework sous Linux Bastien Andrès 3.4.1.2. ’Layout Management’ La hiérarchie d’imbrication permettant de définir les contenances, il est de plus nécessaire de définir comment les composants vont s’organiser géométriquement les uns par rapport aux autres. Plusieurs méthodes de description existent. La première, simple à appréhender mais peu souple est de définir les positions x et y et les tailles des composants par rapport à la fenêtre, ou par rapport à leur conteneur. L’inconvénient de cette méthode est que l’application doit recalculer les tailles lors d’un redimensionnement. Une seconde méthode consiste à utiliser des ’Layout Managers’. Ceux−ci permettent de définir les positions relatives des composants dans les conteneurs et s’occupent de calculer les positions en fonction. Les librairies mettent alors dispositions des conteneurs de différents types, permettant le placement des composants selon divers modes. Par exemple, un conteneur positionnera les composants contenus verticalement, les uns au dessus des autres, tandis qu’un autre conteneur les répartira suivant une table (invisible) dont il sera possible de définir le nombre de colonnes et de lignes. A ce mécanisme s’ajoute celui permettant d’évaluer la place que prendra de préférence un composant. Celui−ci se base sur les propriétés des divers composants. Ainsi, un bouton pourra avoir comme propriété celle d’être de taille fixe, (x,y). Il pourrait aussi avoir la propriété de remplir tout l’espace disponible et d’avoir un bord vide de n pixels de large. Le calcule alors les tailles préférentielles des composants et les dessine selon le schéma obtenu. Ainsi, l’aspect est décrit relativement à la place disponible plutôt que de manière statique. Cette méthode permet de garder un aspect cohérent dans une plus grande gamme de dimensions de la fenêtre. 3.4.1.3. ’Top Level’ Certains composants de type conteneur sont particuliers en ce sens qu’ils correspondent au plus haut niveau hiérarchique. Ceux−ci se trouveront directement placé dans l’espace ’client’ d’une fenêtre. Certaines librairies proposent plusieurs types de ces conteneurs, suivant qu’il s’agit de réaliser une fenêtre standard, ou par exemple un dialogue d’avertissement. Ces composants ne peuvent pas être insérés dans un autre conteneur. 3.4.1.4. Utilisation des contextes graphiques Les librairies proposent un grand nombre de composants de types très divers permettant de réaliser une majeure partie des fonctionnalités standard d’une application. Néanmoins, il arrive que ceux−ci ne soient pas suffisant et qu’il faille dessiner à l’écran. Les librairies mettent à disposition des composants fournissant un contexte graphique. Une région est alors définie, dans laquelle il est possible de dessiner au moyen de fonctions prévues a cet effet. Ces composants permettent de récupérer les événements souris avec la position du pointeur. Il sera alors possible à l’application de mettre en relation la position du pointeur et les objets dessinés, tandis que lorsque des composants traditionnels sont utilisés, les événements sont directement dirigés par la librairie au composant concerné. 3.4.2. Déroulement standard d’une application

3.4.2.1. Introduction Une application ayant recours aux services d’une librairie graphique suit un déroulement spécifique. Il est nécessaire de bien le comprendre pour réaliser des applications efficaces. Celui−ci est donc décrit ci− après dans ses étapes les plus importantes. 3.4.2.2. Création des objets La grande majorité des librairies utilisant un environnement orienté objet, la première étape consiste en la création des objets. Le premier objet à construire dans la plupart des cas est un objet de gestion propre à la librairie, qui s’occupera de la gestion des autres objets. Java ne nécessite pas la création de cet objet, celui−ci est automatiquement créer si des composants graphiques sont utilisés. Les objets ’composants graphiques’ peuvent être créés au fur et à mesure de leur nécessité. Page 16/112 eivd Framework sous Linux Bastien Andrès 3.4.2.3. Assignation des propriétés Une fois un composant créé, ses propriétés sont définies. Cela se fait soit par assignation de données membres de l’objet ou par l’appel de ses méthodes avec les paramètres en argument. Durant cette étape, on définit par exemple le texte d’un bouton, ses dimensions préférentielles ou le texte par défaut d’une zone d’édition. De même, les méthodes de traitement des événements sont connectées à ces derniers de manière à pouvoir être appelées.

3.4.2.4. Composition Les composants étant définis, la composition doit être réalisé. Les composants doivent être insérés dans les conteneurs. Cette action est généralement réalisée par le biais de méthodes du conteneur portant fréquemment un nom contenant ’add’. Ces méthodes prennent en argument le composant à insérer et éventuellement des informations définissant le positionnement relatif du composant dans le conteneur et par rapport aux autres composants insérés. 3.4.2.5. Réalisation Un fois la composition effectuée, intervient une étape très importante. La librairie possède alors tous les éléments lui permettant de calculer l’aspect de la fenêtre. Il est alors fait appel à une méthode demandant la ’réalisation’ de la fenêtre. La librairie calcule alors les dimensions des composants et les ’réalise’; c’est−à−dire que leur aspect graphique est généré. Il est ensuite fait appel à une méthode permettant de rendre visible la fenêtre ainsi réalisée. 3.4.2.6. Lancement du processus événementiel La fenêtre étant affichée, la boucle de traitement des événements est lancée. Cela se fait par le biais d’une méthode de l’objet de gestion. Cette méthode ne retourne habituellement pas. Un événement générera la fin de l’application. Dans le cas de Java, si des composants graphiques sont présents, des threads sont automatiquement créés pour la gestion de l’affichage et des événements. De ce fait, après avoir rendu la fenêtre visible, le thread ayant créé l’interface peut s’arrêter, l’exécution continuera du fait de la présence des autres threads.

3.4.2.7. Schéma de traitement des événements Lorsqu’un événement survient, la librairie analyse celui−ci afin de sélectionner le composant auquel il se rapporte, puis appelle la méthode de traitement liée. L’application peut alors effectuer les traitements spécifiques. Dans le cas ou elle aurait dessiné dans un contexte graphique, elle appellera une méthode de la librairie, lui indiquant qu’une mise à jour de l’affichage est nécessaire, lui précisant éventuellement la zone modifiée. Le contrôle retourne alors à la librairie qui effectuera les mises à jour nécessaires. Certains événements comme un redimmensionnement de la fenêtre ne seront peut−être pas traités par l’application mais par la librairie. Dans ce cas celle−ci effectuera la totalité du travail. 3.4.2.8. Mise à jour de l’état de l’application Il est possible qu’en réaction à un événement, le code de l’application modifie la composition de l’interface graphique. Dans ce cas, une fois les modifications effectuées, il sera fait appel à une méthode de la librairie demandant le recalcul de l’aspect. Une fois le contrôle retourné à celle−ci, elle procédera à la mise à jour de la zone graphique concernée par la modification. 3.4.2.9. Terminaison de l’application La terminaison de l’application peut être initiée de plusieurs manières. La source peut être le Window Manager, émettant une demande de terminaison en réponse au clic dans la case de fermeture de la fenêtre. L’application elle−même peut être initiatrice, en réponse, par exemple, à l’événement de sélection de l’entrée ’Quitter’ d’un menu. Un événement de fin est alors généré et peut être traité par

Page 17/112 eivd Framework sous Linux Bastien Andrès l’application, lui permettant, par exemple de demander à l’utilisateur s’il désire sauver des données. Il est donc à ce moment possible d’annuler la terminaison de l’application. Si cette annulation n’a pas lieu, il est encore fourni à l’application l’occasion de rendre proprement les ressources utilisées, puis la librairie effectuera enfin ses propres tâches de terminaison et mettra un terme à l’exécution. 3.5. Librairies graphiques ’standards’ sous Linux 3.5.1. Introduction Cette partie introduit trois librairies de programmation d’interfaces graphiques ainsi que leur environnement. Il s’agit de Motif, de Gtk et de Qt. Ces trois librairies sont sans doute les plus répandues sous Linux. La première car il s’agit d’un standard sous UNIX, la seconde grâce à l’environnement Gnome et la troisième par l’environnement KDE. 3.5.2. Motif / Lesstif Motif est une librairie de programmation d’interfaces graphiques écrite en C. Motif est un produit de l’OSF (Open Software Foundation). Deux versions dont les exécutables sont incompatibles sont actuellement utilisées : la version 1.2 qui est basée sur X11R5 et la version 2.0. Un des développements majeurs utilisant Motif est l’environnement graphique CDE, présent sur la plupart des UNIX commerciaux. En 1996, l’OSF et l’XConsortium (responable de Xwindow) sont devenu l’OpenGroup, regroupant de ce fait le fournisseur du système graphique et celui des composants l’utilisant. Ces logiciels sont commerciaux et nécessitent donc l’acquisition de licences. La communauté Open Source ne pouvant se satisfaire de ces conditions mais ayant néanmoins la nécessité d’utiliser de tels produits a généré des copies de ceux−ci. Le système Xwindow a été réécrit en grande partie, et porte le nom de Xfree86. De même Lesstif est une version ’free software’ de Motif et est compatible au niveau des sources avec sa version 1.2. Motif est basé sur la librairie . L’architecture de cette librairie est reprise et Motif consiste en une couche supplémentaire. De ce fait Motif est essentiellement une librairies d’objets graphiques et de fonctions définissant leur comportement; la partie en relation avec l’application étant dévolue à la librairie Xt (X Toolkit). Motif apporte une légère orientation objet, bien qu’écrite en C. Plusieurs librairies existent afin de faciliter l’usage de Motif avec le langage C++. Les concepts utilisés dans Motif sont assez anciens de même que ceux de Xt. De ce fait, ces librairies sont généralement moins performantes que d’autres, plus modernes, basées directement sur Xlib et profitant de l’avancées des techniques orientées objets. Elle reste néanmoins un standard, bien que nettement moins utilisée par la communauté GNU/Linux qui lui préfère Gtk ou Qt. 3.5.3. Gtk+ / Gtk−− / libsigc++ 3.5.3.1. Gtk Gtk est un widget−set issu du projet ’GIMP’ (GNU Image Manipulation Program). Son histoire y est donc liée. Les premières versions de GIMP, dont la première sorite officielle (0.54) ont été réalisées en utilisant Motif. Exaspéré par la programmation sous Motif, l’auteur de GIMP a alors décidé d’écrire ses propres librairies. Elles reçurent le nom de gtk (Gimp Tool Kit) et (Gimp Drawing Kit). Ces librairies étaient à l’origine destinées uniquement à l’usage de l’écriture de Gimp et sans aucune vocation généralistes. Les versions suivantes de Gimp (dès la 0.60) ont donc été programmées en utilisant ces librairies. Lors de la sortie de la version 0.99 de Gimp, en février 1997, apparut une nouvelle version de gtk/gdk qui reçut le nom de GTK+. Celle−ci est toujours en C mais incorpore une orientation objet. En juin 1997 les auteurs sortirent une nouvelle version de Gimp et de GTK+. Ils arrivèrent alors à la fin de leurs études. Ils abandonnèrent le projet mais celui−ci continua du fait de nombreuses personnes impliquées. Le développement se poursuivit suivant un mode de travail différent et classique du

Page 18/112 eivd Framework sous Linux Bastien Andrès mouvement Open−Source, c’est−à−dire par contribution multiples et communication par liste de mails.

3.5.3.2. Gtk+ Gtk+ est donc un ensemble de librairies graphiques composé d’une librairie d ’affichage et d’une librairie de composant, accompagné de divers éléments permettant la programmation graphique. A l’origine écrit pour GIMP, elle a été généralisée, la rendant utilisable pour la programmation d’un vaste spectre d’applications. Il s’agit donc d’un ’GUI Toolkit’ Open Source. Il est multi−plate−forme car un portage sous Windows est en cours. Il est sous licence LGPL ( General Public Licence). Il consiste en un ensemble de librairies destinées à la création d’interfaces graphiques. Il est basé directement sur X11 (version UNIX). L’architecture de Gtk+ est orientée objet et basée sur le langage C. Il existe des liaisons avec de nombreux langages de programmation tels que : C++, ObjectiveC, Guile/Scheme, , Python, Ada95, Free Pascal, Eiffel, etc... Actuellement, Gtk+ en est à la version 1.2. Il est utilisé dans de nombreux projets Open Source dont les plus importants sont sans doute Gnome et Gimp. Bien que programmé en C, Gtk+ fournit la plupart des mécanismes du concept ’orienté objet’. La couche d’abstraction ’Gtk+ Object System’ permet l’héritage par imbrication de structure de données et permet le polymorphisme. Les références peuvent être comptées. Diverses fonctionnalités adaptées à son orientation graphiques sont implémentées tels qu’un système de notification d’événements par ’signaux’. 3.5.3.3. Glib Glib est une librairie procurant des versions améliorées de fonctions de la libc standard. Elle contient des fonctions d’allocation de la mémoire (g_malloc(), g_free(), ...) plus sûres que celles de la libc et un mécanisme de supervision de la mémoire est disponible. Glib contient aussi une grande collection de types et de fonctions utilitaires (guint16, MIN(), etc...) ainsi que l’implémentation de diverses structures de données telles les listes chaînées, tables de hachage, la gestion des ’strings, des fonctions ’Timers’ et un scanner lexical. Elle procure aussi une ’boucle principale d’événement’ générique et extensible (traite des événements graphiques ou non). Cette librairie est utilisée par gtk et gdk.

3.5.3.4. Gdk Gdk est la librairie d’affichage de Gtk+. Elle est une couche d’abstraction de la librairie Xlib, et est construite sur cette base. Les fonctions fournies sont plus simples et intuitives que celles équivalentes de la Xlib. Elle utilise Glib et est donc plus sûre et portable sur différentes plate−formes. 3.5.3.5. Gtk−− Gtk−− est une interface de liaison de Gtk+ au language C++. Elle procure de nombreuses classes encapsulant les fonctionnalités de Gtk+. Elle permet de créer des widgets par héritage. Elle permet l’utilisation typée du mécanisme de signalisation par Callback de Gtk+. Tous les widgets sont regroupés dans une hiérarchie d’objets unique. Un support du concept Modèle−Vue−Contrôleur est fourni. Gtk−− représente un outil de choix pour programmer des interfaces graphiques en utilisant les possibilités de Gtk+ et le langage C++ pour son orientation objet. 3.5.3.6. libsigc++ libsigc++ consiste en la librairie de Callback de gtk−− extraite de l’environnement. Il devient alors possible d’utiliser ce mécanisme de communication sans la nécessité de lier son application avec la librairie entière. Cela est utile, par exemple, pour la programmation d’application sans interface graphique tout en utilisant ce puissant mécanisme.

Page 19/112 eivd Framework sous Linux Bastien Andrès 3.5.3.7. Gnome Gnome (GNU Network Object Model Environnement) est un projet qui a vu le jour dans le but de créer un environnement graphique entièrement libre pour les systèmes UNIX libres (Linux, freeBSD). Il s’agit d’un ensemble de programmes qui constitue un ’desktop’ de même qu’un environnement de développement. Gnome utilise GTK+. Gnome fournit des librairies supplémentaires ajoutant des widgets de même que de nombreux mécanismes permettant la création d’un bureau ainsi que des applications orientées objet et composant. De nombreux projets sont associés au projet Gnome, réalisant un environnement de programmation très performant (environnement graphique, applications distribuées, internationalisation, système virtuel de gestion de fichiers, ...). 3.5.4. QT Qt est un Framework de création d’application avec interface graphique multi−plate−formes en langage C++. Il utilise le concept orienté objet. Il a été introduit en 1996 et est utilisé comme base pour de nombreuses application, dont l’environnement graphique KDE. Les plate−formes supportées sont la plupart des UNIX par le biais de X11 et Windows. Qt est un produit de TrollTech (http://www.trolltech.com). Qt est disponible en deux versions dont une, pour UNIX/X11, est libre. La version Windows ainsi que les versions professionnelles sont commerciales. Qt propose son modèle objet en complément de celui du C++ afin d’améliorer sa flexibilité, qualité importante lors de la création d’interfaces graphiques. Les principaux ajouts sont : un mécanisme puissant de communication inter−objets (signal/slot), des filtres d’événements, un mécanisme interne de timers, des objets pointeurs mis à jour automatiquement, la possibilité d’obtenir les propriétés des objets et les liens hiérarchiques, ainsi qu’une prédisposition à l’internationalisation. La plupart de ces fonctionnalités sont fournies par l’usage des techniques standard du C++, et basé sur l’héritage de la classe Qobject. Néanmoins, certaines d’entre elles telles que le mécanisme de communication ou le système de propriétés dynamique requièrent l’usage d’un précompilateur spécifique, le ’Meta Object Compiler’ (moc). Le ’Meta Object System’ peut être perçu comme une extension au C++, permettant la programmation d’interfaces graphiques ’orientées composant’. Le Meta Object Compiler traite les fichiers sources C++. Il traite les déclarations de Macros ’Q_OBJECT’ et produit un fichier C++ supplémentaire qui contient le code ’meta object’ pour la classe en question. Ce fichier doit alors être compilé et lié ou être inclu pour permettre la compilation conventionnelle. La précompilation permet l’inclusion des mécanismes performant de Qt, mais ne supporte pas toutes les techniques avancées du C++. De ce fait l’écriture des fichiers devra suivre quelques règles supplémentaires afin de permettre le traitement par le précompilateur. 3.5.5. Comparaison Les trois librairies ont leur atout. Motif/Lesstif est un standard de longue date, tandis que Gtk et Qt sont des produits récents et performants. Ces deux dernières sont de performance équivalentes bien qu’utilisant des approches parfois différentes. Chacune possède ses amateurs et détracteurs de même que les ’desktops’ issus de celles−ci. Ainsi, les puristes ont critiqué KDE dans son utilisation de Qt, alors produit commercial, et ont penché du côté de Gnome qui est un projet GNU. Qt est maintenant sous licence proche de la GPL, tandis que le principal acteur de Gnome a créé une société, Helixcode, dans le but de développer Gnome, mais aussi de vendre le support pour celui−ci ainsi que sa distribution sous forme de CD. On peut y voir une sorte de guerre de religion entre les utilisateurs de l’un ou l’autre système, tandis que certains développeurs préfèrent parler de compétition stimulante. 3.5.6. Conclusion Il n’y a donc pas à proprement parler de solution nettement supérieure. Chacune a ses avantages et inconvénients. Dans le cadre d’un développement pédagogique certains aspects spécifiques sont à étudier. L’utilisation de Motif est sans doute un cas d’école, mais il y a le risque de passer à côté de techniques

Page 20/112 eivd Framework sous Linux Bastien Andrès plus récentes et qui préfigurent sans doute l’avenir de ce genre de programmation. L’utilisation de Qt semble d’un intérêt certain du fait de son utilisation native du C++. Néanmoins, son usage d’un précompilateur peut être source de complication supplémentaires et peut sans doute cacher une partie des concepts fondamentaux. Gtk, par le biais de gtk−−, paraît une bonne solution, permettant l’utilisation du C++ et ne nécessitant pas d’outils particuliers supplémentaires. 3.6. Fonctionnalités graphiques de Java Le JDK fournit des classes permettant de réaliser des interfaces graphiques. Deux méthodes peuvent être utilisées. La première en utilisant des composants dépendants de la plate−forme, la seconde presque entièrement composées de composants indépendants. 3.6.1. Interface AWT L’AWT, pour ’’, représente un ensemble de classes permettant de réaliser des interfaces graphiques. Il s’agit en fait d’abstractions des composants fournis par le système d’exploitation ou une librairie graphique de base. De ce fait, l’apparence de l’interface dépendra de la plate−forme sur laquelle le programme est exécuté, elle sera semblable à celle des programmes natifs du système. 3.6.2. Interface Swing Les classes Swing représentent la deuxième solution pour la réalisation graphique avec Java. Des composants dépendant du système ne sont gardés que les composants du plus haut niveau hiérarchique, c’est−à−dire ceux de type fenêtre. Tous les autres composants sont créés par Swing et présentent une apparence spécifique, éventuellement modifiable à l’aide de ’thèmes’. 3.6.3. Comparaison et interactions de Swing et de l’AWT Les classes Swing et l’AWT représentent deux méthodes de réalisation graphique. Néanmoins, celles− ci ne sont pas totalement exclusives. L’AWT, plus ancien, se suffit à lui même, tandis que les classes Swing font appel à certaines fonctionnalités de celui−ci. En effet, outre les composants proprement dit, il propose de nombreuses classes utilitaires proposant par exemple le placement des composants. Ces classes sont réutilisées par Swing pour effectuer les mêmes tâches. Il est généralement préférable d’utiliser les classes Swing car celles−ci permettent d’obtenir un résultat d’apparence similaire sur tous les systèmes et la gestion, indépendante du système sous−jacent, est généralement plus performante. 3.6.4. Composants (Widgets) Les composants graphiques (’Widgets’) ont été définis par l’AWT. Ceux−ci sont redéfinis par les classes Swing. Ils sont organisés de manière hiérarchique. Cet ensemble est très complet et suffit généralement pour la réalisation d’interfaces complexes. Ceux−ci sont de plusieurs types. Des composants de type fenêtre ou dialogue constituent le lien avec les fenêtre du Window Manager. De nombreux types de conteneurs constituent une solution très complète pour le placement des composants. La palette des contrôles standards tels que bouton, zone de texte ou liste sont présents. Plusieurs composants utilitaires sont fournis permettant par exemple de réaliser des bulles d’aide ou de constituer une interface avec des systèmes d’aide aux malvoyants. 3.6.5. ’Layout Management’ Le positionnement des composants suit la méthode de ’Layout Management’ décrite ci−dessus. Plusieurs types de ’LayoutManager’ sont donc fournis. Les principaux d’entre eux sont les suivants. Le FlowLayout, permet le positionnement des composants à la suite les uns des autres, en fonction de la taille disponible pour le conteneur. Le BorderLayout permet de disposer les composants suivant les

Page 21/112 eivd Framework sous Linux Bastien Andrès quatre bords du conteneur, ainsi qu’au centre. Le BoxLayout les place en colonne ou en ligne. Le GridLayout effectue un placement sous forme de table tandis que le GridBagLayout permet de concevoir un schéma de grille complexe sur laquelle les composant s’appuieront. Quelques autres Layout Managers permettent de réaliser des géomètries spécifiques. La plupart de ces Managers possèdent quelques propriétés qu’il est possible de fixer afin de personnaliser la mise en page. De même, quelques classes utilitaires permettent la construction de bords d’aspects variés ou de modifier l’apparence des composants. 3.6.6. Absence de ’Layout Mangement’ Il est possible de se passer de Layout Manager. Ceci doit être défini explicitement en précisant que le conteneur ne devra pas posséder de Layout Manager, un de ceux−ci étant assigné par défaut. Il sera alors nécessaire de préciser la taille et la position de chaque composant à l’aide de la méthode adéquate. Dans ce cas, la cohérence de l’aspect n’est pas garanti et il sera nécessaire de recalculer les tailles à chaque redimmensionnement de la fenêtre si tous les composants doivent rester visibles. 3.6.7. Window L’élément de plus haut niveau hiérarchique est la fenêtre. La classe Swing l’implémentant est ’JFrame’. Celle−ci fournit la possibilité de placer divers éléments sur sa surface. Celle−ci est découpée en plusieurs parties, certaines facultatives. Ainsi, une zone peut être utilisée pour placer une barre de menu. Si celle−ci est absente, la zone n’existe pas. La surface de la fenêtre possède plusieurs couches superposées appelées ’panes’. Chacune d’entre elle peut contenir des composants. Un de ceux−ci placé sur une couche supérieure cache un autre placé sur une couche inférieure. Généralement, seule une de ces couches est utilisée. Les autres sont utilisées par exemple pour placer un menu contextuel, ou une bulle d’aide. Il n’y est donc pas fait explicitement référence dans le code de l’application. Les deux zones principalement utilisées sont la barre de menu et la ’contentPane’. Cette dernière est constituée par la surface de la fenêtre soustraite de la barre de menu éventuelle. C’est cette zone qui comprendra les composants traditionnels tels que les boutons par exemple. La ’contentPane’ n’accepte qu’un composant. Il s’agira donc généralement d’un conteneur permettant le placement de plusieurs composants. L’objet fenêtre intercepte des événements. Ceux−ci sont principalement en provenance du Window Manager. Il s’agit des redimmensionnement de la fenêtre, ainsi que la requête de fermeture de la fenêtre. Pour cette dernière, il est possible de définir le comportement par défaut. 3.6.8. Evénements Le langage Java implémente plusieurs schémas de communication d’événements. Le mécanisme ’EventListener’, utilisé par Swing est décrit ci−dessous. 3.6.8.1. Events Une classe de base représente un événement générique. Celui−ci permet de transmettre la source de la classe source de l’événement. Des classes dérivées implémentent divers données relatives à des événements concrets. Par exemple, la classe ’MouseEvent’ représente un événement en provenance de la souris. 3.6.8.2. Listeners Afin de pouvoir réagir aux événements générés, une classe doit implémenter l’interface adéquate. Tout type d’événement est accompagné d’une interface ’Listener’ qui définit les méthodes de traite− événement. Ainsi, à l’événement ’MouseEvent’ correspond l’interface ’MouseListener’ qui définit les méthodes ’mouseClicked’, ’mousePressed’ ainsi que quelques autres permettant de réagir aux divers événements générés par la souris. Afin que le composant qui génère un événement sache qu’une classe désire y réagir, il est nécessaire de souscrire à cet événement. Il sera donc fait appel à la méthode

Page 22/112 eivd Framework sous Linux Bastien Andrès ’AddListener’ de la classe génératrice, en précisant en argument quelle classe désire recevoir cet événement. La mise en oeuvre des événements sera traitée plus en détail lors de la description du Framework réalisé en Java.

3.6.8.3. Action Il est fréquent dans le cadre des interfaces graphiques que plusieurs méthodes soient mises à disposition de l’utilisateur pour réaliser une même action. Par exemple, un bouton présent dans une ’ToolBar’ invoquera le même traitement qu’une entrée de menu. Java met a disposition une classe permettant de réaliser proprement la liaison entre les composants et l’action. Cette classe, appelée justement ’Action’ permet de récuperer les sources multiples d’événements et de générer un autre événement. Le traitement se fera alors par interception de ce dernier. Cette façon de faire permet une meilleure séparation entre les composants et les données. Son utilisation permet de rajouter aisément une source d’événement aboutissant au même traitement, de même qu’une modification de ce denier est aisée, étant donné qu’il n’est alors pas nécessaire de se préoccuper des méthodes provoquant ce traitement. Cela va dans le sens du concept ’MVC’. 3.6.8.4. Menus Les menus sont réalisés principalement par l’utilisation de trois classes : JMenuBar, JMenu, et JMenuItem. La classe JMenuBar représente la barre de menu. Celle−ci est ajoutée à un composant fenêtre. Ce dernier réserve alors un espace pour la placer. Cela réduit d’autant la zone des autres composants. La classe JMenu représente un menu déroulant, tandis que la classe JMenuItem représente une entrée d’un menu. Cette classe dérive de la classe JMenuItem. Ainsi, un menu déroulant est un cas particulier d’un menu. Ce fait permet la création aisée de sous−menus. Un menu déroulant possède un titre qui apparaîtra dans la barre de menu ou comme entrées de menu, dans le cas de la création de sous− menu. Les trois classes générent des événements. Le plus important d’entre eux est celui de sélection d’une entrée de menu. C’est celui qui permet d’agir lorsque l’utilisateur aura choisi une entrée. Les autres événements permettent de réaliser des actions complexes lorsqu’un menu est déroulé, comme par exemple une aide contextuelle. Quelques classes supplémentaires permettent de réaliser des menus de type ’pop−up’ tels que les menus contextuels. 3.6.8.5. Contexte Graphique Lorsqu’il est nécessaire de dessiner à l’écran, la classe ’Graphics’ est utilisée. Celle−ci représente une région. Une instance de Graphics est obtenue au moyen de la méthode ’getGraphics’ d’un panel (conteneur). Les méthodes fournies par la classe Graphics permettent de dessiner dans la région et la méthode ’repaint’ permet d’indiquer au thread Swing que a région doit être redessinée à l’écran.

3.6.8.6. Threads & Swing Le mécanisme d’événements implique la distribution de ceux−ci; c’est−à dire l’appel de toutes les méthodes des classes qui ont souscrit à l’interception de l’événement concerné. Cette tâche est réalisé sous le contrôle d’un thread. Celui−ci s’occupe aussi de la mise à jour de l’affichage. De ce fait, il est important que les traitements effectuées en réponse à un événement soit de courte durée. Sinon, l’application ne pourra pas répondre aux requêtes suivantes de l’utilisateur dans des délais raisonnables et ce dernier percevra un manque de responsivité. Lorsque des traitements longs doivent être initiés par un événement, il convient de créer un thread qui s’occupera de l’exécution de cette tâche. De ce fait, le thread graphique n’aura que l’appel à réaliser et pourra boucler rapidement sur l’attente de nouveaux événements.

Page 23/112 eivd Framework sous Linux Bastien Andrès 4. Concepts développés L’étude des techniques présentes dans les librairies graphiques met en évidence la complexité de certains aspects de ce genre de programmation. Dans le but de proposer un Framework d’accès aisé, il est nécessaire de développer des méthodes permettant de cacher cette complexité. Plusieurs réflexions ont abouti à des solutions permettant d’atteindre ce but. Celles−ci sont introduites ci−dessous. Elles seront traitées en détail ultérieurement lors de la description de la réalisation en Java, ces solutions ayant vu le jour lors de l’élaboration de ce dernier. 4.1. Méthode de description de l’interface graphique Dans de nombreux cas, l’aspect général d’une interface graphique est assez statique. La fenêtre ne subit pas de modification de la structure de sa composition. Seules certaines propriétés de certains composants changent. Ainsi, les boutons ne seront que rarement déplacés; ils seront désactivés si une fonctionnalité n’est pas utilisable dans un certain contexte. Il doit alors être possible de définir la composition de l’interface de manière simple. La méthode fournie par les librairies graphiques, ayant recours à une suite d’appel de méthodes, ne permet pas une description naturelle. Une méthode a donc été développée dans le cadre du Framework en Java permettant la description par une série d’assignations de variables. 4.2. Modèle de programmation par redirection des événements Comme précédemment décrit, les actions à effectuer en réponse à un événement ne dépendent pas uniquement de celui−ci, mais aussi de l’état − ou contexte − dans lequel se trouve l’application. De ce fait, une méthode fréquemment utilisée est de placer des structures conditionnelles (if, case) dans le traite−événement afin d’adapter celui−ci au contexte. Cela rend l’écriture de ces fonctions assez lourde et peu performante. Une technique alternative a été développée. Il s’agit de rediriger le traitement des événements vers différentes fonctions en fonction de l’état dans lequel se trouve l’application. On s’affranchit alors de la plupart des structures conditionnelles, celles−ci sont remplacées par de simples assignations.

Page 24/112 eivd Framework sous Linux Bastien Andrès 5. Réalisations 5.1. Ajout de la fonctionnalité de multi−fenêtrage Comme décrit précédemment, il existe deux méthodes de multi−fenêtrage employées. L’une par imbrication, l’autre par multiplication de fenêtres du Window Manager. La mise en oeuvre de la première solution requiert l’usage de deux composants spécifiques. Le premier représente une surface à insérer à l’interieur d’une fenêtre et instanciant un nouveau ’Desktop’. Le deuxième composant représente une fenêtre interne qu’il est possible de placer à l’intérieur du premier. La deuxième méthode permettant de réaliser le multi−fenêtrage est mise en oeuvre de façon triviale par l’instanciation de plusieurs composants de type fenêtre. L’ajout de la fonctionnalité de multi−fenêtrage à un Framework ne la possédant pas par le biais de cette deuxième méthode n’est pas d’une grande complexité. Elle nécessite principalement la modification des structures permettant de stocker l’information relative à la fenêtre unique afin de pouvoir tenir compte de la multiplicité de celle−ci. Éventuellement, une modification de l’objet fenêtre peut être nécessaire de façon à ce que certaines ressources puissent être partagées. L’emploi de la méthode de multi−fenêtrage par imbrication nécessite plus de travail. En effet, si cette fonctionnalité n’est pas présente dans le Framework, il sera nécessaire de créer les classes représentant les composants de ’desktop’ et de fenêtre interne. Plusieurs étapes sont alors nécessaires pour réaliser cet ajout. La structure du Framework doit être étudiée afin de pouvoir créer les nouvelles classes suivant le même schéma. La librairie graphique utilisée par le Framework doit être elle aussi étudiée afin de connaître la méthode de mise en oeuvre des composants à ajouter. Ensuite, l’écriture des classes est à réaliser, de même que l’adaptation des parties existantes du Framework afin d’intégrer aux mieux les nouvelles fonctionnalités. 5.2. Framework en Java 5.2.1. Introduction Le langage Java peut être considéré comme un Framework du fait de son utilisation d’une machine virtuelle. En ce qui nous concerne, l’API est d’une trop grande complexité pour permettre une prise en main rapide. Une ébauche de Framework en Java a donc été réalisée afin de proposer une solution simple et de valider plusieurs concepts de programmation tels que le mode de description de l’aspect de l’interface graphique ou la méthode de liaison des traite−événements par redirection. 5.2.2. Techniques développées Plusieurs techniques ont été développées lors de la conception de la solution de Framework en Java. Celles−ci ont trait à différents aspects de la programmation d’un Framework en vue de son utilisation la plus aisée et naturelle possible, tout en ne dégradant pas outre mesure les performances des applications réalisées. Les domaines suivants ont fait l’objet d’études particulières : Méthode de démarrage de l’application : obtention de la classe dérivée désirée. Méthode de définition de l’apparence de l’interface graphique. Mécanismes de réaction aux événements. Les diverses problématiques et solutions sont décrites ci−dessous.

5.2.3. Méthode de démarrage de l’application Le démarrage d’une application implique divers travaux routiniers relativement complexes. Ce fait est encore accentué si de nombreuses structures de données doivent être initialisées, ce qui est fréquemment

Page 25/112 eivd Framework sous Linux Bastien Andrès le cas en présence d’une interface graphique élaborée. Il est donc souhaitable que le Framework prenne en charge la plus importante partie de ces travaux, laissant au programmeur final la tâche de ne devoir coder que la partie spécifique à l’application concernée. De ce fait, la fonction ’main()’ est placée dans le Framework. Ce choix pose le problème suivant : il faut alors indiquer quelle classe application dérivée il convient de créer. La solution préférentielle serait de pouvoir placer la méthode main() dans la classe application de base et fournir la classe dérivée lors du lancement de la machine virtuelle Java. Malheureusement, la création d’une instance dans la méthode main génère une instance de la classe de base. Deux solutions permettent de résoudre ce problème. La première consiste à donner le main() à réaliser au programmeur. Celui−ci peut être très court, mais doit permettre la création d’une instance de la classe application voulue. Le main() doit ensuite appeler une fonction du Framework (décrite dans la classe de base) pour démarrer celui−ci. L’exemple suivant illustre cette méthode : public class StartApplication { public static void main(String[] args) { BasicApplication theApp = new MyApplication(); theApp.start(args); } } La deuxième solution consiste à fournir une méthode retournant une instance de l’application dérivée. Cette méthode doit se trouver dans une classe dont le nom est invariable et connu du Framework. La modification de cette méthode permettra de retourner la classe désirée. L’exemple suivant illustre cette méthode : public class ApplicationsDefinitions { // ...

public static BasicApplication getApplication() { return new MyApplication(); } }

public class BasicApplication { // ...

public static void main(String[] args) throws Exception { BasicApplication theApp = null; theApp = ApplicationsDefinitions.getApplication(); appMain.start(args); } } Dans cet exemple, le programmeur final aura la tâche de modifier le fichier ’ApplicationsDefinitions.java’ en remplaçant la valeur de retour de la méthode ’getApplication()’ par la construction d’une instance de sa classe application dérivée. Cette méthode permet de centraliser le travail à faire. En effet, on peut placer dans la classe ’ApplicationsDefinitions’ toutes les constantes de définition des propriétés de base d’exécution de l’application comme par exemple la méthode définie par défaut pour le traitement des exceptions, ou la destination des messages de déboggage. C’est cette dernière méthode qui est utilisée pour la réalisation de l’implémentation du Framework.

Page 26/112 eivd Framework sous Linux Bastien Andrès 5.2.4. Définition de l’interface graphique La plupart des méthodes existantes de création de composants graphiques ont recours à des fonctions de type ’add(Component)’ afin d’ajouter des composants à des containers. L’exemple suivant illustre ce type de code. public static void main(String[] args) { JFrame frame = new Jframe("Title"); Container contentPane = frame.getContentPane(); contentPane.setLayout(new BorderLayout()); //unnecessary contentPane.add(new JButton(" 1 (NORTH)"), BorderLayout.NORTH); contentPane.add(new JButton("2 (CENTER)"), BorderLayout.CENTER); contentPane.add(new JButton("Button 3 (WEST)"), BorderLayout.WEST); contentPane.add(new JButton("Long−Named Button 4 (SOUTH)"), BorderLayout.SOUTH); contentPane.add(new JButton("Button 5 (EAST)"), BorderLayout.EAST); frame.show(); } Cette méthode rend le codage de la création de l’interface graphique fastidieux et artificiel. Il est plus naturel de définir les propriétés des objets par simple assignation de variables−propriétés et de considérer la description des sous−composants comme une propriété de type multiple (implémentée par un tableau, par exemple). Le Framework s’occupe alors de l’ajout des composants à l’aide des fonctions ’add’ du système sous−jacent. On obtient alors une méthode de description plus naturelle de l’apparence de l’interface. Les exemples suivants mettent cette constatation en évidence. L’action conceptuelle décrite par : « Les bords du boutons doivent être de 3 pixels » se traduit plus naturellement par : border = 3; placé dans une méthode spécifique de la classe du bouton, que par : myButton.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); De même, « le container central contient le bouton start et la zone de texte login » se traduit plus naturellement par : childs = {theStartButton, theLoginText}; placé dans une méthode spécifique de la classe du bouton, que par : centralPane.add(theStartButton, BorderLayout.NORTH); centralPane.add(theLoginText, BorderLayout.SOUTH); De ce fait, l’implémentation définit une méthode pour chaque composant, dans laquelle les propriétés sont assignées à l’objet. Le Framework utilise ensuite ces propriétés dans une suite d’appel aux méthodes des composants du système graphique afin de créer ceux−ci et de les paramétrer. Ainsi, pour un bouton, la classe JfButton définit la méthode suivante qui sera dérivée afin de définir les particularités d’un bouton. public void assign() { strTitle = "Default JfButton"; constraint = null; setActionTarget(this, "defaultActionTarget"); } De même, la classe JfPane, représentant un container possède la même méthode avec ces propres propriétés, dont celle de définition des composants contenus : Page 27/112 eivd Framework sous Linux Bastien Andrès public void assign() { layout = new JfLayout(); JfComponent[] jfca = {new JfButton()}; childs = jfca; } Afin de définir un ’panel’ spécifique, la classe JfPane peut être dérivée comme illustré par l’exemple suivant : class MyPane extends JfPane { protected JfComponent[] myChilds = {MyApp.theClrButton, MyApp.theButton, MyApp.theCenterPane};

public void assign() { layout = MyApp.theLayout; childs = myChilds; } } Cette classe uitlise les variables statiques de tous les composants, définies dans la classe application dérivée (MyApp).

5.2.5. Mise en oeuvre des événements Swing La plupart des composants Swing génèrent des événements. Il est possible d’enregistrer des méthodes qui seront appelées lorsqu’un de ces événement se produit. La méthode de travail de Swing est basée sur les interfaces ’Listener’. Chaque type d’événement possède son interface dérivée. Lorsqu’on veut qu’une classe possède une méthode de réaction à des événements d’un certain type, il faudra que la classe implémente le ’Listener’ concerné. L’extrait de code suivant illustre la méthode de réaction à un événement. Le bouton peut générer des événements de type ’Action’. La classe ’MyFrame’ implémente l’interface ’ActionListener’ et doit alors fournir la méthode ’actionPerformed’. Durant la construction de ’MyFrame’, l’instance de celle−ci est ajoutée à la liste des ’Listener’ du bouton. De cette manière, un appui du bouton provoquera l’exécution de la méthode ’actionPerformed’. public class MyFrame extends JFrame implements ActionListener { JButton button;

public MyFrame() { button = new JButton("Click Me"); getContentPane().add(button, BorderLayout.CENTER); button.addActionListener(this); }

public void actionPerformed(ActionEvent e) { button.setText("Clicked !"); } } Certaines interfaces ’Listener’ pouvant nécessiter la présence de plusieurs méthodes de réaction, il peut être ennuyeux de devoir écrire plusieurs méthodes vides afin de se conformer à l’interface décrite. Pour cette raison, Swing propose plusieurs classes d’adaptation pour les interfaces possédant le plus de méthodes. Ces ’Adapter’ implémentent l’interface pour laquelle ils sont prévus et fournissent les méthodes vides. Il est donc possible de créer une classe de réaction à un type d’événement par dérivation

Page 28/112 eivd Framework sous Linux Bastien Andrès de l’Adapter concerné plutôt que par implémentation de l’interface Listener. Il est alors possible de n’écrire que les méthodes choisies. L’extrait de code suivant illustre l’utilisation d’un ’Adapter’ dans le but de n’écrire qu’une des méthodes de réaction de l’interface ’MouseListener’. public class MyClass extends MouseAdapter { ... someObject.addMouseListener(this); ... public void mouseClicked(MouseEvent e) { ...//Event handler implementation goes here... } } Par contre, le fait de dériver d’une classe ’Adapter’ empêche de dériver d’une autre classe, Java ne permettant pas l’héritage multiple. Cela peut être ennuyeux si la dérivation d’une autre classe était envisagée. Une solution à ce problème est de placer les méthodes de réaction (toutes celles demandées par l’interface ’Listener’ concernée) dans une classe implémentant l’interface correspondante, et appeler les méthodes de réaction effectives depuis les méthodes décrites par l’interface. Cela permet de localiser les traite−événements où bon nous semble, tant que la méthode est rendue accessible par le biais des modificateurs adéquats (public). Cela revient à écrire une classe du style des ’Adapter’, mais avec la possibilité d’y mettre d’autres fonctionnalités ainsi que le mécanisme d’assignation des méthodes de traitement effectif. Il faut alors résoudre le problème consistant à décrire de façon simple quelle méthode doit être appelée. Java fournit les objets ’Class’ et ’Method’. Ceux−ci décrivent respectivement une classe et une méthode. Par le biais de méthodes de ces classes et de la classe ’ClassLoader’, il est possible de décrire une méthode spécifique d’une classe spécifique et de l’invoquer. Les mécanismes suivants sont utilisés. L’obtention de la classe d’une classe : Class clsMyClass = Class.forName("MyClassName"); ou d’une instance : Class clsMyClass = anInstance.getClass(); L’obtention d’une méthode : // créer de objets Class des paramètres Class clsParam1 = parameter1.getClass(); Class clsParam2 = parameter2.getClass(); // créer un tableau de la classe des paramètres Class[] clsaParams = {clsParam1, clsParam2}; // obtention de la méthode Method mtdMyMethod = clsMyClass.getMethod("methodName", clsaParams); L’invocation d’une méthode : // créer un tableau contenant les paramètres Object[] objaParams = {parameter1, parameter2}; // appeler la méthode de la classe Object result = mtdMyMethod.invoke(anInstance, objaParams); De cette manière, il est possible de décrire la méthode à appeler en stockant un objet de type ’Method’ (mtdTarget) et un objet de type ’Objet’ (objTarget). La méthode correspondante sera alors : « La méthode ’mtdTarget’ de l’instance ’objTarget’ ». Cela implique que les types des paramètres soient standardisés afin de permettre une construction simple du tableau des paramètres au moment de

Page 29/112 eivd Framework sous Linux Bastien Andrès l’invocation. Dans le cas contraire, il est nécessaire de mettre en place un mécanisme permettant de créer le tableau des paramètres en fonction de la méthode à appeler. En utilisant ces mécanismes, il est possible de décrire assez simplement quel traite−événement doit être exécuté. Le Framework utilise ces mécanismes. Les interfaces ’Listener’ décrivent toutes des traite− événements de la forme : public void actionPerformed(ActionEvent event){}; Le paramètre de ces méthodes est toujours un objet du type de l’événement considéré. Tous les types d’événements ont un ancêtre commun : ’java.util.EventObject’. Il est alors possible de donner un prototype général de traite−événement : public void myHandler(EventObject event); Ainsi, le paramètre de tout traite−événement est connu et permet de simplifier l’invocation de la méthode de traitement. La réaction aux événements se fait donc de la manière suivante dans le Framework. Pour les composants Swing générant des événements qui sont définis comme utile, les classes de ’wrapping’ implémentent le ou les ’Listener’ en question. Elles possèdent donc toutes les méthodes décrites dans ces interfaces. Celles−ci consistent en l’invocation d’une méthode stockée dans l’objet, avec passage de l’objet ’événement’ en paramètre. Une méthode de traitement par défaut est fournie. Comme pour les autres propriétés, la méthode de traitement pour chaque événement est assignée à l’initialisation de l’objet. Une classe utilitaire (JfTarget) fournit une méthode retournant l’objet méthode, permettant ainsi de s’affranchir de la complexité de l’obtention de celui−ci. Les extraits de code suivants illustrent la mise en oeuvre de ces techniques dans le cadre du Framework. La classe JfButton implémente l’interface ’ActionListener’. Le bouton Swing permet la souscription à des événement de ce type. La classe contient les objets de type Method et Object stockant la méthode de traitement. La méthode assign (à dériver) décrit le traitement par défaut. La méthode init() appelle assign puis construit le bouton et inscrit la classe comme ’Listener’ de celui−ci. La méthode setActionTarget permet d’assigner la méthode de traitement. La méthode actionPerformed, de l’interface ’ActionListener’ est appelée lorsqu’un événement se produit et appelle la méthode assignée. La méthode defaultActionTarget est le traite−événement par défaut et ne fait rien. public class JfButton extends JfComponent implements ActionListener { // méthode et objet cible de l’événement protected Method mtdActionTarget = null; protected Object objActionTarget = null; // ...

public void assign() { // ... setActionTarget(this, "defaultActionTarget"); }

// méthode d’initialisation du bouton

public void init() { assign(); button = new JButton(strTitle); button.addActionListener(this); }

// méthode assignant la cible de l’événement

public void setActionTarget(Object objParam, String strMethod)

Page 30/112 eivd Framework sous Linux Bastien Andrès { objActionTarget = objParam; mtdActionTarget = JfTarget.getMethod(objParam, strMethod); }

// méthode de l’interface EventListener (appui du bouton)

public void actionPerformed(ActionEvent event) { Object[] objaArgs = {event}; try { mtdActionTarget.invoke(objActionTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

// méthode par défaut de réaction à l’appui du bouton

public void defaultActionTarget(EventObject event){;} } Le code suivant illustre la dérivation de la classe JfButton permettant la création d’un bouton spécifique. La méthode assign() est dérivée afin d’assigner les propriétés initiales du bouton (son texte, son emplacement dans le ’layout’ et l’action appelée lors d’un appui). Deux méthodes action sont définies. Celles−ci correspondent au prototype d’un traite événement. Chacune change le texte du bouton et modifie la méthode de réaction afin que l’autre soit appelée la fois suivante. class MyButton extends JfButton { public void assign() { strTitle = "MyButton : actionA"; constraint = JfLayout.CENTER; setActionTarget(this, "actionA"); }

public void actionA(EventObject event) { button.setText("MyButton : actionB"); setActionTarget(this, "actionB"); }

public void actionB(EventObject event) { button.setText("MyButton : actionA"); setActionTarget(this, "actionA"); } } Un inconvénient de cette méthode est que plusieurs des méthodes utilisées (forName, getMethod et invoke) sont génératrices d’exceptions. Il est alors nécessaire de prévoir un traite−exception. Dans le cadre du Framework, une méthode statique est définie dans la classe ’JfAppDef’ pour traiter celles−ci. Cette méthode imprime la ’trace’ de l’exception dans la fenêtre ’shell’ ayant servi à lancer Java. Cela permet le déboggage du programme. En effet, une erreur d’assignation de méthode de réaction à un événement est d’une gravité ne permettant pas de récupération aisée. Les avantages de cette méthode de réaction aux événements sont importants. Un premier avantage est l’aisance de définition de la méthode de traitement des événements. Un autre avantage est qu’il devient aisé de changer de traitement par simple modification de la méthode de réaction. Cela permet de

Page 31/112 eivd Framework sous Linux Bastien Andrès construire l’application selon un modèle ne nécessitant plus de nombreux ’if’ ou structures ’switch/case’ dans les méthodes de réaction, permettant de choisir le traitement en fonction de l’état de l’application. Chaque cas possédera alors sa méthode et une modification de l’état de l’application devra être suivie d’une modification des redirections des traite−événements. Ce modèle permet une meilleur dynamique de l’application. En effet, si la plupart des événements ne modifient pas l’état global de l’application, on évite par cette méthode de nombreux test permettant le choix du traitement. De même, un traitement modifiant l’état de l’application possédera aussi dans son code la redirection des méthodes pour les événements futurs. 5.3. Framework en GTK−− 5.3.1. Introduction Le Framework en GTK−− a été réalisé après celui en Java. De ce fait, plusieurs des concepts développés dans ce dernier ont été réutilisés. Ceux−ci ont dû être adaptés au langage. Des solutions ont été développées dans le but de pouvoir implémenter les méthodes précédemment développées en C++. 5.3.2. Référencement Le langage C++ possède des règles strictes concernant l’ordre dans lequel les divers éléments sont définis. De même, il est nécessaire d’avoir recours à des directives de compilation de façon à ce que chaque fichier ne soit inclu qu’une fois. Cette rigueur n’est pas présente en Java. Il a donc été nécessaire de procéder à diverses adaptations des techniques développées dans ce langage. La méthode habituelle d’encapsulation des définitions dans des directives de compilation est utilisée. Ainsi, chaque fichier est entouré des lignes suivantes : #ifndef __NOM_DU_FICHIER__ #define __NOM_DU_FICHIER__ // corps du fichier #endif 5.3.3. Schéma de définition des traite−événements Gtk−− propose deux méthodes de liaison des événements aux fonctions de réaction. L’une par l’emploi d’une fonction de connexion de la fonction au ’signal’. Cette méthode permet une grande flexibilité, mais est de mise en oeuvre complexe. L’autre méthode fournie par Gtk−− est l’utilisation de méthodes membres des composants. En effet, ceux−ci proposent de dériver des méthodes assignées d’office au traitement des événements. C’est cette dernière méthode qui a été utilisée. Il a donc été nécessaire de créer des classes dérivées des composants dont les événements étaient nécessaires de réaliser. La hiérarchie de de composant du Framework étant indépendante de celle de la librairie, la liaison étant assurée par la présence dans les classes du Framework de pointeurs sur les composants de la librairie. Il a donc été nécessaire de définir des objets supplémentaires, dérivés des composants de la librairie. C’est alors sur ces derniers que pointent les variables des composants du Framework. Ces objets supplémentaires dérivent les méthodes de traitement des événements et permettent l’exécution des actions désirées. Celles−ci sont alors présentes dans une troisième classe conçue à cet effet. De ce fait, un composant de la librairie est représenté par trois classes du Framework, dans le cas ou ses événements doivent pouvoir être traités. La première représente le composant proprement dit, dans la hiérarchie d’objets du Framework. La deuxième est une classe dérivée du composant de la librairie, constituant un adaptateur permettant la récupération des événements. La troisième classe représente les traitements à assigner aux événements. L’utilisation du Framework nécessite la dérivation de deux d’entre eux. Le composant lui même, afin d’assigner ses propriétés, et la classe des traitements, afin de préciser les actions à exécuter lorsqu’un événement a lieu. On obtient ainsi, pour le composant bouto, le schéma

Page 32/112 eivd Framework sous Linux Bastien Andrès d’héritage et de référencement suivant :

GfContainer Gtk::Container

GfBin Gtk::Bin

Is a Gtk::Button GfAction

Has a GfEventButton GfButtonAction

GfButton

Lors de la création d’une instance de GfEventButton, un pointeur sur une instance d’une classe dérivée de GfButtonAction lui est transmise. Lorsqu’un événement aura lieu, les traitement décrits dans la classe dérivée de GfButtonAction seront exécutés. Un autre problème a dû être résolu. Pour certains événements, il est nécessaire d’effectuer le traitement décrit dans la méthode de la super−classe. Il convient donc d’appeler celle−ci. Le problème réside dans le fait qu’il s’agit d’une méthode protégée. Il n’est donc pas possible de l’appeler depuis la classe GfButtonAction. De ce fait, cet appel doit être effectué depuis la classe GfEventButton. Afin de pouvoir spécifier des actions à réaliser avant et après l’appel à la super−classe, ainsi que de pouvoir choisir si le traitement effectué par cette dernière doit ou non être effectué, la structure d’un traite− événement de la classe GfEventButton prend la forme suivante. virtual void clicked_impl () { button_action−>pre_clicked_action(); if(button_action−>clicked_parent)Gtk::Button::clicked_impl (); button_action−>post_clicked_action(); }; La classe GfButtonAction possède donc deux métodes de réaction à cet événement. L’une à exécuter avant (pre_clicked_action), l’autre après (post_clicked_action) le traitement de la méthode par défaut. De même, une variable de type booléen (clicked_parent) permet d’indiquer si le traitement par défaut doit être effectué. Dans cet exemple, ’button_action’ représente le pointeur sur la classe GfButtonAction spécialisée. 5.3.4. Hiérarchie des composants du Framework Afin de simplifier l’apprentissage du Framework, la connaissance de la librairie graphique ne devrait pas être nécessaire. De ce fait, tous les composants de la librairie utilisés dans le cadre du Framework sont cachés par une classe de ce dernier prenant en charge la liaison avec la librairie. Afin de pouvoir étendre facilement le Framework à d’autres composants, la hiérarchie présente dans la librairie est respectée et copiée par les classes du Framework. Ainsi, le composant bouton dépend des classes Bin, Container, Widget puis Objet. Toutes ces classes possèdent alors un équivalent dans le Framework. Lors de l’initialisation d’un bouton, toutes les super−classes sont initialisées. Chacune possède un pointeur sur l’objet équivalent de la librairie. Ces multiples pointeurs ont la même valeur puisqu’ils représentent le Page 33/112 eivd Framework sous Linux Bastien Andrès même objet. Leur présence permet néanmoins d’éviter la nécessité d’avoir fréquement recours au mécanisme de ’casting’. 5.3.5. Mécanisme de définition par dérivation de la méthode ’assign()’ Le mécanisme de définition des propriétés d’un composant est repris de la solution Java. Ainsi, lorsqu’un composant particulier est désiré, il est essentiellement nécessaire de dériver l’objet composant et de redéfinir la méthode ’assign()’. Lorsqu’un traitement des événements de ce composant est désiré, il convient de créer une classe dérivée de la classe ’Action’ correspondante et de l’assigner dans les propriétés de l’objet. 5.3.6. Structure de l’application utilisateur L’application utilisateur est formée de deux types d’éléments : des classes dérivées pour spécialiser le comportement des composants, et des variables globales statiques représentant les instances de ces objets. Un soin particulier doit être apporté à l’ordre dans lequel ces éléments sont placés. En effet, une classe d’actions devra nécessairement précéder une classe de composant pour que les actions puissent être assignées au composant. On obtient donc des fichier constitués d’une alternance de définitions de classes et de variables les instanciant. Pour des applications simples, il est alors possible de placer tout le code utilisateur dans un seul fichier. 5.4. Simplification d’un Framework existant Les exemples précédants sont basés sur le langage Java et son API graphique, respectivement sur la librairie GTK−− écrite en C++. La solution de réalisation d’un Framework simple par adaptation d’un Framework existant n’est pas fondamentalement différente. Des classes intermédiaires doivent être créées afin de cacher la complexité de la structure sous−jacente. La simplification s’effectue suivant plusieurs directions. D’une part, proposer un sous−ensemble des composants disponibles afin d’éviter que les utilisateurs ne sachent pas lesquels choisir. D’autre part, réduire le nombre de paramètres réglables pour chaque composant afin de ne laisser que les plus pertinents. Ceux dissimulés peuvent être assignés par défaut. En dernier lieu, il faudra simplifier les tâches complexes. Globalement, les méthodes utilisées pour simplifier un Framework existant peuvent être identiques à celles employées pour réaliser les deux ébauches de Framework. Une autre approche peut être utilisée. Dans le cas ou le Framework de base est assez simple de mise en oeuvre mais trop étoffé, il peut être procédé à une simplification indirecte. Si la plupart des valeurs par défaut sont satisfaisantes, la simplification peut être réalisée par la rédaction d’une version allégée de la documentation utilisateur, passant sous silence les aspects trop complexes. Cette dernière méthode n’est applicable que sous la condition d’usage simple possible du Framework de départ. 5.5. Comparaisons des solutions Les divers solutions ont un point commun. Il est nécessaire de constituer une nouvelle hiérarchie de classes sur la base de celle de l’API, de la librairie ou du Framework utilisé. La simplification de l’utilisation est donc réalisée au prix d’une certaine lourdeur de la solution, celle−ci étant constituée d’un empilement des hiérarchies du Framework réalisé et de celles des outils utilisés. Par exemple, la librairie GTK−− est basée sur la librairie GTK+, chacune possédant ses mécanismes. Les solutions se basant sur un nombre minimal de couches sont préférables car elles nécessiteront moins de ressources processeur et mémoire. Elles permettront une compilation plus rapide et présenteront une meilleure dynamique à l’exécution. Ces considérations ne sont néanmoins pas d’une extrême gravité, un Framework simple ne convenant de toute façon pas pour l’écriture d’applications lourdes et le matériel disponible actuellement étant relativement performant. De ce fait la solution est une question de choix du langage de programmation, puis, lors de la réalisation, il convient de bien définir les fonctionnalités désirées et fixer la limite de la complexité.

Page 34/112 eivd Framework sous Linux Bastien Andrès 6. Conclusion L’étude des techniques de librairies graphiques et de l’API de Java a permi de développer quelques méthodes de programmation permettant de simplifier la programmation d’une application. De même cet apprentissage a conduit à la réalisation d’ébauches de solution permettant de vérifier que ces méthodes sont implémentables et qu’elles sont effectivement facteurs de simplicité. Les questions posées par le cahier des charges ont trouvé réponse lors du développement de ces techniques. Les librairies graphiques et l’architecture d’application sont des domaines vastes et d’un très grand intérêt. Le fait d’avoir orienté le travail vers la recherche de diverses solutions utilisant plusieurs types de techniques et plusieurs langages m’a permis d’acquérir une quantité importante de connaissances de nombreux aspects de la programmation. Les réalisations en sont à un stade d’ébauche, mais permettent de bien appréhender la voie à suivre pour obtenir des solutions performantes. Les étapes suivantes seraient de vérifier et améliorer la robustesse de la structure et des éléments existants pour pouvoir par la suite ajouter les composants supplémentaires sur une base sans faille.

Le candidat :

Bastien Andrès

Yverdon−les−Bains, le 21 décembre 2000

Page 35/112 eivd Framework sous Linux Bastien Andrès 7. Bibliographie La plupart de la documentation utilisée provient de sites Web. De ce fait, la bibliographie référence principalement des liens Internet plus que des ouvrages traditionnels. 7.1. Références bibliographiques ’XFree86 for Linux’ Aaron Hsiao 1999 Que ISBN 0−78−972182−1 ’Using Motif with C++’ David J. Bernstein 1995 Sigs Books ISBN 0−13−207390−0 ’Le système LINUX’ Matt Welsh et Lar Kaufman 1999 O’Reilly ISBN 2−84177−033−8 ’Bien Utiliser StarOffice 5.2’ 2000 ENI Editions ISBN 2−7460−1112−3

De nombreux numéros de magazines dont :

’Login’ posse−presse http://login.posse−press.com

’Linux Magazine France’ Diamond Editions http://www.linuxmag−france.org

’Linux+’ Publications Sepcom http://www.sepcom.fr

’Linux Pratique’ Diamond Editions http://www.linux−pratique.org

’Planète Linux’ DP Presse http://www.dppresse.com 7.2. Références WWW 7.2.1. C / C++ http://cscene.org http://www.maths.warwick.ac.uk/c++/pub/wp/html/cd2/ http://libsigc.sourceforge.net/ 7.2.2. Callback, signal/Slot et Messages //http://www.bestweb.net/~rhickey/functor.html http://libsigc.sourceforge.net/ http://lazy.ton.tut.fi/gtk−−/signals.html 7.2.3. X Windows et XFree86 http://www.x.org http://www.xfree86.org 7.2.4. OSF/Motif et LessTif http://www.rahul.net/kenton/xsites.framed.html http://www.lesstif.org ou http://lesstif.sourceforge.net http://www.cm.cf.ac.uk/Dave/X_lecture/X_book_caller/index.html http://www.cen.com/mw3/

Page 36/112 eivd Framework sous Linux Bastien Andrès 7.2.5. Gtk+ / Gtk−− http://www.gtk.org http://gtkmm.sourceforge.net http://www.ece.ucdavis.edu/~kenelson/libsigc++/ http://www.gimp.org http://www.gnome.org http://libsigc.sourceforge.net http://developper.gnome.org 7.2.6. FOX http://cyberia.cfdrc.com/FOX/ 7.2.7. JX http://www.newplanetsoftware.com/jx/ 7.2.8. Java http://java.sun.com/ http://www.blackdown.org/

Page 37/112 eivd Framework sous Linux Bastien Andrès 8. Annexes 8.1. Projet de semestre Cette annexe consiste en le rapport du projet de semestre qui a précédé le projet de diplôme. Dans une première partie, une recherche a été effectuée dans le but de trouver les solutions existantes de Framework simple du domaine publique sous Linux. Puis une étude des mécanismes de programmation sous Linux ainsi qu’en rapport avec la programmation graphique et événementielle a été menée. Celle−ci a été poursuivie dans le cadre du présent projet. Il a enfin été réalisé un framework en Java, afin de parfaire la maîtrise de ce langage et de tester divers mécanismes de programmation multi−thread ainsi que de communication inter−thread. En parallèle, la configuration de la station Linux à été paufinée. 8.1.1. Introduction Ce document représente la synthèse des recherches et travaux effectués dans le cadre du projet de semestre préliminaire au travail de diplôme ayant pour sujet : « Étude des Frameworks existants, conception et réalisation d’un Framework (LAF) sur Linux, basée par ex. sur Motif : environnement graphique minimal sur Linux ». Plusieurs notions seront premièrement décrites, puis précisées dans le cas du projet, puis les travaux effectués seront décrits. 8.1.2. Introduction des notions principales Le premier terme intervenant dans le projet est celui de framework. Ce terme peut être traduit en français par squelette, échafaudage ou cadre. Ces traductions reflètent bien le concept informatique qu’il décrit. En effet, un Framework est un ensemble de composants constituant un cadre de travail pour la réalisation d’une tâche. Il peut s’agir de tout type de tâche, néanmoins, dans le cadre de ce projet, il s’agira de l’écriture d’applications. Dans ce cas, le Framework met à disposition un squelette autour duquel les applications seront construites et dont il en constituera les fondations. Le Framework fournit alors les structures de base d’une application classique comme l’exécutable principal tandis que le programmeur écrit son code dans des fonctions, méthodes et classes spécifiques à l’application à créer et qui viendront s’assembler sur ces bases. Cette approche est radicalement différente de celle de l’utilisation de librairies, celles−ci étant des outils utilisés par le programme de l’utilisateur, alors que le framework utilise le code de l’utilisateur pour définir son comportement spécifique. Il est à noter qu’il existe des frameworks spécialisés dans l’écriture de certains genres d’applications alors que d’autres sont plus généralistes. De plus, le niveau de fonctionnalité fourni par les différents frameworks varie énormément, nécessitant de la part du programmeur la quantité de travail complémentaire. Deux notions qui s’apparentent à celle de framework sont celles de toolkit et de widget−set. Le terme toolkit décrit des ensembles d’outils d’aide à la programmation. Un framework peut être considéré comme un toolkit. Un widget−set est un ensemble de composants graphiques additionné de code permettant de récupérer les diverses actions de l’utilisateur ainsi que de modifier leur apparence et comportement. Un widget−set n’est pas un framework, bien que dans de nombreux documents, la séparation ne soit pas aisément discernable. En effet, un widget−set ne fourni pas le squelette de l’application mais juste des outils facilitant la programmation d’interfaces graphiques. Dans le cadre de ce projet, il convient de préciser le type de framework qui est considéré. En effet, celui−ci doit permettre aux étudiants d’appréhender pratiquement l’utilisation d’un framework. Dans ce but, il est nécessaire qu’il soit simple d’accès et de taille raisonnable afin de permettre une prise en main rapide et l’écriture d’applications simples à but didactique en peu de temps.

Page 38/112 eivd Framework sous Linux Bastien Andrès 8.1.3. Travail de recherche

8.1.3.1. Frameworks disponibles sous Linux Une très grande quantité de frameworks sont disponibles pour Linux, dont une majorité sont ’libres’ ou gratuits. La recherche ’+framework +linux’ donne : ’www.google.com’ 123’000 réponses ’www.altavista.com’ 6’455 réponses ’www.yahoo.com’ 54’000 réponses De nombreuses pages ont trait aux frameworks (de tous types). Outre les pages relatives à des produits ou solutions spécifiques, certaines pages traitent des frameworks en général. L’une d’entre elle : ’http://www.geocities.com/SiliconValley/Vista/7184/guitool.html’ répertorie une grande quantité de frameworks, widgets−sets et autres outils de création d’interfaces graphiques. Elle constitue un point de départ de grande qualité pour la recherche des frameworks disponibles. Vu le grand nombre de ceux−ci, il ne sera pas fait une énumération de toutes leurs caractéristiques, mais une brève description des particularités des plus importants framework gratuits ou libres en langage C ou C++. 8.1.3.2. Description des principaux frameworks ’libres’ disponibles sous Linux OpenAmulet (http://www.openip.org/oa_overview.html) Développé par l’organisation OpenIP (Open Intellectual Property) sur la base d’Amulet, originalement développé à l’université de Carnegie Mellon, il s’agit d’un framework de création d’interfaces graphiques. Actuellement disponible pour Unix et Windows. Berlin (http://www.berlin−consortium.org) Système de fenêtrage basé entre autres sur le framework InterViews. Actuellement en développement et d’une grande ampleur, il est destiné à devenir à terme une alternative à XWindows. Ce projet regroupe de nombreux projets abandonnés de frameworks et de toolkits. Coral (http://www.imonk.com/coraldoc.html) Framework très complet comprenant de nombreuses fonctionnalités non−GUI, telles que la gestion multi−thread ou le support réseau. Disponible pour Windows et Unix. Gnome (http://developer.gnome.org) Système graphique bien connu sous Linux. Les bases de Gnome constituent un framework très complet. Le projet est en cours de développement et des nouvelles versions assez stables sont disponibles rapidement. Les concepts de base sont actuellement en refonte radicale. Il en découle de nouvelles versions très différentes et sans doute incompatibles. GNUstep (http://www.gnustep.org) Framework de développement orienté−objet et basé sur la spécification originale d’OpenStep (Next). Le langage de programmation utilisé est ObjectiveC, une alternative au C++ dont la syntaxe est différente mais plus évoluée dans son orientation objet. Est disponible pour les principaux Unix libres. Gtk−− (http://gtkmm.sourceforge.net) Un framework basé sur gtk+(librairie graphique développée pour le projet Gimp). Propose un environnement performant en C++. Il utilise une librairie particulièrement performante pour la gestion des signaux, libsigc++, disponible également séparément. JX (http://www.newplanetsoftware.com/jx/)

Page 39/112 eivd Framework sous Linux Bastien Andrès Framework très complet supportant le modèle ’MVC’ ainsi que de très nombreux types d’objets. Le langage de programmation est le C++. Il est basé directement sur Xlib. Il est donc disponible pour Unix uniquement (et éventuellement sur Windows moyennant Cygwin). Il utilise ACE (Adaptative Communication Environnement) qui constitue une couche d’abstraction de l’OS en ce qui concerne la communication inter−processus et réseau. Il s’agit d’une solution très professionnelle. KDE (http://developer.kde.org) Autre système graphique très connu sous Linux. Ses bases constituent aussi un framework de grande ampleur. KDE connaît aussi des changements radicaux de concept ces temps. Qt (http://www.trolltech.com) Un framework de grande qualité. Propose de nombreuses possibilités graphiques. Développé par TrollTech, il constitue la base du système graphique de KDE. La licence de cette librairie a été assouplie très récemment, afin de permettre une large distribution. Il existe une version commerciale pour Windows. V (http://www.objectcentral.com) Un framework écrit par un professeur pour ses cours de programmation. Très simple, il permet l’initiation à certains concepts dont celui de framework. Il tourne sous UNIX et sous Windows. Ce framework est décrit plus précisément par la suite. ViewKit (http://www.viewkit.com) Framework en C++ sous Motif et CDE. Flexible, il met à disposition des programmeurs de nombreux outils, tout en permettant l’accès direct à la librairie Motif. WxWindows (http://web.ukonline.co.uk/julian.smart/wxwin) Framework multi−plate−formes permettant un portage quasi sans effort des programmes l’utilisant (Mac−Windows−Unix−OS/2). Il s’agit d’un framework très connu et toujours en développement. YAAF (http://www.pandawave.com/yaaf1.html) Framework multi−plate−formes (Mac−Windows−Unix). Il en est à la version 1.08 donc sans doute assez récent et pas encore correctement documenté !? Zinc (http://www.zinc.com/products/zaf/zaf.htm) Framework supportant de nombreuses plate−formes (dont DOS) ainsi que des systèmes embarqués. Certaines versions sont gratuites, dont celle pour Linux (version non professionnelle). La plupart de ces framework sont de bonne qualité et proposent un grand nombres de fonctionnalités. Chacun propose sa version de la structuration d’une application, d’une interface graphique voire du système. Dans le cadre de ce projet, nous nous intéressons à un framework de faible taille et simple à apprendre. Peu d’entre ceux proposés ci−dessus présentent ces qualités. Le principal candidat est ’V’. 8.1.4. Description du framework ’V’ Le framework ’V’ propose des caractéristiques suivantes. Il est disponible sous licence LGPL, permettant sa libre utilisation, distribution et modification sous contrainte que son code source soit disponible et que toutes les modifications soient documentées et tous les auteurs cités. Il fonctionne sous Windows et Unix, le passage de l’un à l’autre ne nécessitant qu’un nombre très restreint de modifications. Linux est donc supporté, en s’appuyant sur les librairies de composants ’Athena’ (Xaw) (en plus de X Toolkit (Xt)), sur Motif ou sur gtk. Il est écrit en C++ et suit une approche orientée objet. Il comprend un nombre restreint de classes (une trentaine environ) le rendant d’accès assez simple par rapport à la majorité des autres frameworks se composant pour certains de plusieurs centaines de classes. Il a été écrit par un professeur (Bruce Wampler) de l’université du Nouveau Mexique pour ses cours de programmation. De ce fait, il est prédestiné à un usage didactique et illustre bien les concepts de

Page 40/112 eivd Framework sous Linux Bastien Andrès framework et de programmation orientée objet. Il permet néanmoins la réalisation d’applications totalement fonctionnelles. Une documentation d’environ 230 pages est disponible et, selon les dires de l’auteur, permet à elle seule une mise en oeuvre du framework. Un mode de debugging est disponible, documentant les événements. L’archive comprend le framework, plusieurs utilitaires, ainsi que quelques exemples en illustrant l’utilisation. La modeste taille de la solution a été atteinte en faisant quelques compromis. Parmi les simplifications effectuées, nous remarquons que certaines listes de composants sont statiques. Cette solution s’écarte quelque peu de l’orientation objet mais est nettement plus simple à comprendre et à utiliser. De même, les messages événementiels sont bien envoyés aux objets cibles, mais leur discrimination doit être effectuée par analyse du type de message (structure switch/case). Il est à noter que l’affichage souffre de quelques menus défauts, en tout cas dans la version ’Motif’, lors de modification de la taille des composants. La version ’Motif’ étant assez récente, ces défauts seront sûrement corrigés dans une prochaine mise à jour. La home page de ’V’ sur internet (’http://www.objectcentral.com’) présente tous les aspects de ce framework ainsi que des outils associés. 8.1.5. Mise en place de la plate−forme Le système d’exploitation utilisé dans le cadre de ce projet est GNU/Linux. Au vu de son utilisation restreinte au laboratoire de Télécommunications, du moins au début du semestre, il a été nécessaire d’installer ce système sur une machine. La distribution installée a d’abord été une Mandrake7. Mais de nombreux éléments ont poussé à abandonner cette solution. En effet, la Mandrake est une distribution récente et visant à fournir à moindre effort une station opérationnelle pour des personnes inexpérimentées ou ayant besoin d’un niveau élevé de sécurité et de performance sans devoir régler de nombreux paramètres. Cette distribution atteint ces buts de manière relativement correcte, au vu de l’engouement qu’elle suscite actuellement, mais au prix d’un besoin élevé de mémoire et de rapidité processeur, ressources manquant cruellement sur la machine cible. Comme plusieurs autres, elle gère de manière centralisée de nombreux paramètres, offrant des outils de configuration pour leur gestion. Cette façon de faire est efficace pour des besoins généraux, mais nécessite un travail d’autant plus accru lorsque des conditions particulières se présentent. En effet, dans ce cas, il n’est plus seulement nécessaire de trouver la solution au problème, mais encore de trouver la solution qui conviendra au système de gestion des ressources. Ainsi, pour une station de développement, il m’apparaît plus commode d’utiliser la distribution Slackware, qui est d’abord plus technique, mais qui permet, lorsqu’on commence à la connaître, de résoudre les problèmes rapidement et de manière simple. Cette distribution a donc remplacé la Mandrake et, jusqu’à présent, satisfait à toutes les attentes. La distribution Slackware7 ne possède pas forcément tous les gadgets actuels qui ornent les puissantes machines et qui sont présents sur d’autres distributions, mais offre des outils de développement qui suffisent parfaitement pour travailler sur une machine peu puissante. Cette machine a été configurée de façon à s’intégrer dans le réseau de l’école. Les divers paramètres réseau ont été ajustés de manière à correspondre aux valeurs attribuées à la machine. De plus, ’Samba’ à été installé, permettant la communication avec les machines du monde Windows. Il est ainsi devenu possible de se connecter aux volumes partagés et de transférer des fichiers depuis et vers ceux−ci. Après plusieurs heures de recherche, des scripts ont été mis au point de façon à pouvoir imprimer sur la LaserJet du laboratoire. X a aussi été configuré et l’environnement KDE a été ajusté de façon à restreindre au maximum les ressources utilisées. 8.1.6. Installations spécifiques Le framework ’V’ étant d’un intérêt accru dans le cadre de ce laboratoire, il a été téléchargé, puis installé. Il a été premièrement nécessaire d’installer la librairie ’LessTif’, remplaçante ’open source’ de ’Motif’, que ’V’ utilise. Il a été alors possible d’essayer les différents programmes exemples avec succès. Destiné principalement à la réalisation de divers laboratoires, l’environnement de programmation Java a aussi été installé et a permis plusieurs essais relatifs à la technique de programmation orientée objet et aux frameworks.

Page 41/112 eivd Framework sous Linux Bastien Andrès 8.1.7. Apprentissage du travail sur la plate−forme Linux L’utilisation journalière de Linux m’a accoutumé aux outils et aux mécanismes de travail sous Linux, comme l’écriture de script, la recherche d’informations ou l’installation de composants à partir des sources. 8.1.8. Étude des mécanismes de programmation L’utilisation des MFC, dans le cadre d’un projet lors du précédant semestre, m’a initié à la programmation événementielle, de même qu’au concept de base de framework. Durant ce semestre, la lecture de nombreux articles et pages web m’a permis d’appréhender divers aspects des techniques utilisées dans le cadre de la programmation des couches de base tels que la gestion des threads, des signaux, des messages et de la synchronisation, de même que la réalisation de hiérarchies de classes cohérentes implémentant ces mécanismes. L’utilisation des librairies de composants graphiques (widgets−set) a aussi été étudiée. La lecture d’un ouvrage sur XWindow m’a permis de comprendre une partie du fonctionnement de ce système de fenêtrage qui représente la solution de prédilection sous Unix. 8.1.9. Réalisation d’une ébauche de framework Afin de mettre en pratique les techniques étudiées et de les essayer réellement, j’ai réalisé un ensemble de classes constituant une ébauche de framework. Cette réalisation à été programmée en Java, pour sa simplicité et la rapidité avec laquelle il est possible d’obtenir des résultats exécutables. Elle consiste en un ensemble de classes constituant un environnement applicatif. Le concept de programmation événementiel est présent, que ce soit entre les divers composants, ou en réaction à l’utilisateur, à travers l’utilisation des classes Swing. Le concept de framework est aussi présent, les divers composants représentant des instances de classes dérivées et dont les parties spécifiques correspondent à la surcharge de méthodes des classes de base. De même, le ’main()’ fait partie de l’environnement. Celui−ci est multi−thread et le dialogue entre eux est effectué par le biais de transmission de messages dans des queues. 8.1.10. Conclusions Il a été procédé à la recherche des frameworks disponibles sous Linux, en portant l’attention vers des solutions simples. Le framework ’V’, très intéressant puisque coïncidant particulièrement bien à l’utilisation prévue du framework a été étudié plus avant, installé et testé. Le système d’exploitation Linux, sur lequel doit être réalisé le framework à été installé et configuré de manière à être opérationnel et à permettre des développements logiciels. Je me suis familiarisé à son usage. De nombreux concepts et mécanismes intervenant dans la réalisation d’un framework ont été étudiés. Certains d’entre eux ont été sommairement essayés dans un bref essai logiciel. Il faut alors définir les objectifs à atteindre par la suite. Tout d’abord il convient de choisir la spécification des fonctionnalités que doit offrir le framework à obtenir. Plusieurs alternatives apparaissent, réalisables de diverses manières. Il peut être désiré que le framework satisfasse aux spécifications de ’LAF’, car celui−ci étant déjà utilisé dans le cadre de l’école, la transition vers Linux en serait facilitée. Une telle solution peut être atteinte de plusieurs manières. Premièrement par le portage de ’LAF’, c’est−à−dire par la réécriture du code. Deuxièmement par la réalisation d’une couche d’adaptation de l’interface de ’LAF’ sur un framework sous Linux ayant fait ses preuves. Il peut aussi être désiré d’utiliser la spécification de ’V’ ou d’un autre framework existant sous Linux pour sa stabilité et l’intérêt de sa solution, tel quel, ou modifié. Dans quel cas, un travail d’adaptation au cadre de l’école, ainsi que de documentation en français et adaptée à l’enseignement est à effectuer. Il peut enfin être désiré redéfinir la spécification du framework, non identique à une solution existante.

Page 42/112 eivd Framework sous Linux Bastien Andrès Dans ce cas le framework devra être réalisé, soit en adaptant une solution disponible, soit depuis la base. Ces diverses solutions correspondent à des quantité différentes de travail. En fonction de la solution sur laquelle se sera porté le choix, il conviendra de définir le travail à réaliser dans le cadre du travail de diplôme. 8.2. Journal de Projet 8.2.1. Semaine du 2 au 6 octobre Préparation du travail. Analyse de la structuration du travail en étapes. Recherche de documentation sur les librairies graphiques utilisables pour la réalisation du Framework. Pré−sélection des librairies intéressantes. Début de l’étude des librairies graphiques : Etude de la structure et des mécanismes de base de Motif. Etude de l’historique de Motif en vue de sa description. 8.2.2. Semaine du 9 au 13 octobre Suite de l’étude de Motif. Passage à StarOffice 5.2 : Récupération des anciens documents Création d’un premier modèle de document A4 et personnalisation de quelques styles. Etude de la librairie graphique Gtk et affiliées (Gtk−−, Gdk, Glib) Etude des mécanismes de Callback et de Signal / Slot Etude des mécanismes de communication par messages du système d’exploitation QNX. Etude de la librairie Qt. 8.2.3. Semaine du 16 au 20 octobre Création d’un document de référencement des sources bibliographiques et web. Recherches concernant l’historique de Gtk en vue de sa description. Etude de l’utilisation des Macros par Qt. Etude de l’utilisation par Qt d’un précompilateur et son extension du langage C++ Etude des fonctionnalités fournies par LAF. Etude du démarrage d’une application (liaison programmation classique − objet). 8.2.4. Semaine du 23 au 27 octobre Ecriture du document de description et de comparaison des librairies graphiques. Début de l’étude de la structure des classes Java Swing Etude de l’écriture d’applications Swing Thread Safe Etude de la manière de décrire naturellement la composition d’une interface graphique. Etude et essais de la manière de démarrer une application en Java. Analyse de l’utilité d’utilisation des Threads dans le Framework. 8.2.5. Semaine du 30 octobre au 3 novembre Ecriture de classes d’entrées/sorite simple en mode texte ou graphique à des fins de test. Etude de la hiérarchie des composants des classes Swing. Etude des composants ’TopLevel’ (Window, Frame, Dialog). Etude des composants fournis par LAF et leurs relations. Sélection des composants à réaliser et leur priorité dans le cadre d’essais ou de réalisation.

Page 43/112 eivd Framework sous Linux Bastien Andrès Etude des mécanismes d’événements en provenance de l’utilisateur. Comparaison des solutions utilisées par QNX et Swing pour la distribution des événements souris. 8.2.6. Semaine du 6 au 10 novembre Etude de la composition des interfaces graphiques. Etude des tâches dévolues aux ’Window Manager’ Réflexion sur les similitudes entre la composition d’une interface graphique et un système de fichier. Etude de la possibilité de reprise d’éléments du framework réalisé durant le projet de semestre. Etude de l’API ’Réflexion’ de Java (travail sur les classes et leur contenu). 8.2.7. Semaine du 13 au 17 novembre Installation du JDK 1.3 et analyse des différences avec la version 1.2.2. Développement du concept de redirection des traite−événements (No If Model). Etude de la vision de l’application comme une machine à états. Etude des exemples d’utilisation des composants Swing présents dans la documentation du Jdk. Première définition de la structure du Framework en Java. Sélection des composants nécessaires à une première étape de réalisation. 8.2.8. Semaine du 20 au 24 novembre Définition de la structure interne des classes et de leurs interactions. Définition de classes utilitaire et de la répartition de méthodes générales. Etude de l’API ’Event/Listener’ de Java. Sélection des événements importants à inclure dans le Framework. Début de l’écriture du Framework en Java. Premiers tests et modifications des méthodes d’initialisation des composants. Améliorations des méthodes d’initialisation et de composition de l’interface graphique. 8.2.9. Semaine du 27 novembre au 1er décembre Etude de l’utilisation des menus et essais des exemples. Ajout des composants liés aux menus dans le Framework en Java. Etude de la mise en oeuvre de Threads de ’calcul’ en relation avecune interface Swing. Etude de la compléxité de l’ajout de Thread de ’calcul’ dans le Framework. (−> trop complexe). Ecriture d’une partie de rapport concernant la réalisation du Framework en Java. Réalisation de la page web du projet (page principale présentant le résumé et liens). 8.2.10. Semaine du 4 au 8 décembre Etude et réalisation de la sécurisation de répertoires dans le cadre du serveur http Apache. Création du plan du rapport et début de rédaction. Installation de la librairie Gtkmm et essais des exemples fournis. Etude des schémas de dépendances en C++. Etude de la structure du framework V et de LAF. Etude de la documentation de Gtk−− et de sa hiérarchie de composants. Choix des composants Gtk−− et définition de la structure du Framework en Gtk−−. 8.2.11. Semaine du 11 au 15 décembre Ecriture du Framework en Gtk−−. Essais, déboggage et modifications du Framework suite à des problèmes de référencement. Définition de la structure hiérarchique permettant la définition fléxible des traite−événements.

Page 44/112 eivd Framework sous Linux Bastien Andrès Ecriture, essais et adaptation du mécansime de traitement des événements. Rédaction du rapport. 8.2.12. Semaine du 18 au 21 décembre Rédaction du rapport. 8.3. Documentation Utilisateur Les ébauches de Framework réalisées ne sont pas assez avancées pour permettre leur utilisation autre que celle de test. En effet ceux−ci ont été réalisés pour valider les techniques développées et se rendre compte des problèmes liés à leur mise en oeuvre. Leur état permet une compilation et une exécution d’exemples triviaux mais ne permet pas la création d’application réellement utilisable. De ce fait, une documentation utilisateur digne de ce nom n’est pas envisageable. Par contre, il est décrit sommairement les opérations nécessaires à l’obtention d’un environnement permettant une compilation et une exécution des exemples fournis. Dans les explications qui suivent, il est admis qu’une station Linux est disponible et qu’un environnement graphique fonctionnel est configuré. 8.3.1. Utilisation du Framework en Java Un environnement de développement Java est nécessaire. Il convient donc de s’assurer de la présence du Jdk 1.2.2 ou 1.3. Dans le cas ou aucun n’est présent, il faudra en installer un. Il s’agit de logiciel gratuit. Il est donc possible de le trouver sur un CD, une archive locale. Sinon, Le site de Sun (http://java.sun.com) ou celui de Blackdown (solutions Java pour Linux, http://www.blackdown.org) permettent le téléchargement. Il peut être intéressant de disposer du compilateur d’IBM : Jikes. Celui−ci est nettement plus rapide et au moins aussi bien documenté. Celui−ci peut être téléchargé depuis le site Blackdown. Cet environnement doit être configuré. Il est nécessaire que les variables CLASSPATH et JAVA_HOME soient configurées dans le shell utilisé. La variable JAVA_HOME doit refléter le chemin ou se trouve le Jdk. La variable CLASSPATH devrait contenir le répertoire de travail (.) et l’archive contenant les classes du Jdk. Le Framework doit ensuite être installé. Le fichier makefile doit être adapté suivant le compilateur utilisé. Généralement, il s’agira que la ligne suivante soit présente : JAVAC = javac Le script ’run’ permet l’exécution du projet après compilation (make) si les paramètres du makefile n’ont pas été modifiés. La création d’une application nécessite les étapes suivantes. Chaque composant présent dans l’interface graphique nécessitera la création d’une classe dérivée. Il est nécessaire de ne redéfinir que la méthode ’assign()’. La méthode de la classe de base peut être copiée afin de servir de base de travail. De même, la classe application devra être dérivée. Sa méthode ’assign()’ devra être redéfinie. Cette classe devra en plus contenir une variable statique pour chaque composant défini. Cela permet de les référencer. Les variables à ajouter seront donc du genre : public static MyButton theButton = new MyButton(); Leur initialisation est nécessaire. Une dernière étape est la modification de la classe JfAppDef. Il est nécessaire de modifier la construction de la classe constituant la valeur de retour de la méthode statique ’getApplication’. Il Page 45/112 eivd Framework sous Linux Bastien Andrès convient de générer la création d’une instance de la classe application dérivée créée. Cette ligne doit correspondre à la suivante, ou’MyApp’ est à remplacer par le nom choisi. return new MyApp(); La compilation peut être effectuée en utilisant la commande ’make’. Si le compilateur choisi est javac, il est possible que cela prenne un certain temps si aucun fichier classe n’est à jour. Le fichier MyApp.java donne un exemple d’application qu’il est possible de suivre. Celui−ci illustre l’utilisation de la plupart des composants. 8.3.2. Utilisation du Framework en Gtk−− Plusieurs librairies sont nécessaires à la compilation et à l’exécution de projets utilisant ce Framework. Il est également nécessaire que les outils GNU de développement C et C++ soient présents. Ces derniers sont présents dans la plupart des cas si une distribution Linux a été installée correctement. Les librairies nécessaires sont les suivantes (ou versions supérieures): libgtkmm−1.2.so.0 libgdkmm−1.2.so.1 libgtk−1.2.so.0 libgdk−1.2.so.0 libgmodule−1.2.so.0 libglib−1.2.so.0 libdl.so.2 libXi.so.6 libXext.so.6 libX11.so.6 libsigc−1.0.so.0 libpthread.so.0 libstdc++−libc6.2−2.so.3 libm.so.6 libc.so.6 /lib/ld−linux.so.2 La plupart de ces librairies doivent figurer sur toute distribution. Néanmoins, la première partie de la liste sera généralement absente. Il convient de les installer. Des archives sont disponibles convenant à la plupart des distributions Linux. Celles−ci peuvent être obtenues à partir des sites http://www.gtk.org et http://gtkmm.sourceforge.net. Il s’agit des librairies suivantes : glib (actuellement glib−1.2.8) gtk+ (actuellement gtk+−1.2.8) libsigc++ (actuellement libsigc++−1.0.2) gtkmm (actuellement gtkmm−1.2.4) Lorsque les librairies seront installées, le Framework devra être copié dans un répertoire. Comme précédemment décrit, l’application peut consister en un seul fichier. Le fichier GfApp.cc est un exemple de réalisation. Celui−ci illustre la forme à donner au fichier représentant l’application. Chaque objet de l’interface graphique devra faire l’objet de la création d’une classe dérivée du composant désiré. Seule la méthode ’assign’ est à redéfinir. Une classe application dérivée devra être créée. Celle−ci redéfinira suivant les besoins ’initInstance’ et ’assign’. De plus pour chaque composant dont les événements sont à intercepter, une classe d’actions doit être dérivée. La liaison avec le Framework est effectuée par la présence de la variable statique de nom theApp. Celle−ci doit correspondre à une instance de la classe application dérivée générée. Le paragraphe suivant, répété de la documentation de développement décrit le résultat obtenu: « L’application utilisateur est formée de deux types d’éléments. Des classes dérivées pour spécialiser Page 46/112 eivd Framework sous Linux Bastien Andrès le comportement des composants et des variables globales statiques représentant les instances de ces objets. Un soin particulier doit être apporté à l’ordre dans lequel ces éléments sont placés. En effet, une classe d’actions devra nécessairement précéder une classe de composant pour que les actions puissent être assignées au composant. On obtient donc des fichier constitués d’une alternance de définitions de classes et de variables les instanciant. Pour des application simples, il est alors possible de placer tout le code utilisateur dans un seul fichier. » 8.4. Implémentation du Framework en Java 8.4.1. Introduction Les techniques utilisées ont été décrites ci−dessus. Cette section décrit le Framework réalisé dans sa forme. L’ébauche de Framework se compose de douze classes. Il permet la construction d’applications très simples. Seul un sous−ensemble des composants graphiques mis à disposition par les classes Swing est présent, de même qu’un sous−ensemble des fonctionnalités de ceux−ci. Cet ensemble permet néanmoins de tester le Framework et illustre clairement la manière de l’étendre à la plupart des autres composants et fonctionnalités de ceux−ci. 8.4.2. Classes De ces douze classes, six sont des composants graphiques véritables, tandis que trois sont des composants auxiliaires, deux sont des classes utilitaires et une représente l’application principale. Les noms des classes commencent tous par ’Jf’, un repère pouvant être assimilé à ’Java Framework’. Les classes correspondant à des composants graphiques sont des classes d’emballage des composants fournis par Swing ; c’est−à−dire que la classe contient le composant et fournit certaines fonctionnalités, tout en dissimulant l’implémentation du composant Swing. Les classes sont les suivantes : JfApplication la classe application de base JfAppDef une classe utilitaire globale pour l’application JfTarget une classe utilitaire simplifiant la tâche d’assignation des traite−événements JfLayout une classe de simplification du concept de ’layout’ JfFrame le composant fenêtre JfComponent la super−classe de la plupart des composants graphiques JfPane le composant ’panel’ JfButton le composant bouton JfTextField le composant ’zone de texte’ JfMenuBar le composant ’barre de menu’ JfMenuItem le composant ’élément de menu’ JfMenu le composant ’menu déroulant’ 8.4.3. Héritage En Java, tous les objets dérivent directement ou non de la super−classe ’Object’. Il en va donc de même des classes du Framework. Certaines classes de celui−ci sont dérivées d’autres d’entre−elles. Les composants graphiques pouvant être présents dans la partie ’cliente’ d’une fenêtre, excepté les éléments de menus, sont dérivés de la classe ’JfComponent’. Cette dérivation permet d’utiliser le polymorphisme lors de l’insertion des composants dans un ’container’. De même, la classe ’JfMenu’ dérive de

Page 47/112 eivd Framework sous Linux Bastien Andrès ’JfMenuItem’ permettant ainsi de créer des sous−menus en remplaçant un élément de menu par un menu. Nous avons donc le schéma d’héritage suivant (les classes concernées uniquement) :

JfComponent JfMenuItem

JfPane JfButton JfTextField JfMenu

8.4.4. Description Ce paragraphe consiste en la description du contenu et des particularités de codage des classes du Framework.

8.4.4.1. JfApplication La classe JfApplication représente la classe application de base. Elle doit être dérivée afin d’obtenir une application spécifique. Celle−ci possède une unique donnée membre : la fenêtre principale de l’application (JfFrame frame). Elle possède un constructeur ainsi que trois méthodes : initInstance(String[] args), assign() et init(). Les deux premières peuvent être dérivées et permettent respectivement l’initialisation globale de l’application et l’assignation des propriétés de celle−ci. La méthode init() permet l’initialisation récursive de l’application et de ses composants. Cette classe possède aussi la méthode statique ’main(String[] args)’ qui est appelée lors du lancement de l’application. Cette dernière obtient tout d’abord une instance de la classe application dérivée par un appel à la méthode ’getApplication()’ de la classe JfAppDef, puis appelle successivement initInstance et init de l’instance obtenue. La suite de l’exécution est contrôlée par le thread du système graphique. 8.4.4.2. JfAppDef La classe JfAppDef est une classe utilitaire globale à l’application. Elle représente l’endroit de prédilection pour placer des méthodes statiques effectuant des traitement relatifs à l’application dans son ensemble. Elle contient deux méthodes statiques. Vu qu’il est fait référence à ses méthodes statiques, cette classe doit être modifiée plutôt que d’être dérivée. La méthode ’getApplication()’ retourne une instance de la classe application dérivée. Elle est à modifier afin que l’instance retournée corresponde effectivement à la classe application désirée. La méthode ’notifyException(Exception e)’ est le traite−exception par défaut. Elle peut être modifiée ou dérivée. Le traitement par défaut est l’impression de la ’trace’ de l’exception.

8.4.4.3. JfTarget Il s’agit d’une classe utilitaire permettant la simplification de l’obtention des objets de type ’Method’ décrivant un traite−événement. Il est possible de l’utiliser par création d’instance et appel aux méthodes ’setTarget(Objet, String)’ puis ’getMethod()’. Néanmoins, il est plus facile de n’utiliser que la méthode statique ’getMethod(Object, String)’ effectuant toutes les étapes en une seule fois sans création d’instance. 8.4.4.4. JfLayout Il s’agit d’une classe d’emballage du concept Java de ’layout’. Elle définit le positionnement des

Page 48/112 eivd Framework sous Linux Bastien Andrès composants à l’intérieur d’un ’container’. Elle permet de fixer le mode de positionnement. Elle permet d’utiliser les principaux modes fournis par Swing : BorderLayout, FlowLayout, BoxLayout, GridLayout et GridBagLayout. Une extension permettant l’utilisation du CardLayout et du positionnement sans ’manager’ est prévue. Cette classe contient la redéfinition de plusieurs constantes définies à la base dans diverses classes Swing et définissant la position des composants par le biais des différents layouts proposés, ainsi que les noms des types de ’layout’. Elle contient également plusieurs variables représentant le layout et ses propriétés. Le constructeur est celui par défaut. Plusieurs méthodes utilisées par le Framework permettent l’accès aux variables. La méthode ’assign()’ permet l’assignation des propriétés du layout. La méthode ’init()’ permet l’initialisation du layout en utilisant les propriétés assignées. La méthode ’getLayout()’ permet l’obtention de l’instance de ’LayoutManager’ et est utilisée pour l’assigner aux ’panels’ lors de leur initialisation. 8.4.4.5. JfFrame Cette classe représente un ’top−level widget’. C’est−à−dire un composant graphique du plus haut niveau. Tout composant graphique est contenu dans un de ces composants ou en est un. L’objet Swing ’JFrame’ que représente cette classe est une fenêtre. Cette classe possède une référence sur une insatance de la classe JFrame qui est la fenêtre Swing. Elle possède deux références sur la barre de menu et une JfPane, représentant la surface sur laquelle se trouvent tous les autres composants. Elle possède de plus des variables représentant les propriétés de la fenêtre ainsi que les méthodes traite−événement. Les méthodes de cette classe sont de plusieurs types. Les méthodes nécessaires à l’initialisation, les méthodes requises par l’interface ’WindowListener’, les méthodes d’assignation des traite−événement et les traite−événement par défaut. Les méthodes nécessaires à l’initialisation sont au nombre de deux. Une première, à dériver, est la méthode ’assign()’ dévolue à l’assignation des propriétés. La seconde, ’init()’ permet l’initialisation récursive de tous les composants. Celle−ci ne doit en général pas être dérivée. Les méthodes de l’interface ’WindowListener’ permettent la redirection des événements vers les méthodes choisies. Celles d’assignation des traite−événement permettent l’assignation des méthodes choisies aux variables les stockant. En l’absence de leur utilisation, les événements sont dirigés vers deux méthodes de traitement par défaut. L’une, assignée à l’événement de fermeture de la fenêtre termine l’application, l’autre assignée par défaut aux autres événements n’effectue aucun traitement. 8.4.4.6. JfComponent La classe JfComponent est la super classe des composants graphiques pouvant être imbriqués dans la fenêtre. La fenêtre et les éléments de menu n’en sont pas. Cette classe définit les méthodes dont la présence est nécessaire dans tous les composants : ’init()’ et ’assign()’. Elle définit aussi les variables ’component’ et ’constraint’. Celles−ci représentent le composant générique Swing, respectivement un objet de définition de la manière de disposer le composant. Ces deux variables sont utilisées lors de la réalisation de la structure d’imbrication des composants. Il est ainsi possible d’ajouter le composant à un conteneur par polymorphisme. 8.4.4.7. JfPane Cette classe représente un conteneur. Pour l’utiliser efficacement, une instance de ’Layout’ lui est assignée de façon à gérer de la manière désirée le placement des composants. Les données membres sont les suivantes : la ’JPane’ Swing, une instance de la classe ’JfLayout’ et une de ’LayoutManager’ définissant la méthode de positionnement, ainsi qu’un tableau de JfComponent

Page 49/112 eivd Framework sous Linux Bastien Andrès décrivant les composants contenus par le conteneur. Les méthodes présentes sont ’init()’ et ’assign()’ permettant l’assignation des propriétés et l’initialisation de cet objet. La méthode ’init()’ initialise le conteneur, provoque l’initialisation des composants contenus et les ajoute au ’panel’ Swing’.

8.4.4.8. JfButton Cette classe représente, comme son nom l’indique un bouton. Elle possède les données suivantes : l’instance du bouton Swing, le texte du bouton, ainsi que l’objet et la méthode cible de l’événement d’appui du bouton. Les méthodes présentes sont : ’assign()’, init()’, une méthode permettant de définir la cible de l’événement, deux permettant d’en retourner l’objet et la méthode, la méthode requise par l’interface ’ActionListener’ pour réagir à l’événement et enfin la méthode de traitement par défaut de ce dernier. 8.4.4.9. JfTextField Cette classe représente une zone de texte. Il est possible de lui assigner un texte par défaut, de choisir si elle est éditable par l’utilisateur, ainsi que de réagir à l’événement produit par l’appui de la touche ’Entrée/Return’ du clavier lorsque ce composant est ’activé’. Les données membres de cette classes sont les suivantes. Les propriétés de texte par défaut, l’éditabilité et la longeur par défaut de la zone. L’objet et la méthode cible de l’événement ainsi que l’instance du bouton Swing. Les méthodes présentes sont similaires à celles du bouton. Les méthodes d’initialisation ’assign()’ et ’init()’. Les méthodes de gestion du traite−événement ainsi que la méthode de traitement par défaut.

8.4.4.10. JfMenuBar Cette classe représente la barre de menu. Aucun événement généré par ce composant n’est intercepté. Les données membres sont les suivantes : l’instance de la barre de menu Swing et un tableau contenant les instances des menus déroulants. Les méthodes membres sont ’assign()’ et ’init()’. La méthode ’assign()’ est utilisée pour initialiser le tableau des menus présents. Sur la base de cette information, la méthode ’init()’ provoque l’initialisation des menus et ajoute les instances Swing des menus à la barre de menu Swing. 8.4.4.11. JfMenu Cette classe représente un menu déroulant. Aucun événement de ce composant n’est intercépté. Cette classe est dérivée de la classe JfMenuItem, décrite ci−dessous et permet ainsi qu’une entrée de menu soit un menu et de la sorte créer des sous−menus. Les données membres sont l’instance Swing du menu, le titre du menu et un tableau de ’JfMenuItem’ correspondant aux entrées du menu. Les méthodes membres sont ’assign()’ et ’init()’. Celles−ci ont les comportements habituels. La méthode ’init()’ réalise l’ajout des entrées de menu au menu.

8.4.4.12. JfMenuItem Cette classe représente une entrée de menu. L’événement de séléction de cette entrée de menu est intercepté. Les données membres sont les suivantes : l’instance d’entrée de menu Swing, le titre et la description de l’entrée ainsi que l’objet et la méthode cible de l’événement. La description est présente dans le but de préparer une extension future de l’objet permettant la présence de bulles d’aide relatives à celui−ci.

Page 50/112 eivd Framework sous Linux Bastien Andrès Les méthodes ’init()’ et ’assign()’ sont présentes. Celles−ci sont additionnées des méthodes permettant la gestion de l’événement : ’setActionTarget’, ’getActionTargetObject’, getActionTargetMethod’. De même, les méthodes permettant la réaction à l’événement : ’actionPerformed’ et ’defaultActionTarget’. 8.4.5. Agrégation Le lancement de l’application provoque l’exécution de la méthode ’main()’ de la classe ’JfApplication’. Celle−ci crée une instance de la classe application dérivée dans une variable statique. Toutes les autres instances doivent être référencées pour pouvoir exister. Au niveau fonctionnel, les différentes instances possèdent une référence des instances qu’elles contiennent. Le schéma suivant illustre un exemple des types d’instances référencées par leur utilisateur dans le cas d’une application simple.

JfApplication JfFrame JfMenuBar JfMenu JfMenuItem

JfMenuItem

JfMenu JfMenuItem

JfMenu JfMenuItem

JfMenuItem

JfPane JfLayout

JfButton

JfPane JfLayout

JfButton

JfTextField Néanmoins, les instances doivent être créées assez tôt après le lancement de l’application. La méthode la plus simple est de créer une instance de chaque classe utilisée dans des variables statiques placées dans la classe application dérivée. Ainsi il faudrait ajouter au schéma ci−dessus une référence de chacune des instances depuis la classe application. 8.4.6. Construction Toutes les instances des classes utilisées dans une application doivent être construites. De même, toutes les propriétés de ces instances doivent être initialisées. Lors de ces initialisations, il est très souvent nécessaire de faire référence à d’autres composants. Si l’initialisation des propriétés est effectuée dans le constructeur, il faudra construire les objets suivant un ordre donné afin que l’objet référencé soit déjà construit. Néanmoins, cela ne permet pas de référencement circulaire. L’initialisation des propriétés, tâche dévolue à la méthode ’assign()’, n’est donc pas effectuée à partir du constructeur. Suite à la construction de la totalité des composants, le Framework appelle la méthode ’init()’ de la classe JfFrame. Tous les composants possèdent cette méthode qui procède à l’initialisation proprement dite du composant. Cette méthode effectue séquentiellement les actions suivantes. D’abord, les propriétés du composant sont assignées. Puis il est fait appel récursif à la méthode ’init()’ des éventuels composants ’fils’. Ensuite, le composant graphique est créé et les propriétés lui sont assignées. En dernier lieu, les éventuels composants ’fils’ sont ajoutés au composant graphique. Dans le cas de la fenêtre, celle−ci est encore optionnellement affichée. Ce schéma représente les grandes lignes, quelques différences existent en fonction des spécificités des composants.

Page 51/112 eivd Framework sous Linux Bastien Andrès 8.4.7. Dérivation La méthode privilégiée pour la création de ses propres composants consiste à créer une classe dérivée du composant désiré. La définition des propriétés du composant se fait principalement par la dérivation de la méthode ’assign()’. Les autres méthodes ne sont en principe pas dérivées. Des objets et méthodes supplémentaires sont à ajouter selon les fonctionnalités désirées. La classe application de base (JfApplication) doit être dérivée. Il est commode d’y intégrer des variables correspondant à tous les composants utilisés et de les créer immédiatement. La méthode ’assign()’ de cette classe doit aussi être dérivée. Afin de définir quelle classe application doit être créée au lancement de l’application, il est nécessaire de modifier la méthode ’getApplication()’ afin qu’elle retourne une nouvelle instance de la classe application dérivée. Pour les petits projets, il est possible de mettre toutes les classes dans le même fichier. Ceci est possible si les classes ne sont pas décrites comme ’public’. 8.5. Implémentation du Framework en GTK−− 8.5.1. Introduction Cette partie décrit l’implémentation du Framework en Gtk−−. Elle présente d’abord la liste des classes. Puis, les relations d’héritage seront présentées. Enfin, le contenu des classes sera décrit. 8.5.2. Classes Le Framework en Gtk−− se compose de 14 fichiers. Ceux−ci seront décrits ci−dessous. La liste est la suivante: GfMain Fichier contenant la fonction ’main()’ GfUtil Fichier utilitaire contenant les inclusions des fichiers Gtk−− nécessaires GfApplication Classe application de base GfObject Classe d’abstraction de Gtk::Object, classe de base des composants Gtk−− GfWidget Classe d’abstraction de Gtk::Widget, correspondant à un composant graphique GfContainer Classe d’abstraction de Gtk::Container, correspondant à un conteneur générique GfBin Classe d’abstraction de Gtk::Bin, correspond à un conteneur à contenu unique GfButton Classe d’abstraction du bouton Gtk::Button GfWindow Classe d’abstraction de la fenêtre Gtk::Window GfEventButton Classe dérivée de Gtk::Button, permet de récupérer les événements GfEventWindow Classe dérivée de Gtk::Window, permet de récupérer les événements GfAction Classe de base des classes de description des actions (traite−événements) GfButtonAction Classe de description des traite−événements liés au bouton GfWindowAction Classe de description des traite−événements liés à la fenêtre 8.5.3. Héritage Pour ce qui est des composants graphiques, le schéma d’héritage est copié sur celui de Gtk−−. Cela afin de pouvoir étendre le Framework aux autres composants de Gtk−− sans devoir toucher à la partie déjà réalisée. Deux composants sont dérivés des composants Gtk−− afin de pouvoir intercepter les Page 52/112 eivd Framework sous Linux Bastien Andrès événements. Les deux classes de description des traite−événements (GfButtonAction et GfWindowAction) sont dérivées d’une classe de base (GfAction). Cela n’est pas utile a l’état actuel mais est prévu en vue d’une extension. Le schéma présent dans la documentation de développement illustre les interactions au niveau des composants générant des événements. 8.5.4. Schéma d’initialisation des composants L’initialisation des composant faisant partie de l’interface graphique suit un schéma bien particulier. En effet, celle−ci est réalisée récursivement. Elle est initiée par la fonction ’main’ et chaque composant provoque l’initialisation des composants qu’il contient graphiquement. De plus, chaque composant initialise récursivement ses super−classes. Chaque class correspondant à un composant possède quatre méthodes : deux méthodes ’init’, une méthode ’init_common’ et une méthode ’assign’. La fonction ’main’ fait appel à la méthode ’init()’ de l’objet application. A son tour, celui−ci fait appel à celle de la fenêtre principale de l’application. Cette dernière appelle celle du conteneur qu’elle contient. Et ainsi de suite. La méthode ’init’ d’un bouton, par exemple, fait appel à la méthode ’init’ de la classe GfBin de laquelle elle dérive. Par contre, cet appel est fait à la méthode ’init’ surchargée : ’init(Gtk::Bin& bin)’. Elle fourni ainsi en argument le composant Gtk−− concerné et indique par cette utilisation que l’objet créé est un objet dérivé. La méthode ’init()’permet donc l’initialisation d’un objet du type considéré. Dans le cas de la plupart des classes cela est impossible car elles sont virtuelles et ne permettent pas l’accès au constructeur. Néanmoins, certains composants peuvent être créés et posséder des composants dérivés. Dans ce cas, la présence de deux méthodes d’initialisation est valable. La méthode ’init(Gtk::Object& object)’, par exemple, permet l’initialisation de ce composant par une de ses classes dérivées. Ainsi, un composant dérivé appellera depuis sa méthode ’init’ cette méthode de sa super−classe de façon à initialiser sa structure de donnée. En argument est passé l’instance de la classe Gtk−− correspondante de façon à pouvoir assigner la variable y correspondant. La présence de cette variable à tous les niveaux de dérivation permet d’y accéder aisément sans nécessiter de casting et ne constitue pas une charge énorme. La troisième méthode, ’init_common()’, regroupe les actions à exécuter dans les deux cas d’initialisation. Le schéma d’exécution des méthodes ’init’ de tous les composants est standard. Elle commence par une éventuelle assignation par défaut des propriétés dont la présence est vitale. Puis il est fait appel à la méthode ’assign’ dont la tâche est d’assigner les valeurs des propriétés du composant. Cela effectué, le ou les composants imbriqués sont connus. Il est alors possible d’appeler leur méthode ’init’ afin de provoquer récursivement leur initialisation. Ensuite, le composant Gtk−− correspondant à la classe est créé, ses propriétés assignées et les composants imbriqués lui sont ajoutés. Puis, la méthode init surchargée de la super−classe est appelée afin d’initialiser sa structure de donnée. Les méthodes ’init’ effectuent des tâches spécifique de création propre à chaque classe et nécessaire au Framework. Elles ne sont donc généralement pas dérivées par l’uitlisateur. La méthode ’assign’, présent elle aussi dans la plupart des composants est donc dévolue à l’assignation des propriétés de l’objet. Elle doit être dérivée pour spécifier les propriétés désirées de l’objet. 8.5.5. Description Cette partie décrit le contenu de chaque classe.

Page 53/112 eivd Framework sous Linux Bastien Andrès 8.5.5.1. GfMain Ce fichier comprend uniquement la fonction ’main’. Celle−ci consiste en la suite d’action suivante. Tout d’abord, une instance de la classe Gtk::Main est créée. Cette étape initialise les structures de base de Gtk−− et est nécessaire pour toute application utilisant cette librairie. Ensuite la méthode initInstance de l’instance statique de la classe GfApplication (theApp) est appelée, permettant à l’application utilisateur de traiter les arguments de la ligne de commande et de procéder à des initialisations globales à l’application. Il est ensuite fait appel à la méthode init() de cette même instance. Celle−ci provoque récursivement l’initialisation de tous les composants graphiques présent dans l’interface. En dernier lieu, il est fait appel à la méthode ’run’ de l’instance de Gtk::Main, afin de lancer la boucle de traitement des événements. Le reste de l’execution à lieu sous le contrôle de cette boucle. 8.5.5.2. GfUtil Ce fichier contient les inclusions des fichiers de Gtk−− nécessaires à la compilation. C’est aussi l’endroit ou pourraient se trouver des fonctions utilitaires relatives à l’application ou au Framework. Dans l’état actuel, aucune n’est présente. 8.5.5.3. GfApplication Il s’agit de la classe application de base. Cette classe doit être dérivée afin de générer une application spécifique. Cette classe contient une seule donnée membre correspondant à l’instance de la fenêtre de l’application. Trois méthodes sont présentes. Celles−ci sont initInstance, assign et init. La première est appelée par la fonction main et sert à l’initialisation de l’application. Elle doit être redéfinie pour spécifier les tâches à effectuer. La méthode init est elle aussi appelée par la fonction main. Son but est de permettre l’initialisation récursive de tous les composants. La méthode assign permet de définir l’instance de la fenêtre principale. 8.5.5.4. GfObject Cette classe permet l’abstraction de la classe Gtk::Object de Gtk−−. Comme elle, elle n’est utilisée qu’afin de définir un objet générique. Il s’agit de la classe de base des objets Gtk−−. Dans l’état actuel, cette classe n’est pas utile. En effet, il aurait été possible de commencer la hiérarchie à Gtk::Widget. Néanmoins, dans l’optique d’une extension du Framework à d’autres objet Gtk−−, il était intéressant de la créer. Cette classe contient une seule donnée membre. Il s’agit d’un pointeur sur l’instance Gtk::Object dont il est fait abstraction. Les méthodes membres sont les suivante : ’init()’, init(Gtk::Object& object)’ et init_common(). Ces trois méthodes ont pour but l’initialisation de l’instance selon le schéma décrit précédemment. 8.5.5.5. GfWidget Cette classe correspond à la classe Gtk::Widget, parente de tous les composant graphiques de Gtk−−. Elle est importante, car c’est une instance de celle−ci qui est utilisée par les conteneurs pour l’ajout d’un composant. Cet ajout utilise le polymorphisme afin de pouvoir ajouter tout type de composant. Les données membres de cette classe sont les suivantes : Un pointeur sur l’instance Gtk::Widget, deux propriétés représentant les valeurs x et y de la taille minimale du composant et deux propriétés représentant celle de la positon préférée de celui−ci. Les méthodes présentes sont celles d’initialisation et d’assignation.

Page 54/112 eivd Framework sous Linux Bastien Andrès 8.5.5.6. GfContainer Cette classe représente la classe Gtk−− de conteneur générique Gtk::Container. C’est durant l’initialisation de cette classe que sont ajoutés les composants contenus. Dans le cas ou le conteneur instancié ne peut posséder qu’un composant, c’est le dernier de la liste qui sera présent si celle−ci en possède plusieurs. Les données membres sont les suivantes : un pointeur sur l’instance de Gtk::Container, une propriété représentant la largeur du bord présent autour des composants contenus, un tableau de ceux−ci et finalement la taille de ce tableau. Les méthodes membres sont celles d’initialisation. Dans la méthode ’init_common()’ se trouve l’ajout des composant au conteneur. Quel que soit le type de conteneur, c’est à cet endroit que la composition sera faite. 8.5.5.7. GfBin Cette classe représente la classe Gtk::Bin. Il s’agit d’un conteneur ne présentant qu’un seul composant. Sa présence est nécessaire car la classe bouton en dépend. La seule donnée membre est le pointeur sur l’instance Gtk::Bin. De même, les seules méthodes sont celles d’initialisation. 8.5.5.8. GfButton Cette classe représente un bouton. Elle correspond à la classe Gtk::Button. Pour pouvoir récupérer les événements liés au bouton, il a fallu créer une classe dérivée, GfEventButton, décrite ci−dessous. C’est cette classe qui est utilisée à la place. La donnée membre de composant est donc un pointeur sur une instance de GfEventButton. Les autre données membres représentent des propriétés. Elles sont le texte du bouton, le type de relief du bouton ainsi qu’un pointeur sur une instance de la classe GfButtonAction définissant les traite−événements. Les méthodes sont celles d’initialisation et d’assignation. Lors de l’initialisation, le GfEventButton est créé. Le constructeur est appelé en passant en paramètre le texte du bouton, ainsi que le pointeur sur l’instance de la classe GfButtonAction.

8.5.5.9. GfWindow Cette classe représente une fenêtre. Elle correspond à la classe Gtk::Window. Comme dans le cas du bouton, une classe d’adaptation est présente afin de permettre de récupérer les événements. Il s’agit de la classe GfEventWindow. La donnée liée au composant est donc un pointeur sur cette classe. De nombreuses propriétés sont présentes :

Le type de fenêtre,

le titre de la fenêtre,

la position par défaut,

la politique de redimmensionnement,

un indicateur d’affichage automatique de la fenêtre à la fin de l’initialisation,

un indicateur permettant de garder la fenêtre en cas de requête de fermeture,

et le pointeur sur une instance de la classe GfWindowAction, décrivant les traite−événements. Les méthodes sont celles d’initialisation. A la fin de l’initialisation, si cela est demandé par les propriétés, la fenêtre sera affichée. En effet, a ce moment, tous les composants auront été initialisés. Une fonction statique est présente dans le même fichier. Celle−ci est connectée à l’événement de destruction de la fenêtre. Une seule action est générée. Il s’agit d’un appel à la librairie demandant la terminaison de l’application. Page 55/112 eivd Framework sous Linux Bastien Andrès 8.5.5.10. GfEventButton Cette classe est dérivée de Gtk::Button. Elle fournit les méthodes d’interception des événements. Lorsqu’un de ceux ci survient, il est fait appel à l’objet GfButtonAction lié pour traiter l’événement. Afin de sécuriser le traitement des événement, il est fait un contrôle de validité à chaque nouvelle assignation d’une instance de la classe d’actions. Dans le cas ou un pointeur ’NULL’ est fourni, une instance de la classe par défaut est créée. Une variable booléenne garde cette information en vue de permettre la destruction de celle−ci. Les deux données membres sont donc un pointeur sur une instance de la classe GfButtonAction et une variable booléenne d’indication de gestion de cette instance. Les méthodes sont de deux types. Une méthode permet l’assignation des actions. Les autres correspondent à la redéfinition des méthodes de traite−événement fournies par la classe Gtk::Button. Celles−ci font appel à la classe de définition des actions pour le traitement de l’événement. 8.5.5.11. GfEventWindow Cette classe a la fonction identique à la classe GfEventButton décrite ci−dessus. Celle−ci utilise les mêmes mécanismes. Une différence de fonctionnement est néanmoins présente. En effet, les événements liés à la fenêtre sont traités par des méthodes possédant un paramètre et retournant un entier. Afin de permettre l’utilisation de ceux−ci par les traite−événements, ils sont passés aux méthodes de la classe GfWindowAction. Il a donc été rajouté aux mécanismes présents dans la classe GfEventButton la transmission des paramètres et valeurs de retour. 8.5.5.12. Gf Action Cette classe correspond à la classe de base de celles définissant le traitement à assigner aux événements. Dans l’état actuel, cette classe n’a pas d’utilité. Celle−ci pourrait survenir lors d’une extension du Framework. 8.5.5.13. GfButtonAction Cette classe représente la définition des traitements à effectuer lorsque survient un événement lié à un bouton. Cette classe doit être dérivée afin de spécifier les traitements. Un pointeur sur une instance d’une classe dérivée de celle−ci est passée à la classe GfEventButton. Les données membres consistent en une série de variables booléenes. Celles−ci indiquent, pour chaque type d’événement, si la fonction de réaction à l’événement définie par la super−classe de GfEventButton (Gtk::Button) doit être appelée. Les méthodes membres constituent deux séries de méthodes de réaction à chaque type d’événement. Une des séries correspond aux actions à réaliser avant l’éventuel appel à la super−classe. L’autre après. Si l’appel au traitement par la super−classe n’est pas demandé, l’une ou l’autre des méthodes pourra être indifféremment utilisée, voire les deux. 8.5.5.14. GfWindowAction Cette classe représente la définition des traitements des événements liés à la fenêtre. Elle suit exactement le schéma de la classe GfButtonAction décrite ci−dessus. 8.5.5.15. GfApp Ce fichier ne fait pas à proprement parlé partie du Framework. Il s’agit d’un exemple simple d’une application utilisateur. Il regroupe les classes dérivées spécialisant l’application, la fenêtre, un bouton et ses traite événement. Les instances statiques de ces objets sont aussi présentes.

Page 56/112 eivd Framework sous Linux Bastien Andrès 8.6. Listing du code réalisé 8.6.1. Framework en Java

8.6.1.1. JfApplication

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfApplication.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Cette classe définit l’application par défaut // Il convient de la dériver afin de générer une // application réalisant les fonctionnalités dérivées // La methode main permet le lancement de l’application // Celle−ci appele la fonction getApplication de // la classe JfAppDef afin construire la bonne classe // dérivée //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import JfAppDef; import JfFrame; import javax.swing.*; public class JfApplication { // constants

// variables

// la frame (fenêtre) de l’application

public JfFrame frame = null;

// ctor

public JfApplication() { }

// méthodes

// méthode définissant les propriétés // de l’application ainsi que le type // de frame

public void assign() { frame = new JfFrame(); }

// méthode d’initialisation de l’application // recoit les arguments de la ligne de commande // afin de les traiter. A dériver afin de définir // un traitement particulier

public boolean initInstance(String[] args) { for (int i = 0 ; i < args.length ; i++) { Page 57/112 eivd Framework sous Linux Bastien Andrès System.out.println("JfApplication : Argument #"+i+" : "+args[i]); } return true; }

// méthode d’initialisation de l’application // généralement non dérivée.

public boolean init() { assign();

frame.init();

return true; }

// main() // méthode de démarage de l’application // construit une instance de la classe application // dérivée puis procéde aux initialisations. // La suite des traitements est alors réalisé // par le Thread d’interface graphique Swing

public static void main(String[] args) { // appelle get Application qui retourne une // instance de la classe dérivée

JfApplication theApp = JfAppDef.getApplication();

// initialisation de l’application permettant // le traitement des arguments de la ligne de commande

theApp.initInstance(args);

// initialisation de la frame // (et indirectement de tous les composants)

theApp.init(); } }

Page 58/112 eivd Framework sous Linux Bastien Andrès 8.6.1.2. JfAppDef

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfAppDef.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // cette classe contient les définitions globales // de l’application. En particulier, elle fournit // une méthode retournant une instance de la classe // JfApplication dérivée afin de générer l’application // désirée. //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import JfLayout; import java.util.*; import java.awt.*; import java.awt.event. *; import javax.swing.*; public class JfAppDef { // constants

// variables

// methodes

// methode retournant une instance de la classe application // afin de lancer cette dernière. modifier le constructeur // de facon a retourner une instance de la classe désirée

public static JfApplication getApplication() { // remplacer l’instance retournée par une instance // de la classe dérivée de JfApplication désirée

return new MyApp(); }

// methode de traitement des exceptions // permet de definir un traitement par defaut // peut être appelée si le traitement par défaut // est désiré

public static boolean notifyException(Exception e) { System.out.println("−−− Exception Occured −−−−−−−−−−−−−−−−−−−−−−−−−"); e.printStackTrace(); System.out.println("−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−"); return true; } }

Page 59/112 eivd Framework sous Linux Bastien Andrès 8.6.1.3. JfTarget

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfTarget.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Classe utilitaire permettant d’obtenir les éléments // nécessaire à l’appel d’un taite−évènement. // A partir de l’objet considéré et du nom de la méthode // la méthode setTarget crée les éléments permettant // l’appel de la méthode considérée. // // Les méthodes valables sont du type : // public void maMethode(EventObject event) // // Cette classe est à utiliser comme suit : // − construction // − setTarget(objet,nom_de_la_methode) // − getMethod() // − getObject() // //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import java.util.*; import java.awt.*; import java.lang.reflect.*; public class JfTarget { // constants

// variables

// méthode cible protected Method mtdTarget = null;

// objet cible protected Object objTarget = null;

// ctor

public JfTarget() { }

// methods

// méthode permettant de générer une instance de la // class Method définissant la méthode cible du traite // évènement

public void setTarget(Object objParam, String strMethod) { try { Class clsTemp = objParam.getClass(); Class clsArg = Class.forName("java.util.EventObject"); Class[] clsaTemp = {clsArg}; mtdTarget = clsTemp.getMethod(strMethod, clsaTemp); }

Page 60/112 eivd Framework sous Linux Bastien Andrès catch (Exception e) { JfAppDef.notifyException(e); } objTarget = objParam; }

// méthode retournant l’instance cible du trait−évènement

public Method getMethod() { return mtdTarget; }

// méthode retournant l’objet cible du traite−évènement

public Object getObject() { return objTarget; } // méthode statique retournant la méthode décrite par // l’objet la définissant et son nom

public static Method getMethod(Object objParam, String strMethod) { Method mtdTemp = null; try { Class clsTemp = objParam.getClass(); Class clsArg = Class.forName("java.util.EventObject"); Class[] clsaTemp = {clsArg}; mtdTemp = clsTemp.getMethod(strMethod, clsaTemp); } catch (Exception e) { JfAppDef.notifyException(e); } return mtdTemp; } }

Page 61/112 eivd Framework sous Linux Bastien Andrès 8.6.1.4. JfLayout //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfLayout.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Classe représentant le layout d’un container (pane) // Permet la création d’un tel layout et son assignation // à un container // Définit les principaux layouts de Swing // Permet de définir certaines des plus importantes // propriétés de ces layouts //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import java.awt.*; import javax.swing.*; public class JfLayout { // constantes

// type du layout public static final String BORDER = "Border"; public static final String BOX = "Box"; public static final String FLOW = "Flow"; public static final String GRID_BAG = "Grid_Bag"; public static final String GRID = "Grid"; public static final String CARD = "Card"; public static final String NO_LAYOUT = "No_Layout";

// propriétés (constraints)

public static final String NORTH = BorderLayout.NORTH; public static final String SOUTH = BorderLayout.SOUTH; public static final String WEST = BorderLayout.WEST; public static final String EAST = BorderLayout.EAST; public static final String CENTER = BorderLayout.CENTER;

public static final int ALIGN_CENTER = FlowLayout.CENTER; public static final int ALIGN_LEADING = FlowLayout.LEADING; public static final int ALIGN_LEFT = FlowLayout.LEFT; public static final int ALIGN_RIGHT = FlowLayout.RIGHT; public static final int ALIGN_TRAILING = FlowLayout.TRAILING;

public static final int X_AXIS = BoxLayout.X_AXIS; public static final int Y_AXIS = BoxLayout.Y_AXIS;

// variables−propriétés

// chaine définissant le type du Layout protected String strLayout = null;

// container contenu en cas de BoxLayout protected Container target = null;

// axe de séparation du BoxLayout protected int axis = BoxLayout.X_AXIS;

// alignement en cas de FlowLayout protected int align = FlowLayout.CENTER;

Page 62/112 eivd Framework sous Linux Bastien Andrès

// espace entre composants protected int hgap = 5; protected int vgap = 5;

// nombre de lignes et colonnes (GridLayout) protected int rows = 1; protected int cols = 1;

// variables de travail

// indicateur de validité protected boolean bValid = false;

// layout manager à ajouter au container protected LayoutManager layout = null;

// ctor

public JfLayout() { }

// définition des propriétés du layout

public void assign() { strLayout = FLOW; target = null; axis = BoxLayout.X_AXIS; align = FlowLayout.CENTER; hgap = 5; vgap = 5; rows = 1; cols = 1; }

// initialisation du layout

public void init() { assign(); setLayout(strLayout); }

// méthodes // retourne le LayoutManager

public LayoutManager getLayout() { if (bValid) return layout; else return new FlowLayout(); }

// retourne le nom du layout

public String getLayoutName() { return strLayout; }

Page 63/112 eivd Framework sous Linux Bastien Andrès // assigne la cible (BoxLayout)

public void setTarget(Container param) { target = param; }

// assigne l’axe (BoxLayout)

public void setAxis(int param) { if (param == BoxLayout.X_AXIS || param == BoxLayout.Y_AXIS) axis = param; }

// assigne l’alignement (FlowLayout)

public void setAlign(int param) { if (param == FlowLayout.CENTER) align = param; if (param == FlowLayout.LEFT) align = param; if (param == FlowLayout.RIGHT) align = param; if (param == FlowLayout.LEADING) align = param; if (param == FlowLayout.TRAILING) align = param; }

// assigne les espacements

public void setGaps(int hParam, int vParam) { hgap = hParam; vgap = vParam; }

// assigne le nombre de lignes et de colonnes

public void setRowsCols(int rParam, int cParam) { rows = rParam; cols = cParam; } // crée une instance de LayoutManager du type désiré // en utilisant les paramètres assignés

public void setLayout(String strParam) { strLayout = strParam; bValid = true; if (strParam.equals(BORDER)) layout = new BorderLayout(hgap, vgap); else if (strParam.equals(BOX)) layout = new BoxLayout(target, axis); else if (strParam.equals(FLOW)) layout = new FlowLayout(align, hgap, vgap); else if (strParam.equals(GRID_BAG)) layout = new GridBagLayout(); else if (strParam.equals(GRID)) layout = new GridLayout(rows, cols, hgap, vgap);

Page 64/112 eivd Framework sous Linux Bastien Andrès else if (strParam.equals(CARD)) layout = new CardLayout(hgap, vgap); else if (strParam.equals(NO_LAYOUT)) layout = null; else { // parameter string isn’t a layout identifier layout = null; strLayout = ""; bValid = false; } } }

Page 65/112 eivd Framework sous Linux Bastien Andrès 8.6.1.5. JfFrame

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfFrame.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Cette classe représente un ’wrapper’ de la classe // Swing JFrame définissant une fenêtre. Elle contient // le mécanisme de redirection des évènements de type // WindowEvent //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import java.lang.reflect.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; public class JfFrame implements WindowListener { // constantes // variables−propriétés

// nom de la fenêtre protected String strTitle = null;

// indicateur d’affichage automatique protected boolean bAutoShow = true;

// barre de menu protected JfMenuBar menuBar = null; // ’pane’ de contenu protected JfPane contentPane = null;

// position de la fenêtre protected int iPosX; protected int iPosY; // taille de la fenêtre protected int iSizeX; protected int iSizeY;

// variables de travail

// la fenêtre protected JFrame frame = null;

// méthodes et objets cible des évènements protected Method mtdActivatedTarget = null; protected Object objActivatedTarget = null; protected Method mtdClosedTarget = null; protected Object objClosedTarget = null; protected Method mtdClosingTarget = null; protected Object objClosingTarget = null; protected Method mtdDeactivatedTarget = null; protected Object objDeactivatedTarget = null; protected Method mtdDeiconifiedTarget = null; protected Object objDeiconifiedTarget = null; protected Method mtdIconifiedTarget = null; protected Object objIconifiedTarget = null;

Page 66/112 eivd Framework sous Linux Bastien Andrès protected Method mtdOpenedTarget = null; protected Object objOpenedTarget = null; // ctor

public JfFrame() { }

// assignation des propriétés (méthode à dériver)

public void assign() { strTitle = "Basic JfFrame";

bAutoShow = true;

menuBar = null; contentPane = new JfPane();

iPosX = 100; iPosY = 100; iSizeX = 100; iSizeY = 100; }

// méthode d’initialisation

public void init() { // setting default handlers

setActivatedTarget (this, "defaultTarget"); setClosedTarget (this, "defaultTarget"); setClosingTarget (this, "defaultClosingTarget"); setDeactivatedTarget(this, "defaultTarget"); setDeiconifiedTarget(this, "defaultTarget"); setIconifiedTarget (this, "defaultTarget"); setOpenedTarget (this, "defaultTarget");

// setting the variables

assign();

// build the frame component

frame = new JFrame(strTitle);

// subscribe as listener

frame.addWindowListener(this);

// if there is a menu > init it and assign it to the frame

if (menuBar != null) { menuBar.init(); frame.setJMenuBar(menuBar.menuBar); }

// init the contents pane and assign it to the frame

contentPane.init(); frame.setContentPane(contentPane.panel);

Page 67/112 eivd Framework sous Linux Bastien Andrès

// realize the graphical interface frame.pack();

frame.setLocation(iPosX,iPosY); frame.setSize(iSizeX,iSizeY);

// make the frame visible if desired

if (bAutoShow) frame.show(); }

// méthodes

// méthode de définition des cibles des évènements

public void setActivatedTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objActivatedTarget = trgTemp.getObject(); mtdActivatedTarget = trgTemp.getMethod(); }

public void setClosedTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objClosedTarget = trgTemp.getObject(); mtdClosedTarget = trgTemp.getMethod(); }

public void setClosingTarget(Object objParam, String strMethod) { mtdClosingTarget = JfTarget.getMethod(objParam, strMethod); objClosingTarget = objParam; }

public void setDeactivatedTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objDeactivatedTarget = trgTemp.getObject(); mtdDeactivatedTarget = trgTemp.getMethod(); }

public void setDeiconifiedTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objDeiconifiedTarget = trgTemp.getObject(); mtdDeiconifiedTarget = trgTemp.getMethod(); }

public void setIconifiedTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objIconifiedTarget = trgTemp.getObject(); mtdIconifiedTarget = trgTemp.getMethod(); }

Page 68/112 eivd Framework sous Linux Bastien Andrès public void setOpenedTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objOpenedTarget = trgTemp.getObject(); mtdOpenedTarget = trgTemp.getMethod(); }

// méthodes de l’interface WindowListener // consistent en la redirection vers les méthodes cibles définies

public void windowActivated(WindowEvent event) { Object[] objaArgs = {event}; try { mtdActivatedTarget.invoke(objActivatedTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

public void windowClosed(WindowEvent event) { Object[] objaArgs = {event}; try { mtdClosedTarget.invoke(objClosedTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

public void windowClosing(WindowEvent event) { Object[] objaArgs = {event}; try { mtdClosingTarget.invoke(objClosingTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

public void windowDeactivated(WindowEvent event) { Object[] objaArgs = {event}; try { mtdDeactivatedTarget.invoke(objDeactivatedTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

public void windowDeiconified(WindowEvent event)

Page 69/112 eivd Framework sous Linux Bastien Andrès { Object[] objaArgs = {event}; try { mtdDeiconifiedTarget.invoke(objDeiconifiedTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

public void windowIconified(WindowEvent event) { Object[] objaArgs = {event}; try { mtdIconifiedTarget.invoke(objIconifiedTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

public void windowOpened(WindowEvent event) { Object[] objaArgs = {event}; try { mtdOpenedTarget.invoke(objOpenedTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

// méthodes cible des évènements par défaut

public void defaultTarget(EventObject event) {}

public void defaultClosingTarget(EventObject event) { System.exit(0); } }

Page 70/112 eivd Framework sous Linux Bastien Andrès 8.6.1.6. JfComponent

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfComponent.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Cette classe représente la classe de base de la // plupart des classes de ’wrapping’ des composants // Swing. Elle fournit les objets nécessaire à // l’utilisation polymorphique des divers composants. //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import java.awt.*; public class JfComponent { // constants // variables

// composant AWT, supercalsse de tous les composants Swing // utilisé pour l’ajout des composants dans les ’panes’

public Component component = null;

// objet contenant les contraintes de placement de // l’objet graphique dans le layout de son parent

public Object constraint = null;

// constructeur par défaut

public JfComponent(){}

// methodes

// methode d’initialisation du composant // celle−ci est appelée par le composant parent // apres la construction de tous les composants // afin de pouvoir effectuer des references croisées

public void init(){} // methode de definition des propriétés du composant // cette methode est à surcharger afin de definir // le composant utilisé

public void assign(){} }

Page 71/112 eivd Framework sous Linux Bastien Andrès 8.6.1.7. JfPane

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfPane.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Classe de ’wrapping’ de la classe swing ’JPanel’ // représentant un container pouvant contenir des // composants //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import javax.swing.*; import java.awt.*; public class JfPane extends JfComponent { // variables−propriétés // le wrapper du layout du panel protected JfLayout layout = null; // composants se trouvant dans le panel protected JfComponent[] childs = null;

// variables de travail

// le panel public JPanel panel = null; // le layout du panel protected LayoutManager layoutManager = null;

// ctor

public JfPane() { }

// méthode d’assignation des propriétés (à dériver)

public void assign() { layout = new JfLayout(); JfComponent[] jfca = {new JfButton()}; childs = jfca; }

// méthode d’initialisation du panel et de ses composants

public void init() { // set properties

assign();

// build layout manager

layout.init();

layoutManager = layout.getLayout();

Page 72/112 eivd Framework sous Linux Bastien Andrès // build pane

panel = new JPanel(); panel.setLayout(layoutManager);

component = panel;

// build and add childs

for (int i = 0 ; i < childs.length ; i++) { childs[i].init();

if (childs[i].constraint != null) panel.add(childs[i].component,childs[i].constraint); else panel.add(childs[i].component); } } }

Page 73/112 eivd Framework sous Linux Bastien Andrès 8.6.1.8. JfButton

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfButton.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // classe de ’wrapping’ de la class JButton // représente un bouton contenant du texte // contient le mécanisme de redirection des évènements // pour l’évènement ’bouton appuyé’ //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import javax.swing.*; import java.lang.reflect.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class JfButton extends JfComponent implements ActionListener { // variables−propriétés // texte du bouton protected String strTitle = null;

// variables de travail

// le bouton public JButton button = null;

// méthode et objet cible de l’évènement protected Method mtdActionTarget = null; protected Object objActionTarget = null;

// ctor

public JfButton() { } // assignation des propriétés du bouton

public void assign() { strTitle = "Default JfButton"; constraint = null; setActionTarget(this, "defaultActionTarget"); }

// méthode d’initialisation du bouton

public void init() { assign(); button = new JButton(strTitle); component = button; button.addActionListener(this); } // méthode assignant la cible de l’évènement

Page 74/112 eivd Framework sous Linux Bastien Andrès

public void setActionTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objActionTarget = trgTemp.getObject(); mtdActionTarget = trgTemp.getMethod(); }

// méthode retournant l’objet cible de l’évènement

public Object getActionTargetObject() { return objActionTarget; }

// méthode retournant la méthode cible de l’évènement

public Method getActionTargetMethod() { return mtdActionTarget; }

// méthode de l’interface EventListener (appui du bouton)

public void actionPerformed(ActionEvent event) { Object[] objaArgs = {event}; try { mtdActionTarget.invoke(objActionTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

// méthode par défaut de réaction à l’appui du bouton

public void defaultActionTarget(EventObject event){;} }

Page 75/112 eivd Framework sous Linux Bastien Andrès 8.6.1.9. JfTextField

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfTextField.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // classe de ’wrapping’ de la classe swing ’JTextField’ // représentant un champ de texte éditable ou non // permet la redirection de l’évènement ’touche return’ //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import javax.swing.*; import java.lang.reflect.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class JfTextField extends JfComponent implements ActionListener { // variables−propriétés

// nom de la zone de texte protected String strTitle = null; // taille de la zone (en caractères) protected int iSize; // indicateur d’éditabilité de la zone protected boolean bEditable = false;

// variables de travail

// la zone de texte public JTextField textField = null;

// méthode et objet cible de l’évènement protected Method mtdActionTarget = null; protected Object objActionTarget = null;

// ctor

public JfTextField() { } // méthode d’assignation des propriétés de la zone

public void assign() { strTitle = "Default JfTextField"; iSize = 20; constraint = null; bEditable = true; setActionTarget(this, "defaultActionTarget"); }

// methode d’initialisation de la zone

public void init() { assign(); textField = new JTextField(iSize);

Page 76/112 eivd Framework sous Linux Bastien Andrès textField.setText(strTitle); textField.setEditable(bEditable); component = textField; textField.addActionListener(this); }

// méthode d’assignation de la cible de l’évènement

public void setActionTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objActionTarget = trgTemp.getObject(); mtdActionTarget = trgTemp.getMethod(); }

// méthode retournant l’objet cible

public Object getActionTargetObject() { return objActionTarget; }

// méthode retournant la méthode cible

public Method getActionTargetMethod() { return mtdActionTarget; }

// méthode de l’intwerface ActionListener (appui de return)

public void actionPerformed(ActionEvent event) { Object[] objaArgs = {event}; try { mtdActionTarget.invoke(objActionTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } }

// méthode de traite−évènement par défaut public void defaultActionTarget(EventObject event){;} }

Page 77/112 eivd Framework sous Linux Bastien Andrès 8.6.1.10. JfMenuBar

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfMenuBar.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // classe de ’wrapping’ de la class swing JMenuBar // représentant une barre de menu //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import javax.swing.*; import java.awt.*; public class JfMenuBar { // variable−propriété

// les menus de la barre public JfMenu[] menus = null;

// variable de travail // la barre de menu public JMenuBar menuBar = null;

// ctor

public JfMenuBar() { }

// méthode d’assignation des propriétés

public void assign() { menus = new JfMenu[1]; menus[0] = new JfMenu(); }

// methode d’initialisation de la barre (et des menus)

public void init() { // set properties

assign();

// build the bar

menuBar = new JMenuBar();

// init childs & add childs

for (int i = 0 ; i < menus.length ; i++) { if(menus[i] == null) { menuBar.add(Box.createHorizontalGlue()); } else

Page 78/112 eivd Framework sous Linux Bastien Andrès { menus[i].init(); menuBar.add(menus[i].menu); } } } }

Page 79/112 eivd Framework sous Linux Bastien Andrès 8.6.1.11. JfMenuItem

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfMenuItem.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // classe de ’wrapping’ de la classe swing JMenuItem // représente un artile de menu // contient le mécanisme de redirection de l’évènement // ’article choisi’ //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.lang.reflect.*; public class JfMenuItem implements ActionListener { // variables−propriétés // titre de l’article public String strTitle = null; // description de l’article protected String description = null;

// variables de travail

// l’article public JMenuItem menuItem = null; // méthode et objet cible de l’évènement protected Method mtdActionTarget = null; protected Object objActionTarget = null;

// ctor

public JfMenuItem() { }

// méthode d’assignation des propriétés public void assign() { strTitle = "Default JfMenuItem"; description = "this is a basic JfMenuItem"; setActionTarget(this, "defaultActionTarget"); }

// methode d’initialisation de l’article

public void init() { // set properites

assign();

// build item and assign its properties

Page 80/112 eivd Framework sous Linux Bastien Andrès menuItem = new JMenuItem(strTitle); menuItem.getAccessibleContext().setAccessibleDescription(description); menuItem.addActionListener(this); }

// méthode d’assignation de l’action liée à l’événement de selection de l’article

public void setActionTarget(Object objParam, String strMethod) { JfTarget trgTemp = new JfTarget(); trgTemp.setTarget(objParam, strMethod); objActionTarget = trgTemp.getObject(); mtdActionTarget = trgTemp.getMethod(); }

// méthode retournant l’objet cible

public Object getActionTargetObject() { return objActionTarget; }

// méthode retournant la méthode cible

public Method getActionTargetMethod() { return mtdActionTarget; }

// méthode de l’interface ActionListener // de réaction à l’évènement ’article sélectionné’

public void actionPerformed(ActionEvent event) { Object[] objaArgs = {event}; try { mtdActionTarget.invoke(objActionTarget, objaArgs); } catch (Exception e) { JfAppDef.notifyException(e); } } // méthode de réatcion par défaut à la sélection de l’article

protected void defaultActionTarget(EventObject event){;} }

Page 81/112 eivd Framework sous Linux Bastien Andrès 8.6.1.12. JfMenu

//−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Fichier : JfMenu.java //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Auteurs : B.Andres // Date : 30.11.2000 // Classe : ET6 //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // classe de ’wrapping’ de la classe swing JMenu // représente un menu ou un sous menu //−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− import java.awt.*; import javax.swing.*; public class JfMenu extends JfMenuItem { // variables propriétés

// nom (texte) du menu public String strTitle = null; // éléments du menu protected JfMenuItem[] menuItems = null; // variables de travail

// le menu public JMenu menu = null;

// ctor

public JfMenu() { }

// méthode d’assignation des propriétés du menu

public void assign() { strTitle = "Default JfMenu"; description = "this is a basic menu"; menuItems = new JfMenuItem[3]; menuItems[0] = new JfMenuItem(); menuItems[1] = null; menuItems[2] = new JfMenuItem(); }

// methode d’initalisation du menu (et de ses éléments)

public void init() { // set properties

assign();

// build item

menu = new JMenu(strTitle); menuItem = menu; menu.getAccessibleContext().setAccessibleDescription(description); // init childs & add childs

Page 82/112 eivd Framework sous Linux Bastien Andrès

for (int i = 0 ; i < menuItems.length ; i++) { if (menuItems[i] == null) { menu.addSeparator(); } else { menuItems[i].init(); menu.add(menuItems[i].menuItem); } } } }

Page 83/112 eivd Framework sous Linux Bastien Andrès 8.6.1.13. MyApp (exemple d’application)

// // Jframework test // import JfAppDef; import JfLayout; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; class MyApp extends JfApplication { public static MyMenuItemQuit theQuitItem = new MyMenuItemQuit(); public static MyMenuItemInc theIncItem = new MyMenuItemInc(); public static MyMenuItemDec theDecItem = new MyMenuItemDec(); public static MySubMenu theSubMenu = new MySubMenu(); public static MyMenu theMenu = new MyMenu(); public static MyMenuBar theBar = new MyMenuBar();

public static MyTextField theTextField = new MyTextField(); public static MyTextField2 theTextField2= new MyTextField2(); public static MyClearButton theClrButton = new MyClearButton(); public static MyButton theButton = new MyButton(); public static MyLayout2 theLayout2 = new MyLayout2(); public static MyCenterPane theCenterPane= new MyCenterPane(); public static MyLayout theLayout = new MyLayout(); public static MyPane thePane = new MyPane(); public static JfFrame theFrame = new MyFrame();

public void assign() { frame = theFrame; } } class MyFrame extends JfFrame { public void assign() { strTitle = "MyApp : MyFrame"; bAutoShow = true; iPosX = 100; iPosY = 100; iSizeX = 320; iSizeY = 200;

menuBar = MyApp.theBar; contentPane = MyApp.thePane; } }

// contents stuff class MyPane extends JfPane { protected JfComponent[] myChilds = {MyApp.theClrButton, MyApp.theButton, MyApp.theCenterPane};

public void assign() {

Page 84/112 eivd Framework sous Linux Bastien Andrès layout = MyApp.theLayout; childs = myChilds; } } class MyCenterPane extends JfPane { protected JfComponent[] myChilds = {MyApp.theTextField, MyApp.theTextField2};

public void assign() { layout = MyApp.theLayout2; constraint = BorderLayout.CENTER; childs = myChilds; } } class MyLayout extends JfLayout { public void assign() { strLayout = BORDER; target = null; axis = BoxLayout.X_AXIS; align = FlowLayout.CENTER; hgap = 5; vgap = 5; rows = 1; cols = 1; } } class MyLayout2 extends JfLayout { public void assign() { strLayout = FLOW; target = null; axis = BoxLayout.Y_AXIS; align = FlowLayout.RIGHT; hgap = 5; vgap = 5; rows = 2; cols = 1; } } class MyButton extends JfButton { public int iCounter = 0;

public void assign() { strTitle = "MyButton : 0"; constraint = BorderLayout.NORTH; setActionTarget(this, "increment"); }

public void increment(EventObject event) { iCounter++; button.setText("MyButton : "+iCounter); }

Page 85/112 eivd Framework sous Linux Bastien Andrès

public void decrement(EventObject event) { iCounter−−; button.setText("MyButton : "+iCounter); }

public void clear(EventObject event) { iCounter = 0; button.setText("MyButton : "+iCounter); setActionTarget(this, "decrement"); MyApp.theClrButton.setActionTarget(this,"clear2"); MyApp.theTextField.clear(new EventObject(this)); }

public void clear2(EventObject event) { iCounter = 0; button.setText("MyButton : "+iCounter); setActionTarget(this, "increment"); MyApp.theClrButton.setActionTarget(this,"clear"); MyApp.theTextField.clear(new EventObject(this)); } } class MyClearButton extends JfButton { public void assign() { strTitle = "Clear"; constraint = BorderLayout.SOUTH; setActionTarget(MyApp.theButton, "clear"); } } class MyTextField extends JfTextField { public void assign() { strTitle = "enter Text"; iSize = 20; bEditable = true; // constraint = BorderLayout.CENTER; setActionTarget(this, "textAction"); } public void textAction(EventObject event) { MyApp.theTextField2.textField.setText (MyApp.theTextField2.textField.getText()+"\n"+textField.getText()); textField.selectAll(); }

public void clear(EventObject event) { textField.setText(""); } } class MyTextField2 extends JfTextField { public void assign() {

Page 86/112 eivd Framework sous Linux Bastien Andrès strTitle = ""; iSize = 20; bEditable = false; // constraint = BorderLayout.CENTER; setActionTarget(this, "textAction"); }

public void textAction(EventObject event) { MyApp.theTextField.textField.setText(textField.getText()); }

public void clear(EventObject event) { textField.setText(""); } }

// menu stuff class MyMenuBar extends JfMenuBar { public void assign() { menus = new JfMenu[1]; menus[0] = MyApp.theMenu; } } class MyMenu extends JfMenu { public void assign() { strTitle = "MyMenu"; menuItems = new JfMenuItem[3]; menuItems[0] = MyApp.theSubMenu; menuItems[1] = null; menuItems[2] = MyApp.theQuitItem; } } class MySubMenu extends JfMenu { public void assign() { strTitle = "ButtonMenu"; menuItems = new JfMenuItem[2]; menuItems[0] = MyApp.theIncItem; menuItems[1] = MyApp.theDecItem; } } class MyMenuItemQuit extends JfMenuItem { public void assign() { strTitle = "Quit"; description = "this is a basic JfMenuItem"; setActionTarget(this, "close"); }

public void close(EventObject event) { System.exit(0);

Page 87/112 eivd Framework sous Linux Bastien Andrès } } class MyMenuItemInc extends JfMenuItem { public void assign() { strTitle = "Increment Button"; description = "this is a basic JfMenuItem"; setActionTarget(MyApp.theButton, "increment"); } } class MyMenuItemDec extends JfMenuItem { public void assign() { strTitle = "Decrement Button"; description = "this is a basic JfMenuItem"; setActionTarget(MyApp.theButton, "decrement"); } }

Page 88/112 eivd Framework sous Linux Bastien Andrès 8.6.1.14. Makefile du Framework en Java

# java makefile

JAVAC = jikes JFLAGS = +P −depend −deprecation CLSDIR = classes all: $(JAVAC) $(JFLAGS) −d $(CLSDIR) *.java

$(CLSDIR)/%.class: %.java $(JAVAC) $(JFLAGS) −d $(CLSDIR) $< clean: rm $(CLSDIR)/*.class list: ls −alF $(CLSDIR)/*.class

Page 89/112 eivd Framework sous Linux Bastien Andrès 8.6.2. Framework en Gtk−−

8.6.2.1. GfMain

// Fichier : GfMain.cc

#ifndef _GFMAIN_CC_ #define _GFMAIN_CC_

#include "GfUtil.cc" #include "GfApp.cc" #include "GfApplication.cc"

// extern GfApplication theApp; int main (int argc, char *argv[]) { // initialisation de la librairie

Gtk::Main kit(argc, argv);

// initialisation de l’application theApp.initInstance(argc, argv);

// initialisation graphique

theApp.init();

// passation du controle à la librairie

kit.run();

// retour de l’application

return 0; }

#endif

8.6.2.2. GfUtil

// Fichier : GfUtil.cc

#ifndef _GFUTIL_CC_ #define _GFUTIL_CC_

#include

#include #include #include #include #include #include #include

#endif

Page 90/112 eivd Framework sous Linux Bastien Andrès 8.6.2.3. GfApplication

// Fichier : GfApplication.cc

#ifndef _GFAPPLICATION_CC_ #define _GFAPPLICATION_CC_

#include "GfUtil.cc" #include "GfWindow.cc" class GfApplication { public:

GfWindow* theWindow;

public:

GfApplication(); virtual void initInstance(int argc, char *argv[]); void init(); virtual void assign(); };

GfApplication::GfApplication() {} void GfApplication::initInstance(int argc, char *argv[]) {} void GfApplication::init() { assign(); theWindow−>init(); } void GfApplication::assign() { theWindow = new GfWindow(); }

#endif

Page 91/112 eivd Framework sous Linux Bastien Andrès 8.6.2.4. GfObject

// Fichier : GfObject

#ifndef _GFOBJECT_CC_ #define _GFOBJECT_CC_

#include "GfUtil.cc" class GfObject { protected:

Gtk::Object* gtkobject;

public:

GfObject();

virtual void init (); virtual void init (Gtk::Object& object);

virtual void assign (){;};

private:

void init_common (); };

GfObject::GfObject(){} void GfObject::init() { assign(); // gtkobject = manage(new Object()); init_common(); } void GfObject::init(Gtk::Object& object) { gtkobject = &object; init_common(); } void GfObject::init_common(){;} #endif

Page 92/112 eivd Framework sous Linux Bastien Andrès 8.6.2.5. GfWidget

// Fichier : GfWidget.cc

#ifndef _GFWIDGET_CC_ #define _GFWIDGET_CC_

#include "GfUtil.cc" #include "GfObject.cc" class GfWidget : public GfObject { public:

Gtk::Widget* gtkwidget;

gint min_size_x; gint min_size_y; gint uposition_x; gint uposition_y; GfWidget();

virtual void init(); virtual void init(Gtk::Widget& widget);

virtual void assign(){};

private:

void init_common(); };

GfWidget::GfWidget() { min_size_x = 160; min_size_y = 100; uposition_x = 20; uposition_y = 20; } void GfWidget::init() { assign(); init_common(); } void GfWidget::init(Gtk::Widget& widget) { gtkwidget = &widget; init_common(); } void GfWidget::init_common() { GfObject::init(*gtkwidget);

gtkwidget−>set_usize(min_size_x, min_size_y); gtkwidget−>set_uposition(uposition_x, uposition_y); }

#endif

Page 93/112 eivd Framework sous Linux Bastien Andrès 8.6.2.6. GfContainer

// Fichier : GfContainer.cc

#ifndef _GFCONTAINER_CC_ #define _GFCONTAINER_CC_

#include "GfUtil.cc" #include "GfWidget.cc" class GfContainer : public GfWidget { protected:

Gtk::Container* gtkcontainer;

public:

GtkResizeMode resize_mode; guint border_width; guint child_count; GfWidget** childs;

public: GfContainer();

virtual void init(); virtual void init(Gtk::Container& container);

virtual void assign(){};

private:

void init_common(); };

GfContainer::GfContainer() { resize_mode = GTK_RESIZE_IMMEDIATE; border_width = 0; child_count = 0; childs = NULL; } void GfContainer::init() { assign(); init_common(); } void GfContainer::init(Gtk::Container& container) { gtkcontainer = &container; init_common(); } void GfContainer::init_common() { GfWidget** ppWidget; GfWidget* pWidget; Gtk::Widget* pGtkWidget; GfWidget::init(*gtkcontainer);

Page 94/112 eivd Framework sous Linux Bastien Andrès

gtkcontainer−>set_resize_mode(resize_mode); gtkcontainer−>set_border_width(border_width);

if (child_count != 0 && childs != NULL) { ppWidget = childs; for (guint i = 0 ; i < child_count ; i++) { pWidget = *ppWidget; if (pWidget != NULL) { pWidget−>init(); pGtkWidget = pWidget−>gtkwidget; gtkcontainer−>add(*pGtkWidget); } ppWidget++; } } } #endif

Page 95/112 eivd Framework sous Linux Bastien Andrès 8.6.2.7. GfBin

// Fichier : GfBin.cc

#ifndef _GFBIN_CC_ #define _GFBIN_CC_

#include "GfUtil.cc" #include "GfContainer.cc" class GfBin : public GfContainer { protected:

Gtk::Bin* gtkbin;

public:

GfBin(){};

virtual void init(); virtual void init(Gtk::Bin& bin);

virtual void assign(){}; private:

void init_common(); }; void GfBin::init() { init_common(); } void GfBin::init(Gtk::Bin& bin) { gtkbin = &bin; init_common(); } void GfBin::init_common() { GfContainer::init(*gtkbin); } #endif

Page 96/112 eivd Framework sous Linux Bastien Andrès 8.6.2.8. GfButton

// Fichier : GfButton.cc

#ifndef _GFBUTTON_CC_ #define _GFBUTTON_CC_

#include "GfUtil.cc" #include "GfBin.cc" #include "GfButtonAction.cc" #include "GfEventButton.cc" class GfButton : public GfBin { protected:

GfEventButton* gtkbutton;

public:

Gtk::nstring title; GtkReliefStyle style; GfButtonAction* actions;

GfButton();

virtual void init(); virtual void init(GfEventButton& button); virtual void assign(){};

private:

void init_common(); };

GfButton::GfButton() { title = "GfButton"; style = GTK_RELIEF_NORMAL; actions = NULL;

resize_mode = GTK_RESIZE_IMMEDIATE; border_width = 0; child_count = 0; childs = NULL; min_size_x = 160; min_size_y = 100; uposition_x = 20; uposition_y = 20; } void GfButton::init() { assign(); gtkbutton = manage(new GfEventButton(title, actions)); init_common(); } void GfButton::init(GfEventButton& button) { gtkbutton = &button; init_common();

Page 97/112 eivd Framework sous Linux Bastien Andrès } void GfButton::init_common() { GfBin::init(*gtkbutton); gtkbutton−>set_relief (style); }

#endif

Page 98/112 eivd Framework sous Linux Bastien Andrès 8.6.2.9. GfWindow

// Fichier : GfWindow.cc

#ifndef _GFWINDOW_CC_ #define _GFWINDOW_CC_

#include "GfUtil.cc" #include "GfBin.cc" #include "GfEventWindow.cc" #include "GfWindowAction.cc" class GfWindow : public GfBin { protected:

GfEventWindow* gtkwindow;

public:

GtkWindowType type; Gtk::nstring title; GfWindowAction* window_action; GtkWindowPosition position; gint default_size_x; gint default_size_y; gint policy_allow_shrink; gint policy_allow_grow; gint policy_auto_shrink; gboolean auto_show; gboolean remain_on_delete;

public:

GfWindow();

virtual void init(); virtual void init(GfEventWindow& window);

virtual void assign(){};

private: void init_common(); }; // This is a callback that will hand a widget being destroyed. void destroy_handler() { Gtk::Main::quit(); }

GfWindow::GfWindow() { type = GTK_WINDOW_TOPLEVEL; title = "GfWindow"; window_action = NULL;

position = GTK_WIN_POS_CENTER; default_size_x = 320; default_size_y = 200; policy_allow_shrink = true; policy_allow_grow = true;

Page 99/112 eivd Framework sous Linux Bastien Andrès policy_auto_shrink = true; auto_show = true; remain_on_delete = false;

resize_mode = GTK_RESIZE_IMMEDIATE; border_width = 0; child_count = 0; childs = NULL;

min_size_x = 160; min_size_y = 100; uposition_x = 20; uposition_y = 20; } void GfWindow::init() { assign(); gtkwindow = manage(new GfEventWindow(type, remain_on_delete, window_action)); init_common(); } void GfWindow::init(GfEventWindow& window) { gtkwindow = &window; init_common(); } void GfWindow::init_common() { GfBin::init(*gtkwindow); gtkwindow−>destroy.connect(slot(destroy_handler)); gtkwindow−>set_title (title); gtkwindow−>set_default_size (default_size_x, default_size_y); gtkwindow−>set_position (position); gtkwindow−>set_policy (policy_allow_shrink, policy_allow_grow, policy_auto_shrink);

if (auto_show) gtkwindow−>show_all(); }

#endif

Page 100/112 eivd Framework sous Linux Bastien Andrès 8.6.2.10. GfEventButton

// GfEventButton

#ifndef _GFEVENTBUTTON_CC_ #define _GFEVENTBUTTON_CC_

#include "GfUtil.cc" #include "GfButtonAction.cc" using std::cout; using SigC::slot; class GfEventButton : public Gtk::Button { private:

gboolean own_ptr; GfButtonAction* button_action; public:

GfEventButton(Gtk::nstring& title, GfButtonAction* acts) : Gtk::Button(title) { if (acts) { own_ptr = FALSE; button_action = acts; } else { own_ptr = TRUE; button_action = new GfButtonAction(); } };

~GfEventButton() { if(own_ptr) delete button_action; }; void set_actions(GfButtonAction *acts) { if (acts) { if(own_ptr) delete button_action; own_ptr = FALSE; button_action = acts; } }

protected:

virtual void clicked_impl () { button_action−>pre_clicked_action(); if(button_action−>clicked_parent)Gtk::Button::clicked_impl (); button_action−>post_clicked_action();

Page 101/112 eivd Framework sous Linux Bastien Andrès };

virtual void enter_impl () { button_action−>pre_enter_action(); if(button_action−>enter_parent) Gtk::Button::enter_impl (); button_action−>post_enter_action(); };

virtual void leave_impl () { button_action−>pre_leave_action(); if(button_action−>leave_parent) Gtk::Button::leave_impl (); button_action−>post_leave_action(); };

virtual void pressed_impl () { button_action−>pre_pressed_action(); if(button_action−>pressed_parent) Gtk::Button::pressed_impl (); button_action−>post_pressed_action(); };

virtual void released_impl () { button_action−>pre_released_action(); if(button_action−>released_parent) Gtk::Button::released_impl (); button_action−>post_released_action(); }; };

#endif

Page 102/112 eivd Framework sous Linux Bastien Andrès 8.6.2.11. GfEventWindow

// GfEventWindow

#ifndef _GFEVENTWINDOW_CC_ #define _GFEVENTWINDOW_CC_

#include "GfUtil.cc" #include "GfWindowAction.cc" using std::cout; using SigC::slot; class GfEventWindow : public Gtk::Window { private:

gboolean own_ptr; GfWindowAction* window_action; public:

gboolean remain_on_delete; GtkWindowType window_type;

GfEventWindow(GtkWindowType type, gboolean rod, GfWindowAction* acts) : Gtk::Window(type), window_type(type), remain_on_delete(rod) { if (acts) { own_ptr = FALSE; window_action = acts; } else { own_ptr = TRUE; window_action = new GfWindowAction(); } };

~GfEventWindow() { if(own_ptr) delete window_action; }; void set_actions(GfWindowAction *acts) { if (acts) { if(own_ptr) delete window_action; own_ptr = FALSE; window_action = acts; } }

protected:

virtual gint delete_event_impl (GdkEventAny *event) { gint ret_val = 0; ret_val = window_action−>pre_delete_action(event, remain_on_delete);

Page 103/112 eivd Framework sous Linux Bastien Andrès if(window_action−>delete_parent) ret_val = Gtk::Window::delete_event_impl(event); ret_val = window_action−>post_delete_action(event, remain_on_delete, ret_val); return ret_val; };

virtual gint button_press_event_impl (GdkEventButton *event) { gint ret_val = 0; ret_val = window_action−>pre_button_press_action(event); if(window_action−>button_press_parent) ret_val = Gtk::Window::button_press_event_impl(event); ret_val = window_action−>post_button_press_action(event,ret_val); return ret_val; };

virtual gint button_release_event_impl (GdkEventButton *event) { gint ret_val = 0; ret_val = window_action−>pre_button_release_action(event); if(window_action−>button_release_parent) ret_val = Gtk::Window::button_release_event_impl(event); ret_val = window_action−>post_button_release_action(event,ret_val); return ret_val; };

virtual gint key_press_event_impl (GdkEventKey *event) { gint ret_val = 0; ret_val = window_action−>pre_key_press_action(event); if(window_action−>key_press_parent) ret_val = Gtk::Window::key_press_event_impl(event); ret_val = window_action−>post_key_press_action(event,ret_val); return ret_val; };

virtual gint key_release_event_impl (GdkEventKey *event) { gint ret_val = 0; ret_val = window_action−>pre_key_release_action(event); if(window_action−>key_release_parent) ret_val = Gtk::Window::key_release_event_impl(event); ret_val = window_action−>post_key_release_action(event,ret_val); return ret_val; }; };

#endif

Page 104/112 eivd Framework sous Linux Bastien Andrès 8.6.2.12. GfAction

// Fichier : GfAction.cc

#ifndef _GFACTION_CC_ #define _GFACTION_CC_

#include "GfUtil.cc" class GfAction { public: virtual void action() {;}; };

#endif

Page 105/112 eivd Framework sous Linux Bastien Andrès 8.6.2.13. GfButtonAction

// Fichier : GfButtonAction.cc

#ifndef _GFBUTTONACTION_CC_ #define _GFBUTTONACTION_CC_

#include "GfUtil.cc" #include "GfAction.cc" class GfButtonAction : public GfAction { public:

gboolean clicked_parent; gboolean enter_parent; gboolean leave_parent; gboolean pressed_parent; gboolean released_parent;

GfButtonAction() { clicked_parent = TRUE; enter_parent = TRUE; leave_parent = TRUE; pressed_parent = TRUE; released_parent = TRUE; };

virtual void pre_clicked_action (){;}; virtual void pre_enter_action (){;}; virtual void pre_leave_action (){;}; virtual void pre_pressed_action (){;}; virtual void pre_released_action (){;};

virtual void post_clicked_action (){;}; virtual void post_enter_action (){;}; virtual void post_leave_action (){;}; virtual void post_pressed_action (){;}; virtual void post_released_action (){;}; };

#endif

Page 106/112 eivd Framework sous Linux Bastien Andrès 8.6.2.14. GfWindowAction

// Fichier : GfWindowAction.cc

#ifndef _GFWINDOWACTION_CC_ #define _GFWINDOWACTION_CC_

#include "GfUtil.cc" #include "GfAction.cc" class GfWindowAction : public GfAction { public:

gboolean delete_parent; gboolean button_press_parent; gboolean button_release_parent; gboolean key_press_parent; gboolean key_release_parent;

GfWindowAction() { delete_parent = TRUE; button_press_parent = TRUE; button_release_parent = TRUE; key_press_parent = TRUE; key_release_parent = TRUE; }

virtual gint pre_delete_action (GdkEventAny *event, gboolean rod) { return rod; };

virtual gint pre_button_press_action (GdkEventButton *event) { return 0; };

virtual gint pre_button_release_action (GdkEventButton *event) { return 0; }; virtual gint pre_key_press_action (GdkEventKey *event) { return 0; };

virtual gint pre_key_release_action (GdkEventKey *event) { return 0; };

Page 107/112 eivd Framework sous Linux Bastien Andrès

virtual gint post_delete_action (GdkEventAny *event, gboolean rod, gint def_ret) { return rod; };

virtual gint post_button_press_action (GdkEventButton *event, gint def_ret) { return def_ret; };

virtual gint post_button_release_action (GdkEventButton *event, gint def_ret) { return def_ret; };

virtual gint post_key_press_action (GdkEventKey *event, gint def_ret) { return def_ret; };

virtual gint post_key_release_action (GdkEventKey *event, gint def_ret) { return def_ret; }; };

#endif

Page 108/112 eivd Framework sous Linux Bastien Andrès 8.6.2.15. GfApp (Exemple d’application)

// Fichier : GfApp.cc

#ifndef _GFAPP_CC_ #define _GFAPP_CC_

#include "GfApplication.cc" #include "GfBin.cc" #include "GfButton.cc" #include "GfContainer.cc" #include "GfEventButton.cc" #include "GfEventWindow.cc" #include "GfObject.cc" #include "GfWidget.cc" #include "GfWindow.cc"

// −−− button action −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− class MyButtonAction : public GfButtonAction { public: virtual void post_clicked_action() { cout << "Button Pressed !!" << endl; } };

MyButtonAction mybuttonaction;

// −−− button −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− class MyButton : public GfButton { public: virtual void assign(); }; void MyButton::assign() { title = "MyButton"; style = GTK_RELIEF_NORMAL; actions = &mybuttonaction;

resize_mode = GTK_RESIZE_IMMEDIATE; border_width = 0; child_count = 0; childs = NULL;

min_size_x = 50; min_size_y = 30; uposition_x = 10; uposition_y = 10; }

MyButton mybutton;

// −−− window −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− class MyWindow : public GfWindow { private: GfWidget* child;

Page 109/112 eivd Framework sous Linux Bastien Andrès public: virtual void assign(); }; void MyWindow::assign() { type = GTK_WINDOW_TOPLEVEL; title = "MyWindow"; position = GTK_WIN_POS_CENTER; default_size_x = 320; default_size_y = 100; policy_allow_shrink = true; policy_allow_grow = true; policy_auto_shrink = true; auto_show = true; remain_on_delete = false;

resize_mode = GTK_RESIZE_IMMEDIATE; border_width = 10; child_count = 1; child = &mybutton; childs = &child;

min_size_x = 160; min_size_y = 50; uposition_x = 0; uposition_y = 0; }

MyWindow mywindow;

// −−− application −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− class GfApp : public GfApplication { public:

GfApp(); virtual void initInstance(int argc, char *argv[]); virtual void assign(); };

GfApp::GfApp() {} void GfApp::initInstance(int argc, char *argv[]) { for(int i = 1 ; i < argc ; i++) cout << argv[i] << endl; } void GfApp::assign() { theWindow = &mywindow; }

// −−− the only application instance −−−−−−−−−−−−−−−−−−−−−−−−−−−−

GfApp theApp;

#endif

Page 110/112 eivd Framework sous Linux Bastien Andrès 8.6.2.16. Makefile du Framework en Gtk−−

CXXBUILD = g++ −c $< ‘gtkmm−config −−cflags‘ MARK = echo "ok" > $@ ; rm $@ >> cleaning_list CXXBUILDO = g++ −O2 $< −o $@ ‘gtkmm−config −−cflags −−libs‘

GfMain: GfMain.cc GfApp GfUtil GfApplication $(CXXBUILDO)

GfObject: GfObject.cc $(CXXBUILD) $(MARK)

GfWidget: GfWidget.cc GfObject $(CXXBUILD) $(MARK)

GfContainer: GfContainer.cc GfWidget $(CXXBUILD) $(MARK) GfBin: GfBin.cc GfContainer $(CXXBUILD) $(MARK) GfWindow: GfWindow.cc GfBin GfEventWindow GfWindowAction $(CXXBUILD) $(MARK)

GfButton: GfButton.cc GfBin GfEventButton GfButtonAction $(CXXBUILD) $(MARK)

GfUtil: GfUtil.cc $(CXXBUILD) $(MARK)

GfAction: GfAction.cc $(CXXBUILD) $(MARK)

GfButtonAction: GfButtonAction.cc GfAction $(CXXBUILD) $(MARK)

GfWindowAction: GfWindowAction.cc GfAction $(CXXBUILD) $(MARK)

GfApplication: GfApplication.cc GfWindow $(CXXBUILD) $(MARK)

GfEventWindow: GfEventWindow.cc GfWindowAction $(CXXBUILD) $(MARK)

GfEventButton: GfEventButton.cc GfButtonAction $(CXXBUILD) $(MARK)

GfApp: GfApp.cc GfButton GfWindow GfApplication $(CXXBUILD)

Page 111/112 eivd Framework sous Linux Bastien Andrès $(MARK) test: test.cc $(CXXBUILDO) clean: rm −Rf *.o core cleaning_list echo "# Cleaning list" > cleaning_list

Page 112/112