Page suivante Page précédente Table des matières

### 3. Créer une nouvelle Application

KAppWizard, l'Assistant d'Application KDE, vous aide à commencer à travailler sur de nouveaux projets avec KDevelop. Ainsi, tous vos projets seront d'abord créés par l'assistant ; ensuite, vous pourrez commencer à les construire et à étendre le squelette de code source déjà fourni. KAppWizard vous permet aussi de choisir entre plusieurs types de projets selon les objectifs de votre projet :

- Application KDE Normale : inclut le code source de la structure complète d'une application KDE standard avec le support du modèle Controleur-Document-Vue, une barre de menus, une barre d'outils et une barre d'état ainsi qu'un ensemble de documentations standards basées sur SGML, des liens KDE et des icônes d'applications. C'est le type d'application généralement utilisé pour un nouveau projet KDE ;
- Mini Application KDE : contient la même structure générale que le type d'Application KDE Normale mais avec la différence que le code de l'application ne fournit qu'un widget comme fenêtre ;
- Application Qt Normale : globalement similaire à une application KDE normale avec la différence que le projet est uniquement basé sur la bibliothèque Qt et ne contient aucun support de KDE. C'est utile pour des projets qui doivent être portables pour les plates-formes Windows ou qui ne nécessitent pas les bibliothèques de KDE pour l'utilisateur final ;
- Application C : uniquement basée sur le compilateur C, elle s'exécute dans une console ;
- Application C++ : uniquement basée sur le compilateur C++, elle s'exécute dans une console comme une application C et ne nécessite donc pas X-Window ;
- Projet personnalisé : crée un projet vide sans aucun code source. C'est utile pour porter dans KDevelop des projets existant déjà ou pour des projets que vous commencez à partir de rien. Notez que vous devrez gérer vous-même tous les fichiers de construction (NdT : makefiles) et les scripts de configuration (NdT : configure scripts).

Dans ce chapitre, nous allons voir comment KAppWizard peut être appelé et ce qui doit être fait pour générer un projet d'application KDE. Ce sera aussi la première étape de notre tour d'horizon, où nous créerons la version initiale d'un projet d'exemple. Pour tous les types de projets, les étapes sont globalement les mêmes, certaines options pouvant juste être disponibles ou non.

### 3.1 Appel de KAppWizard et Génération de Projet

#### Lancement de KAppWizard et Première Page

Pour débuter votre première application KDE, ouvrez KDevelop. Sélectionnez ensuite "Nouveau..." dans le menu "Projet". KAppWizard démarre et vous voyez sur la première page une arborescence contenant les types de projets. Quand un type de projet est sélectionné, vous voyez un aperçu de l'apparence qu'aura l'application après le processus de construction. Choisissez le sous-arbre KDE, type Normal. Appuyez ensuite sur le bouton "Suivant" en bas de la première page de l'assistant. Cela vous mènera à la page suivante où vous devrez définir les options générales du projet.

#### La Page des Paramètres de Génération

Pour notre application exemple, nous choisissons `KScribble` comme nom de projet ; vous devez donc le saisir dans le champ "Nom du projet". Sélectionnez ensuite le dossier où vous voulez construire votre projet ; le dossier par défaut est votre dossier utilisateur. Vous pouvez saisir le chemin manuellement ou vous pouvez appuyer sur le bouton à droite pour sélectionner le dossier dans une boîte de dialogue.

Ensuite, vous devez saisir le numéro de version. Pour la première version, définissez le à `0.1`. Il est habituel de numéroter les nouvelles applications qui sont en cours de développement avec un numéro de sortie inférieur à 1, et comme la version initiale contiendra uniquement l'architecture standard, nous appelerons cette version 0.1.

Enfin, ajoutez votre nom dans le champ "Auteur" et votre adresse de courrier électronique. Vous pouvez laisser tels quels (valeurs par défaut) les autres paramètres.

Pour obtenir des informations sur toutes les autres options, vous pouvez faire un clic droit sur les options et vous obtiendrez une fenêtre d'aide rapide décrivant le but de cette option.

Ces options sont :

- générer les sources et les en-têtes : génère le code source de l'application
- Fichiers standards GNU : ajoute une copie de la Licence Publique Générale GNU GPL au projet ainsi que des fichiers standards pour informer l'utilisateur lors de la distribution du paquetage
- Manuel d'utilisation : le manuel d'utilisation en SGML, déjà préparé pour votre projet
- Documentation de l'API : crée une documentation initiale au format HTML pour l'Interface de Programmation d'Application (NdT : API signifie littéralement Application Programming Interface)
- Fichier lsm : le "Linux Software Map", utilisé par les sites de distribution, il contient une description rapide sur le but du projet et ses prérequis
- Fichier .kdelnk : un lien KDE qui installera votre application dans le menu "Applications" du tableau de bord de KDE
- Icône de Programme : une icône qui représente votre projet et peut être utilisée pour créer un lien sur le bureau.

Maintenant, nous allons passer à la page suivante en appuyant à nouveau sur le bouton "Suivant" pour définir le modèle des fichiers d'en-têtes de votre projet.

#### Les modèles des En-têtes et des fichiers Sources

La page de modèle des fichiers d'en-tête vous permet d'ajouter automatiquement, dans vos fichiers d'en-tête, un préambule contenant le nom du fichier, la date de construction, l'année du copyright ainsi que votre nom et votre adresse électronique. Vous n'avez pas à convertir ces parties en majuscule car KAppWizard le fait automatiquement et enregistre le modèle de ce projet pour l'utiliser lors de la création de nouveaux fichiers.

La seconde partie du modèle d'en-tête par défaut contient des informations sur la licence. Par défaut, votre projet est placé sous la Licence Publique Générale GNU GPL qui est aussi incluse dans le paquetage. Cette licence est utilisée pour protéger votre code source contre toute personne qui copierait seulement les sources pour son usage personnel. La Licence Publique Générale, qui vous offre cette licence gratuitement et protège vos droits en tant qu'auteur, est habituelle pour la distribution le logiciels libres. Pour obtenir plus d'informations sur cette licence, vous devriez consulter ultérieurement le fichier COPYING dans le répertoire de base de votre nouveau projet, qui est une copie de la GPL et est déjà distribué avec votre application.

Cependant, vous pouvez choisir une autre licence ou un autre modèle d'en-tête que vous utilisez déjà pour vos projets. Dans ce cas, vous pouvez éditer directement le modèle donné par défaut. Pour cela, le modèle apparaît dans une fenêtre d'édition. Pour effacer la page par défaut, sélectionnez "Nouveau" ; pour utiliser un autre modèle, choisissez "Chargement..." qui vous permet de choisir le fichier de modèle.

Quand vous avez terminé, allez à la page suivante en appuyant sur "Suivant". C'est la page de modèle pour vos fichiers sources, elles est globalement identique à la page de modèle d'en-tête. La seule différence est que ce modèle est utilisé pour vos fichiers d'implantation.

#### Création du Projet

Maintenant que vous avez défini toutes les options de KScribble, sélectionnez "Suivant" et appuyez sur le bouton "Créer" au bas de la fenêtre de l'assistant. Si ce bouton n'est pas disponible, vous n'avez pas défini toutes les options correctement. Pour corriger une erreur, revenez en arrière dans l'assistant en appuyant sur "Précédent".

Ensuite, vous allez voir ce que fait KAppWizard - il copie tous les modèles dans le dossier de votre projet et crée le nouveau projet. Lorsque KAppWizard a terminé, le bouton "Annuler" se change en un bouton "Quitter" pour sortir de l'assistant. Après cette dernière étape, vous avez terminé la création d'un nouveau projet. Ensuite, KDevelop le charge et l'affichage de l'arborescence vous permet de naviguer à travers les fichiers et les classes du projet.

Dans la section suivante, nous discuterons de la construction et de l'exécution de votre première version de KScribble, ainsi que de l'organisation du code source.

### 3.2 La Première Construction

Après la génération du projet, nous allons d'abord faire un petit tour dans le code source pour avoir une vision globale de la façon dont fonctionne l'architecture de l'application. Cela ne nous fera pas vraiment avancer mais il peut être utile de savoir où effectuer les modifications dans les prochaines étapes. Lorsque vous ouvrez la page LFV (Logical File Viewer) de la vue arborescente, vous voyez des dossiers qui contiennent déjà des fichiers du projet utiles au développeur. Les deux premiers dossiers sont "En-têtes" et "Sources". Évidemment, le dossier En-têtes contient tous les fichiers d'en-tête et le dossier Sources tous les codes sources. Les autres dossiers ne nous intéressent pas pour l'instant, nous y reviendrons plus tard pour voir ce qu'ils contiennent. Les deux dossiers contiennent donc les fichiers suivants :

En-têtes :

- kscribble.h : contient la déclaration de la classe KScribbleApp
- kscribbledoc.h : contient la déclaration de la classe KScribbleDoc
- kscribbleview.h : contient la déclaration de la classe KScribbleView
- resource.h : contient un ensemble de macros pour les identificateurs de menus

Sources :

- kscribble.cpp : contient l'implantation de la classe KScribbleApp
- kscribbledoc.cpp : contient l'implantation de la classe KScribbleDoc
- kscribbleview.cpp : contient l'implantation de la classe KScribbleView
- main.cpp : contient l'implantation de la fonction main().

Avant de rentrer dans les sources, nous allons laisser KDevelop construire et exécuter notre nouvelle application. Pour cela, sélectionnez "Construire" dans le menu "Construire" ou cliquez sur le bouton correspondant de la barre d'outils. La fenêtre des messages s'ouvre au bas de KDevelop et vous permet de voir ce que `make` fait grâce aux messages qu'il donne :

> `

>     1   Making all in docs
>     2   make[1]: Entering directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     3   Making all in en
>     4   make[2]: Entering directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs/en'
>     5   make[2]: Nothing to be done for `all'.
>     6   make[2]: Leaving directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs/en'
>     7   make[2]:
>     Entering directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     8  make[2]: Nothing to be done for `all-am'.
>     9   make[2]: Leaving directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     10  make[1]: Leaving directory
>     `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     11  make[1]: Entering
>     directory `/home/rnolden/Tutorial/kscribble1/kscribble'
>     12  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include
>     -I/usr/X11R6/include    -O0 -g -Wall  -c        kscribbleview.cpp
>     13  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include
>     -I/usr/X11R6/include    -O0 -g -Wall  -c        kscribbledoc.cpp
>     14  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include
>     -I/usr/X11R6/include    -O0 -g -Wall  -c                kscribble.cpp
>     15  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include
>     -I/usr/X11R6/include    -O0 -g -Wall  -c        main.cpp
>     16  /usr/bin/moc ./kscribble.h -o kscribble.moc.cpp
>     17  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include
>      -I/usr/X11R6/include    -O0 -g -Wall  -c       kscribble.moc.cpp
>     18  /usr/bin/moc ./kscribbledoc.h -o kscribbledoc.moc.cpp
>     19  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include
>     -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>     kscribbledoc.moc.cpp
>     20  /usr/bin/moc ./kscribbleview.h -o kscribbleview.moc.cpp
>     21  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include
>     -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>     kscribbleview.moc.cpp
>     22  /bin/sh ../libtool --silent --mode=link g++  -O0
>     -g -Wall   -o kscribble -L/opt/kde/lib  -L/usr/X11R6/lib -rpath /opt/kde/lib
>             -rpath /usr/X11R6/lib kscribbleview.o kscribbledoc.o kscribble.o
>     main.o kscribble.moc.o kscribbledoc.moc.o kscribbleview.moc.o   -lkfile -lkfm
>     -lkdeui -lkdecore -lqt -lXext -lX11
>     23  make[1]: Leaving directory `/home/rnolden/Tutorial/kscribble1/kscribble'

> `

Comme vous le constatez, nous avons mis des numéros au début de chaque ligne qui n'apparaissent pas dans les messages ; c'est juste pour simplifier la description de ce qui s'est passé pendant la construction. D'abord, `make` travaille récursivement. Cela signifie qu'il débute dans le dossier où il a été appelé, entre d'abord dans les sous-dossiers, retourne et traite le dossier suivant. À la fin, le dossier où il a été lancé est traité et `make` termine. C'est pourquoi `make` est lancé dans le dossier principal du projet qui contient les sources. Aux lignes 1 et 2, vous voyez que le processus `make` entre dans le dossier `docs`, puis dans le sous-dossier `en`. Comme il n'y a rien à faire, il quitte ces dossiers jusqu'à revenir au répertoire source `kscribble` à la ligne 11. Ensuite, le vrai travail commence : `make` appele le compilateur, ici `g++`, pour compiler le fichier source `kscribbleview.cpp`. La macro `-DHAVE_CONFIG_H` indique que le fichier `config.h` doit être utilisé. Ce fichier, qui contient des macros spécifiques à chaque plate-forme et à l'application, est situé dans le dossier principal du projet. Les commandes `-I` qui suivent ajoutent un chemin vers des fichiers d'inclusion (NdT : appelés aussi "includes") où `g++` pourra trouver les "includes" dont il a besoin. Ces chemins sont le dossier courant, le dossier principal du projet (avec `-I..`) et le chemin des includes pour les fichiers d'en-tête de KDE, Qt et X11. Les dossiers de ces fichiers d'includes sont déterminés par le script `configure` et définis dans les Makefiles, c'est pourquoi le compilateur sait où ils sont situés. Enfin, `-O0` définit l'optimisation à zéro (aucune optimisation), `-g` active le déboguage, `-Wall` définit les avertissements du compilateur à `all` et `-c` dit au compilateur de produire un fichier objet intermédiaire, c'est-à-dire seulement compiler le fichier.

Cela est aussi fait pour les autres fichiers sources de notre projet aux lignes 13-15. Évidemment, nos sources sont compilées mais au lieu de lier (NdT : opération réalisée par l'éditeur de liens) les fichiers objets intermédiaires des sources pour en faire un exécutable, nous voyons d'autres commandes. À la ligne 16, vous voyez que le programe "moc" est appelé pour traiter le fichier d'en-tête `kscribble.h` qui génère le fichier `kscribble.moc.cpp`. Ensuite, à la ligne 17, le fichier source est aussi compilé. La même chose se passe pour les autres fichiers d'en-tête du projet jusqu'à la ligne 21. En fait, comme la boîte à outils Qt contient le mécanisme signal/slot mais reste une implantation C++, vous utilisez des mots-clés comme les déclarations `signals:` et `slots:` dans vos classes qui ne sont pas, à l'origine, dans le langage C++. Cela vous permet de faire facilement communiquer les objets qui héritent de la classe `QObject`, évitant ainsi l'utilisation des traditionnels pointeurs sur les fonctions de callback. Aussi, l'application nécessite les sources qui implantent cette fonctionnalité et c'est pour cela que `moc` est appelé. Moc est le Compilateur de Méta Objet (NdT : Moc signifie littéralement Meta Object Compiler) de la boîte à outils Qt et construit l'implantation des mécanismes de signal et de slot en analysant le fichier d'en-tête et en générant un fichier source qui doit être compilé dans le binaire. Comme KDevelop utilise `automoc` pour déterminer quels fichiers doivent être traités, vous n'avez pas besoin de vous préoccuper des appels à `moc` et au compilateur C++ pour les fichiers générés par moc. Souvenez-vous seulement des règles qui permettent à une classe d'utiliser le mécanisme de signal et de slot de `QObject` ou toute classe qui hérite de `QObject` elle-même, vous devez aussi inclure la macro `Q_OBJECT` (sans point-vigule !) au début de la déclaration de la classe et la déclaration des signaux et des slots.

Enfin, votre binaire est construit par le compilateur. Le binaire généré est appelé `kscribble`, l'éditeur de liens inclut le chemin vers KDE et vers les bibliothèques de X11 et lie les sources avec les bibliothèques `kfile, kfm, kdeui, kdecore, qt, Xext` et `X11`. Ensuite, c'est terminé et make se termine.

### 3.3 Le Squelette des Sources

Pour décrire le concept de fonctionnement d'une application KDE, nous allons d'abord regarder précisément le squelette de sources déjà fourni par l'Assistant d'Application. Comme nous l'avons déjà vu, nous avons un ensemble de sources et de fichiers d'en-tête qui constituent le code initial de l'application et la rendent prête à être exécutée. De ce fait, la plus simple façon d'expliquer le code est de suivre l'implantation ligne par ligne, comme il est traité durant l'exécution du programme jusqu'à ce qu'il entre dans la boucle d'événements principale et soit prêt à accepter les entrées de l'utilisateur. Ensuite, nous regarderons les fonctionnalités qui permettent les interactions avec l'utilisateur et comment certaines choses fonctionnent. C'est probablement la meilleure façon d'expliquer l'architecture et comme elle est similaire pour presque toutes les applications KDE, cela vous permettra de lire plus facilement le code source d'autres projets. En plus, vous apprendrez quoi et où changer dans le code pour faire en sorte que vos applications se comportent comme prévu.

#### La fonction `main()`

Comme l'application commence son exécution en entrant dans la fonction `main()`, ce sera aussi notre point de départ pour examiner le code. La fonction `main()` de KScribble est implantée dans le fichier `main.cpp` et peut également être trouvée grâce au Navigateur de Classe en sélectionnant le sous-dossier "Fonctions" du dossier "Globaux" :

> `

>     1  #include "kscribble.h"
>     2
>     3  int main(int argc, char* argv[]) {
>     4  KApplication app(argc,argv,"KScribble");
>     5
>     6    if (app.isRestored())
>     7  {
>     8       RESTORE(KScribbleApp);
>     9    }
>     10   else
>     11   {
>     12  KScribbleApp* kscribble = new KScribbleApp;
>     13      kscribble->show();
>     14   if(argc > 1){
>     15        kscribble->openFile(argv[1]);
>     16      }
>     17    }
>     18    return app.exec();
>     19  }

> `

La première chose qui se passe généralement est la création d'un objet `KApplication` qui reçoit le nom de notre application KScribble comme troisième paramètre. Lors de la création d'une nouvelle `KApplication`, une nouvelle instance de `KConfig` est également créée, permettant d'accéder au fichier de configuration $HOME/.kde/share/config/appname + rc qui contient toutes les informations que nous voulons utiliser lors du lancement des fenêtres de l'application. Le nom passé au constructeur de `app` sera utilisé plus tard comme titre de la fenêtre. Ici, le code est un petit peu différent du code d'exemple donné précédemment pour convertir une application Qt en une application KDE. Une fois que l'objet `KApplication` est présent, nous testons si l'application est démarrée par le gestionnaire de sessions de `kwm` ou manuellement par l'utilisateur. Cela est déterminé en appelant `isRestored()` sur l'objet `app` qui retourne `true` pour la gestion de sessions et `false` pour un lancement manuel. Comme la gestion des sessions est une fonctionnalité importante des applications KDE et largement utilisée par l'architecture mais plus longue à expliquer, nous allons d'abord continuer par la section `else{}` ; ensuite, nous y reviendrons et expliquerons la fonctionnalité de gestion des sessions dans une étape ultérieure.

#### Lancement de l'Application par l'Utilisateur

La section `else{}` crée maintenant une instance de la classe `KScribbleApp` à la ligne 12. Cet objet est appelé pour s'afficher à la ligne 13, comme d'habitude ; la ligne 14 détermine si un argument a été passé à la ligne de commande et, comme c'est souvent le nom d'un fichier, appelle l'objet `kscribble` pour l'ouvrir avec `openFile()`. Remarquez que nous n'avons pas appelé la méthode `setTopWidget(kscribble)` pour notre application - c'est déjà fait par la classe dont hérite `KScribbleApp`. Maintenant, nous allons regarder notre objet `KScribbleApp` - qu'est-ce que c'est et que fournit-il déjà ? La seule chose que nous savons jusqu'à maintenant, c'est que ça doit être un Widget pour représenter l'interface utilisateur dans la fenêtre pricipale. Allons dans l'implantation de la classe `KScribbleApp` qui se trouve dans le fichier `kscribble.cpp` ou par un clic sur l'icône de classe dans le Navigateur de Classe. L'instance est créée par le constructeur. D'abord, nous voyons qu'elle hérite de la classe `KTMainWindow` qui fait partie de la bibliothèque `kdeui`. Cette classe hérite elle-même de `QWidget` donc, comme d'habitude, nous avons un widget normal comme fenêtre de plus haut niveau (NdT : top-level window). `KTMainWindow` contient beaucoup de fonctionnalités qui sont utilisées par la classe `KScribbleApp`. Elle fournit une barre de menus, une barre d'outils, une barre d'état et le support de la gestion de sessions. La seule chose que nous ayons à faire en dérivant de `KTMainWindow` est de créer tous les objets dont nous avons besoin et de créer un autre widget qui sera géré par notre instance de `KTMainWindow` comme vue principale au centre de la fenêtre ; habituellement, c'est ici que l'utilisateur travaille comme une vue d'édition de texte.

#### Le Constructeur

Regardons le code du constructeur et voyons comment l'instance est créée :

> `

>     1   KScribbleApp::KScribbleApp()
>     2   {
>     3     config=kapp->getConfig();
>     4
>     5
>     6     ///////////////////////////////////////////////////////////////////
>     7     // call inits to invoke all other construction parts
>     8     initMenuBar();
>     9     initToolBar();
>     10    initStatusBar();
>     11    initKeyAccel();
>     12    initDocument();
>     13    initView();
>     14
>     15    readOptions();
>     16
>     17
>     ///////////////////////////////////////////////////////////////////
>     18    //disable menu and toolbar items at startup
>     19    disableCommand(ID_FILE_SAVE);
>     20    disableCommand(ID_FILE_SAVE_AS);
>     21    disableCommand(ID_FILE_PRINT);
>     22
>     23    disableCommand(ID_EDIT_CUT);
>     24    disableCommand(ID_EDIT_COPY);
>     25    disableCommand(ID_EDIT_PASTE);
>     26    }

> `

Nous voyons que notre instance de `KConfig` pointe maintenant vers la configuration de l'application donc nous pourrons travailler avec les entrées du fichier de configuration ultérieurement. Ensuite, tous les composants requis par l'application sont créés par les fonctions membres correspondantes, spécifiquement à notre fenêtre principale :

- initMenuBar() : construit la barre de menus
- initToolBar() : construit la barre d'outils
- initStatusBar() : crée la barre d'état
- initKeyAccel() : définit tous les accélérateurs clavier pour notre application avec les configurations du clavier globale et spécifique à l'application
- initDocument() : crée l'objet document pour la fenêtre de l'application
- initView() : crée le widget principal pour notre vue dans la fenêtre principale
- readOptions() : lit tous les paramètres spécifiques à l'application dans le fichier de configuration et initialise le reste de l'application, comme la liste des fichiers récemment ouverts, la position des barres et la taille de la fenêtre.

Enfin, nous désactivons certaines commandes que l'utilisateur peut activer car elles ne doivent pas être disponibles dans l'état courant de l'application. Comme nous avons maintenant un aperçu global de la façon dont est créée la fenêtre de l'application, nous allons regarder en détail comment les éléments de l'interface utilisateur sont construits par les méthodes ci-dessus.

#### La Barre de Menus

Comme nous l'avons vu au-dessus, la barre de menus de KScribble est créée par la méthode `initMenuBar()`. En fait, nous créons un ensemble de `QPopupMenu`s qui apparaissent si l'utilisateur sélectionne une entrée de menu. Ensuite, nous les insérons dans la barre de menus et nous les connectons aux entrées.

D'abord, nous créons notre `recent_file_menu` qui contiendra les noms des 5 derniers fichiers ouverts. Nous devons le faire en premier car ce menu est inséré dans le `file_menu`. Ensuite, nous ajoutons directement la connexion - nous récupérons juste le signal qui est émis par l'entrée de menu avec son numéro d'entrée et appelons `slotFileOpenRecent( int )`, qui ouvre ensuite le bon fichier de la liste des fichiers récemment ouverts.

Ensuite, nous créons notre menu "Fichier". C'est le menu qui sera visible dans la barre de menus. Les actions standards sont ensuite insérées une par une dans le menu popup - d'abord, les commandes pour créer un nouveau fichier, ouvrir un fichier, fermer un fichier... et enfin "E&xit" pour fermer l'application. Toutes les entrées de menu doivent être créées dans l'ordre dans lequel elles apparaîtront ensuite donc nous devons rester vigilants sur ce que nous voulons mettre et à quelle place. À titre d'exemple, regardons les entrées suivantes :

> `

>     file_menu->insertItem(Icon("fileopen.xpm"), i18n("&Open..."), ID_FILE_OPEN );
>     file_menu->insertItem(i18n("Open &recent"), recent_files_menu, ID_FILE_OPEN_RECENT );

> `

La première ligne insère une entrée "Open...". Comme nous voulons qu'elle ait une icône, nous utilisons la méthode `insertItem()` avec le nom de l'icône. Pour comprendre le processus de chargement d'icône, nous devons savoir comment et où est déclaré `Icon()` - en fait, c'est une macro fournie par la classe `KApplication` :

> `

>     #define Icon(x) kapp->getIconLoader()->loadIcon(x)

> `

Elle utilise, d'ailleurs, la macro interne suivante pour accéder à l'objet application :

> `

>     #define kapp KApplication::getKApplication()

> `

Cela signifie que l'objet `KApplication` contient déjà une instance du chargeur d'icône - nous devons seulement y accéder ; ensuite, il chargera l'icône correpondante. Comme nos icônes viennent toutes des bibliothèques de KDE, nous n'avons pas à nous préoccuper de quoi que ce soit d'autre - elles sont installées automatiquement sur le système et nous n'avons donc pas besoin de les inclure dans le paquetage de notre application pour pouvoir les utiliser.

Après le paramètre d'icône (qui est optionnel), nous insérons le nom de l'entrée de menu par `i18n("&Open...")`. Là, nous devons signaler deux choses : premièrement, l'entrée est insérée avec la méthode `i18n()`. Comme pour `Icon()`, c'est aussi une macro définie dans `kapp.h` et appelle l'objet `KLocale` de `KApplication` pour traduire l'entrée dans la langue utilisée actuellement :

> `

>     #define i18n(X) KApplication::getKApplication()->getLocale()->translate(X)

> `

Il faut signaler que quelqu'un pourrait penser "Je ne veux pas utiliser de macros" - vous pouvez le faire dans la plupart des cas. Mais ici, il est nécessaire d'utiliser `i18n()` car pour l'internationalisation, les fichiers de traduction correspondants doivent être générés. Comme ce processus de génération repose sur l'utilisation de la chaîne de caractères `i18n`, vous devez utiliser cette macro.

Comme vous l'avez peut-être déjà deviné, le symbole & (et commercial) dans les entrées de menu est interprété ultérieurement comme une petite ligne sous la lettre correspondante dans l'entrée de menu. Cela permet un accès rapide à la commande du menu via le clavier quand l'utilisateur appuie simultanément sur la touche `Alt` et la lettre soulignée.

Enfin, nous donnons à l'entrée de menu un identificateur (NdT : ID) qui est une valeur entière par laquelle nous pourrons trouver l'entrée ultérieurement. Pour garder un oeil sur les valeurs utilisées, celles-ci sont définies par des macros et sont regroupées dans le fichier `resource.h` de votre projet. Par souci de cohérence, ces macros sont écrites en majuscules, commencent par ID_ et sont suivies du nom du menu et de celui de l'entrée. Cela aide énormément à se remémorer le sens de chaque entrée n'importe où dans le code ; il n'est donc plus nécessaire d'aller systématiquement dans l'implantation de la barre de menus pour chercher les entrées.

Le deuxième exemple d'entrée montre une autre variante de la méthode `insertItem()`. Ici, nous ajoutons le menu popup recent_files_menu comme un élément de menu. Cela signifie que l'entrée s'affiche elle-même avec la chaîne donnée "Open recent", suivie par une flèche vers la droite. Lors d'une sélection, le menu popup des fichiers récemment ouverts apparaît et l'utilisateur peut choisir un des derniers fichiers.

Enfin, il y a beaucoup d'autres manières d'insérer des éléments de menu - l'architecture rend cela aussi simple que possible. Plus d'informations sur la classe `QMenuData` peuvent être trouvées dans la documentation de Qt.

Maintenant, après avoir créé les menus popup `file_menu, edit_menu` et `view_menu`, nous devons insérer aussi un menu "Aide". Nous pourrions le faire comme pour les autres menus mais la classe `KApplication` fournit une méthode simple et rapide pour le faire :

> `

>     help_menu = kapp->getHelpMenu(true, i18n("KScribble\n" VERSION ));

> `

C'est tout ce que nous avons à faire pour obtenir un menu d'aide qui contient une entrée pour le contenu de l'aide accessible avec le raccourci clavier F1, une boîte de dialogue "À propos" pour l'application et une boîte de dialogue "À propos" pour KDE (qui peut être désactivée en appelant `getHelpMenu(false,...);`). Le contenu de la boîte de dialogue "À propos" de notre application est à nouveau défini avec la chaîne `i18n()` - VERSION prend la macro qui est définie dans le fichier `config.h` pour le numéro de version du projet donc nous n'avons pas besoin de le changer manuellement à chaque fois que nous voulons diffuser une nouvelle version. N'hésitez pas à ajouter ici des informations sur votre application, par exemple votre nom, votre adresse électronique, le copyright, etc.

Maintenant, nous n'avons plus qu'à insérer les popups dans la barre de menus. Comme `KTMainWindow` construit déjà une barre de menus pour nous, il suffit de les insérer en appelant `menuBar()->insertItem();`.

Ce qu'il reste à faire est de connecter les entrées de menu avec les méthodes qu'elles exécuteront. C'est pourquoi, nous connectons chaque menu popup par son signal `activated( int )` à une méthode `commandCallback( int )` qui contient un `switch` qui appelle les méthodes correspondantes pour les entrées de menu. En plus, nous connectons le signal `highlighted( int )` des popups pour fournir de l'aide sur chaque entrée dans la barre d'état. Lorsque l'utilisateur déplace sa souris ou le focus du clavier sur une entrée, la barre d'état affiche alors le message d'aide correspondant.

Après avoir terminé avec la barre de menus, nous pouvons passer à la barre d'outils dans la section suivante. Remarquez qu'une instance de `KTMainWindow` peut avoir seulement une seule barre de menus visible à la fois ; c'est pourquoi, si vous voulez construire plusieurs barres de menus, vous devez les créer séparément avec des instances de `KMenuBar` et définir l'une d'elles comme la barre de menus courante avec la méthode appropriée de `KTMainWindow`. Consultez la documentation de la classe `KMenuBar` pour plus de détails sur ses fonctionnalités étendues, voir aussi Configuration des Barres de Menus et des Barres d'Outils.

#### La Barre d'Outils

La création de barres d'outils est bien plus simple que pour les barres de menus. Comme `KTMainWindow` fournit déjà des barres d'outils qui sont créées dès leur première insertion, vous êtes libre d'en créer plusieurs. Ajoutez juste les boutons pour les fonctions que vous voulez fournir :

> `

>     toolBar()->insertButton(Icon("filenew.xpm"), ID_FILE_NEW, true, i18n("New File") );

> `

Ceci ajoute un bouton aligné à gauche avec l'icône "filenew.xpm" et l'identificateur (NdT : ID) correspondant dans la barre d'outils. Le troisième paramètre décide si le bouton doit être activé ou non ; par défaut, nous le définissons à `true` car nos méthodes `disableCommand()` à la fin du constructeur le font automatiquement pour nous, à la fois pour les entrées du menu et de la barre d'outils. Enfin, le dernier paramètre est utilisé pour la "bulle d'aide" - quand l'utilisateur place le pointeur de la souris sur le bouton pour le mettre en surbrillance, une petite fenêtre, qui contient un court message d'aide dont le contenu est défini ici, apparaît. Enfin, les boutons de la barre d'outils sont connectés à notre méthode `commandCallback()` par leur signal `clicked()`. Lors du signal `pressed()`, nous permettons à l'utilisateur de recevoir le message d'aide correspondant dans la barre d'état.

Informations supplémentaires :

Comme les barres d'outils sont créées en utilisant la classe `KToolBar`, vous devriez consulter la documentation correspondante. Avec `KToolBar`, beaucoup de choses nécessaires pour une barre d'outils peuvent être réalisées, comme un popup retardé si votre bouton veut afficher un menu quand le bouton reste enfoncé ou même l'utilisation d'autres widgets comme par exemple une liste déroulante. Par défaut, la barre d'outils remplit complètement la largeur de la fenêtre ce qui est sympathique pour l'utilisation d'une seule barre. Lorsque vous en avez plus d'une, vous devriez penser à définir la taille de la barre à l'extrémité du bouton le plus à droite, ainsi les autres barres peuvent s'afficher dans la même ligne, sous la barre de menus. Nous évoquerons certaines techniques de conception et d'extension des barres d'outils dans la section Configuration des Barres de Menus et des Barres d'Outils.

#### La Barre d'État

La barre d'état est, comme les autres barres, déjà fournie par l'instance de `KTMainWindow` donc nous avons juste à insérer nos éléments dedans comme nous le souhaitons. Par défaut, l'architecture contient seulement une entrée qui affiche l'aide de la barre d'état. Pour beaucoup d'applications, ce n'est pas suffisant, vous pourrez donc ajouter les entrées dont vous avez besoin pour afficher, par exemple, des coordonnées ou autre chose.

De plus, une application ne peut avoir qu'une seule barre d'état, comme pour la barre de menus. Si vous voulez en construire plusieurs, vous devrez les créer séparément et définir la barre courante avec la méthode appropriée de `KTMainWindow`. La barre d'état permet aussi d'insérer des widgets qui peuvent être utilisés pour créer des zones pour afficher des barres de progression, comme KDevelop le fait. Référez-vous à la documentation de la classe `KStatusBar`.

#### Raccourcis Clavier

Une fois rendu à la méthode `initKeyAccel()`, nous avons déjà construit les éléments standards de la fenêtre principale d'une application - la barre de menus, la barre d'outils et la barre d'état.

Cependant, nous n'avons pas encore défini les raccourcis clavier par lesquels l'utilisateur expérimenté qui veut uniquement travailler avec son clavier peut accéder rapidement aux commandes qui sont le plus souvent utilisées pendant une session de travail de notre programme. Pour cela, nous aurions pu insérer les touches de raccourci , par exemple, lors de l'insertion des éléments du menu mais KDE offre une bonne solution pour construire et maintenir les raccourcis clavier . Beaucoup d'utilisateurs veulent d'un côté qu'ils soient configurables et d'un autre côté les raccourcis standards doivent être les mêmes pour toutes les applications. C'est pourquoi, le Centre de Contrôle de KDE permet de configurer les raccourcis clavier standards en utilisant la classe `KAccel`. En plus, les bibliothèques de KDE contiennent un widget qui permet aux utilisateurs de configurer facilement les raccourcis clavier spécifiques à l'application. Comme l'architecture d'application utilise seulement des éléments de menus qui sont des actions standards comme "Nouveau" ou "Quitter", celles-ci sont définies par la méthode `initKeyAccel()`. Les actions standards doivent juste être connectées, pour la configuration du clavier spécifique à votre application, vous devez les insérer d'abord en spécifiant le nom du raccourci clavier et ensuite les connecter. Comme nos raccourcis sont tous présents dans la barre de menus, nous devons changer le raccourci pour les entrées du popup. Enfin, nous appelons `readSettings()` qui lit la configuration courante depuis la fenêtre du bureau de KDE (NdT : KDE rootwindow) où est stockée la configuration des raccourcis standards, puis les paramètres des raccourcis spécifié dans le fichier de configuration de l'application. Quand nous aurons progressé dans notre projet d'exemple, nous vous dirons aussi comment modifier avec une boîte de dialogue de configuration les raccourcis spécifiques à notre application, voir Configuration des Barres de Menus et des Barres d'Outils pour cette partie du processus de développement.

#### Le Modèle Document-Vue

Les deux appels de fonctions membres suivants, `initDocument()` et `initView()`, permettent enfin de construire la partie que les fenêtres de l'application sont supposées fournir à l'utilisateur : une interface pour travailler avec les données que l'application est sensée manipuler ; et c'est aussi la raison pour laquelle l'architecture d'application contient trois classes `*App`, `*View` et `*Doc`. Pour comprendre pourquoi cette structure est utile, nous allons regarder un peu au-delà du code actuel et introduire un peu de théorie, ensuite nous reviendrons au programme pour voir comment l'architecture de KDevelop supporte ce modèle.

Typiquement, tout ce qui a été expliqué sur cette architecture est que nous avons besoin d'une instance de l'application qui contient une fenêtre principale. Cette fenêtre est chargée de fournir l'interface basique pour l'utilisateur - elle contient la barre de menus, les barres d'outils, la barre d'état et le contrôleur d'événements pour les interactions avec l'utilisateur. En plus, elle contient la zone qui est appelée la "Vue". Le but d'une vue est généralement d'afficher les données que l'utilisateur peut manipuler, par exemple un morceau de fichier texte. Cependant, le fichier texte, bien que probablement plus grand que la vue, est capable de s'afficher à l'écran, il permet à l'utilisateur d'aller dans la partie qu'il veut voir (c'est pourquoi c'est une vue) et l'utilisateur peut alors changer les données du contenu du fichier. Pour donner au programmeur une meilleure façon de séparer les parties du code de l'application, le Modèle Document-Vue a été inventé. Bien que n'étant pas un standard, il fournit la structure de fonctionnement d'une application :

- un objet Contrôleur contenu par l'application
- un objet Vue qui affiche les données avec lesquelles l'utilisateur veut travailler
- un objet Document qui contient réellement les données qui sont manipulées.

Revenons à notre exemple de travail avec un fichier texte - là, le modèle s'applique de la façon suivante : le Document lit le contenu du fichier et fournit des méthodes aussi bien pour modifier les données que pour enregistrer à nouveau le fichier. La Vue, elle, traite les événements que l'utilisateur produit avec le clavier et la souris et utilise les méthodes de l'objet document pour manipuler les données du document.

Enfin, l'objet contrôleur est responsable de l'interaction avec l'utilisateur en fournissant les objets document et vue, ainsi que les interfaces pour envoyer des commandes comme l'ouverture et l'enregistrement. En plus, certaines méthodes de l'objet vue peuvent être fournies par des commandes accessibles par des accélérateurs clavier ou la souris sur les barres de menus et les barres d'outils.

Le modèle Document-Vue a des avantages - il sépare le code du programme à la mode "orientée objet" et par là offre plus de flexibilité ; par exemple, le même objet document peut être affiché simultanément par deux vues, soit avec une nouvelle vue dans une nouvelle fenêtre, soit en séparant la fenêtre courante qui contient alors deux objets vue qui forment la zone de vue de la fenêtre.

Si vous êtes issu des systèmes MS-Windows, vous devez être familier avec tout cela - les MFC fournissent déjà un modèle de document prêt à être utilisé. Pour les applications KDE et Qt, les choses sont un peu différentes. Qt est une boîte à outils puissante car elle fournit la majorité des classes, widgets, etc nécessaires. Mais, il n'y a eu aucune intention de gérer le modèle document-vue et comme KDE hérite de Qt, ce modèle n'a pas été non plus introduit. En fait, cela s'explique aussi par le fait que les applications X ne travaillent généralement pas en MDI (Multiple Document Interface). Chaque fenêtre principale est responsable de ses propres données et cela réduit le besoin d'un modèle de document au fait que les méthodes pour travailler sur le document sont toujours écrites dans les widgets. Actuellement, la seule exception est le projet KOffice qui fournit une suite de bureautique complète comprenant un traitement de texte, un tableur, etc. Techniquement, cela est réalisé par deux changements dans l'utilisation normale de Qt et KDE :

- KOffice utilise KOM et différents mécanismes pour la communication entre objets
- les applications KOffice utilisent un modèle document-vue qui permet à toutes les applications de travailler avec n'importe quelles données de KOffice.

Mais, comme l'objectif de KDevelop est d'utiliser les bibliothèques actuelles de KDE 1.1.x et Qt 1.4x, nous ne pouvons pas utiliser ce modèle par défaut - cela apparaîtra dans les prochaines versions de KDE 2 qui contiendra (probablement) deux changements majeurs par rapport à la situation actuelle :

1. une interface MDI pour KTMainWindow
2. les bibliothèques KOM qui fournissent un modèle de document.

C'est pourquoi, pour l'instant, la façon de travailler des développeurs d'applications est soit d'implanter toutes les méthodes de documents dans leur vue, soit d'essayer de reproduire eux-mêmes un modèle de document. KDevelop souscrit à cette tentative en fournissant les classes requises et les méthodes de base qui sont généralement utilisées pour un modèle Document-Vue avec les architectures d'application de Qt et KDE.

Revenons au code, vous pouvez maintenant imaginer le but des deux méthodes que nous avons mentionnées au début de cette section : les fonctions `initDocument()` et `initView()`. `initDocument()` construit l'objet document qui représente les données de la fenêtre de l'application et initialise les attributs de base comme le bit de modification qui indique si les données utilisées ont été modifiées par l'utilisateur. Ensuite, la méthode `initView()` construit le widget `*View`, le connecte au document et appelle la méthode `setView()` de `KTMainWindow` pour dire à la fenêtre `*App` d'utiliser le widget `*View` comme vue centrale.

Pour le développeur, il est important de savoir que durant le processus de développement, il doit :

- ré-implanter dans l'objet `*View` les méthodes virtuelles de `QWidget` pour les événements du clavier et de la souris afin de pouvoir manipuler les données
- ré-implanter le `paintEvent()` de `QWidget` dans l'objet `*View` pour redessiner (Ndt : repaint()) la vue après des modifications
- compléter l'implantation pour l'impression du document via la méthode d'impression de l'objet `*View`
- ajouter la sérialisation pour permettre à l'objet `*Doc` de charger et enregistrer le fichier
- ajouter l'implantation de la structure des données du document à l'objet `*Doc` qui représente logiquement en mémoire les données du document
- ajouter les méthodes auxquelles l'utilisateur peut accéder via des raccourcis clavier, des menus ou des barres d'outils.

#### Configuration de l'Application

Maintenant, après avoir créé toutes les instances de l'instance de `KTMainWindow` de notre application pour créer la fenêtre principale, nous devons initialiser certaines valeurs qui influencent l'apparence du programme. Pour cela, nous appelons `readOptions()` qui récupère toutes les valeurs et appelle les méthodes nécessaires pour définir les attributs correspondants. La bibliothèque KDE-Core contient la classe `KConfig` qui fournit une bonne façon de stocker les valeurs dans des fichiers de configuration et permet aussi de les relire. De plus, comme chaque instance de `KApplication` crée déjà son fichier de ressources, nous devons seulement accéder à ce fichier et créer nos valeurs. Comme `KConfig` nous donne l'objet fichier, nous devons utiliser la classe `KConfigBase` pour lire et écrire toutes les entrées. L'écriture est très facile avec les méthodes `writeEntry()` mais la lecture dépend du type d'attribut que nous voulons initialiser. Généralement, une entrée dans le fichier de configuration contient un nom de valeur et une valeur. Les valeurs qui appartiennent au même contexte peuvent être regroupées dans des groupes, c'est pourquoi nous devons définir le nom du groupe afin d'accéder ensuite à ses valeurs : le groupe doit être défini seulement une seule fois pour lire les attributs qui sont dans le même groupe. Regardons ce que nous voulons lire :

> `

>     1   void KScribbleApp::readOptions()
>     2   {
>     3
>     4      config->setGroup("General Options");
>     5
>     6      // bar status settings
>     7      bool bViewToolbar = config->readBoolEntry("Show Toolbar", true);
>     8      view_menu->setItemChecked(ID_VIEW_TOOLBAR, bViewToolbar);
>     9      if(!bViewToolbar)
>     10       enableToolBar(KToolBar::Hide);
>     11
>     12     bool bViewStatusbar = config->readBoolEntry("Show Statusbar", true);
>     13     view_menu->setItemChecked(ID_VIEW_STATUSBAR, bViewStatusbar);
>     14     if(!bViewStatusbar)
>     15       enableStatusBar(KStatusBar::Hide);
>     16
>     17     // bar position settings
>     18     KMenuBar::menuPosition menu_bar_pos;
>     19     menu_bar_pos=(KMenuBar::menuPosition)config->readNumEntry("MenuBar Position", KMenuBar::Top);
>     20
>     21     KToolBar::BarPosition tool_bar_pos;
>     22     tool_bar_pos=(KToolBar::BarPosition)config->readNumEntry("ToolBar Position", KToolBar::Top);
>     23
>     24     menuBar()->setMenuBarPos(menu_bar_pos);
>     25     toolBar()->setBarPos(tool_bar_pos);
>     26
>     27     // initialize the recent file list
>     28     recent_files.setAutoDelete(TRUE);
>     29     config->readListEntry("Recent Files",recent_files);
>     30
>     31     uint i;
>     32     for ( i =0 ; i < recent_files.count(); i++){
>     33       recent_files_menu->insertItem(recent_files.at(i));
>     34     }
>     35
>     36     QSize size=config->readSizeEntry("Geometry");
>     37     if(!size.isEmpty())
>     38       resize(size);
>     39   }

> `

Comme nous l'avons vu dans un des morceaux de code précédents, la première action réalisée par notre constructeur était :

> `

>     config=kapp->getConfig();

> `

qui positionne le pointeur `config` de type `KConfig` sur la configuration de l'application. Nous n'avons donc pas besoin de nous préoccuper de l'emplacement de ce fichier de configuration. En fait, ce fichier est, conformément au Système de Fichiers Standard de KDE (KDE FSS) (NdT : KDE FSS signifie KDE File System Standard), situé dans `$HOME/.kde/share/config/` ; nous regarderons plus précisément le KDE FSS dans une autre section quand nous définirons l'emplacement pour l'installation des fichiers du projet. Comme le fichier de configuration est placé dans le répertoire utilisateur (NdT : user's home directory), chaque utilisateur possède sa propre apparence de l'application, excepté les valeurs qui sont situées dans le fichier de configuration global du système qui peut optionnellement être créé et installé par le programmeur dans le dossier de KDE. Mais, bien que cela puisse être utile dans certains cas, nous devrions éviter toute dépendance de l'application envers l'existence d'entrées de fichier. C'est pourquoi toutes les méthodes de lecture fournies par `KConfigBase` permettent d'ajouter une valeur par défaut à utiliser lorsque l'entrée n'existe pas. Une autre chose importante pour le programmeur est que le fichier de configuration est stocké sous la forme d'un fichier texte, et, pour cette raison, vous devez vous souvenir que :

- l'utilisateur peut changer le fichier de configuration avec un simple éditeur de texte
- si l'utilisateur veut changer les valeurs à la main, les entrées doivent être très explicites pour déterminer leur utilisation
- pour les entrées qui doivent être enregistrées mais qui sont aussi critiques en terme de sécurité comme les mots de passe, vous devez chercher une solution propre pour garantir la sécurité.

Maintenant que nous connaissons les bases, nous allons analyser le code. Comme cela a été dit, nous avons juste à utiliser notre pointeur de configuration pour accéder aux valeurs. D'abord, à la ligne 4, nous définissons le groupe courant à "General Options". Cela indique que les valeurs utilisées sont des attributs relativement globaux pour l'application. Ensuite, nous lisons les valeurs pour la barre d'outils et la barre d'état - elles doivent être enregistrées lorsque l'application est fermée afin de pouvoir restaurer leur état quand l'utilisateur redémarre le programme. Comme les barres peuvent seulement être actives ou inactives, nous utilisons une valeur booléenne, c'est pourquoi notre méthode est `readBoolEntry()`. Le processus est identique pour les deux barres donc nous considérerons seulement les lignes 7-10 pour regarder ce qui se passe avec la barre d'outils. D'abord, nous lisons la valeur dans une variable temporaire `bViewToolbar` à la ligne 7. Dans le fichier, le nom de la valeur est "Show Toolbar" et, si la valeur n'est pas présente (ce qui peut être le cas lors du premier démarrage de l'application), la valeur par défaut est définie à `true`. Ensuite, nous positionnons la coche de l'entrée de menu pour (dés)activer la barre d'outils suivant sa valeur : nous appelons `setItemChecked()` pour le menu de la vue, avec l'entrée `ID_VIEW_TOOLBAR` et notre attribut. Enfin, nous faisons en sorte que la barre d'outils utilise cette valeur. Par défaut, la barre d'outils est visible donc nous devons faire quelque chose seulement si `bViewToolbar` vaut `false`. Avec `enableToolBar()` (ligne 10), nous forçons la barre à se cacher si elle est désactivée.

Ensuite, nous devons lire la position de la barre. Comme l'utilisateur peut avoir changé la position de la barre en glissant la barre avec la souris vers une autre zone de la vue, ceci doit aussi être enregistré afin de pouvoir restaurer son état. En regardant les classes `KToolBar` et `KMenuBar`, nous voyons que la position des barres peut valoir :

> `

>     enum BarPosition {Top, Left, Bottom, Right, Floating, Flat}

> `

Comme cette valeur doit être écrite sous forme numérique, nous devons la lire avec `readNumEntry()` et la convertir en une valeur de position. Avec `setMenuBarPos()` et `setBarPos()`, nous disons aux barres de s'afficher.

Vous avez aussi probablement noté que le menu "Fichier" contient un menu pour les fichiers récemment utilisés. Les noms de ces fichiers sont stockés dans une liste de chaînes de caractères qui doit être enregistrée lors de la fermeture de l'application et maintenant, nous devons les lire pour restaurer le menu. D'abord, nous initialisons la liste avec les entrées enregistrées en utilisant `readListEntry()`. Ensuite, dans une boucle `for`, nous créons une entrée de menu pour chaque élément de la liste.

Enfin, nous devons juste prendre en compte la géométrie de notre fenêtre. Nous lisons son apparence avec une variable `QSize` contenant une valeur x et y pour la largeur et la hauteur de la fenêtre. Comme la fenêtre est initialisée par `KTMainWindow`, nous n'avons pas besoin de nous soucier de la valeur par défaut et nous utiliserons `resize()` seulement si l'entrée n'est pas vide.

Ce qui reste à expliquer dans la construction de l'application est que nous devons initialement désactiver les commandes utilisateur qui ne seraient pas disponibles si certaines instances ne correspondent pas aux critères requis. Ce sont l'enregistrement de fichier et les opérations qui utilisent le presse-papiers. Pendant la vie de l'application, nous devons y faire attention à plusieurs reprises mais cela reste vraiment simple. L'architecture nous donne seulement deux méthodes pour activer/désactiver la barre de menus et les éléments de la barre d'outils avec un seul appel à la méthode à la fois.

#### Exécution

Au long de la section précédente, nous avons seulement surveillé ce qui se passe pendant l'appel au constructeur de notre instance de `KScribbleApp` qui nous donne la fenêtre principale. Après le retour à la fonction `main()`, nous devons appeler `show()` pour afficher la fenêtre. Ici, ce qui est différent d'une `KApplication` ou d'une `QApplication`, c'est que nous utilisons `KTMainWindow` comme instance de notre widget principal ; nous n'avons pas besoin de le définir avec `setMainWidget()`. Cela est fait par `KTMainWindow` lui-même et nous n'avons pas besoin de nous en soucier. La seule chose restante est d'interpréter la ligne de commande. Nous lisons la ligne de commande et testons si `int argc` est > 1, ce qui indique que l'utilisateur a lancé notre application avec `kscribble nom_de_fichier_a_ouvrir`. Notre fenêtre est alors sensée ouvrir le fichier ayant ce nom et appeler `openDocumentFile()` avec le nom du fichier.

La dernière ligne de la fonction `main()` fait le travail attendu : elle exécute l'instance de l'application et le programme entre dans la boucle d'événements.

Dans la section La Fonction main(), nous avons commencé à séparer le processus d'exécution par `if( app.isRestored() )` et décrit le processus de lancement habituel. La suite vous donne maintenant une introduction à la gestion de sessions et comment notre application l'utilise.

#### Lancement par Gestion de Sessions

Comme nous l'avons dit, la fonction `main()` teste si l'application a été lancée par le gestionnaire de sessions. Le gestionnaire de sessions est responsable de l'enregistrement de l'état actuel de toutes les fenêtres ouvertes de l'application sur le bureau de l'utilisateur et doit les restaurer quand l'utilisateur se connecte à nouveau, ce qui signifie que l'application n'est pas lancée par l'utilisateur mais automatiquement exécutée. La partie du code qui est exécutée était :

> `

>     6    if (app.isRestored())
>     7    {
>     8       RESTORE(KScribbleApp);
>     9    }

> `

Dans La Fonction main(), nous avons indiqué que nous testons le type de lancement en interrogeant `app.isRestored()`. Ensuite, la ligne 8 est exécutée. Cela ressemble à quelque chose de simple mais, en fait, il en résulte un processus d'exécution complexe que nous allons suivre dans cette section.

RESTORE(), quant à elle, est une macro fournie par `KTMainWindow`. Elle est remplacée par le code suivant :

> `

>     if (app.isRestored()){
>       int n = 1;
>       while (KTMainWindow::canBeRestored(n)){
>             (new KScribbleApp)->restore(n);
>             n++;
>       }
>     }

> `

Cela restaurera toutes les fenêtres de l'application de la classe `KScribbleApp` en créant les instances et en appelant `restore()` pour la nouvelle fenêtre. Il est important de comprendre que si votre application utilise plusieurs widgets différents qui héritent de `KTMainWindow`, vous devez remplacer la macro et déterminer le type des widgets principaux (NdT : top widgets) en utilisant `KTMainWindow::classNameOfToplevel(n)` au lieu de la classe `KScribbleApp`.

La méthode `restore()` lit ensuite la partie du fichier de session qui contient les informations concernant la fenêtre. Comme `KTMainWindow` enregistre tout cela pour nous, nous n'avons rien de spécial à faire. Ensuite, seules les informations spécifiques à notre instance de `KScribbleApp` doivent être retrouvées. Souvent, c'est un fichier temporaire que nous avons créé pour enregistrer le document ou une autre initialisation qui est requise. Pour obtenir ces informations de restauration, nous devons seulement réécrire deux méthodes virtuelles de `KTMainWindow`, `saveProperties()` et `readProperties()`. Les informations que nous devons enregistrer pour la session sont si le fichier actuellement ouvert est modifié ou non et le nom du fichier. Si le fichier est modifié, nous obtiendrons un fichier temporaire pour l'enregistrer. Au début de la session, ces informations sont utilisées pour restaurer le contenu du document :

> `

>     void KScribbleApp::readProperties(KConfig*)
>     {
>       QString filename = config->readEntry("filename","");
>       bool modified = config->readBoolEntry("modified",false);
>       if( modified ){
>             bool b_canRecover;
>             QString tempname = kapp->checkRecoverFile(filename,b_canRecover);

>             if(b_canRecover){
>               doc->openDocument(tempname);
>               doc->setModified();
>               QFileInfo info(filename);
>               doc->pathName(info.absFilePath());
>               doc->title(info.fileName());
>               QFile::remove(tempname);
>             }
>       }
>       else if(!filename.isEmpty()){
>       doc->openDocument(filename);
>       }
>       setCaption(kapp->appName()+": "+doc->getTitle());
>     }

> `

Ici, la ligne `kapp->checkRecoverFile()` paraît un peu étrange car `b_canRecover` n'est pas initialisé. Cela est fait par la méthode qui le met à `true`, s'il y a un fichier de restauration.

Comme nous avons seulement enregistré un document dans un fichier de restauration (NdT : recover file) s'il était modifié, nous positionnons le bit de modification pour indiquer que les informations n'ont pas été enregistrées dans le fichier original. Nous devons aussi faire attention au fait que le fichier de restauration possède un nom de fichier différent du fichier original qui était ouvert. C'est pourquoi, nous devons réinitialiser le nom de fichier et le chemin à l'ancien nom de fichier. Enfin, nous avons les informations que nous voulions restaurer et nous pouvons supprimer le fichier temporaire du gestionnaire de sessions.

Résumé :

Tout au long de ce chapitre, vous avez appris comment l'application est lancée, soit par un appel normal de l'utilisateur, soit par le gestionnaire de sessions. Nous avons parcouru le code pour apprendre comment les parties de l'interface visuelle de l'application sont construites et comment initialiser les attributs grâce aux entrées du fichier de configuration. Maintenant, vous pouvez exécuter l'application pour tester ces fonctions et voir comment la fenêtre du programme réagit.

### 3.4 Contenu Supplémentaire sur les Projets KDevelop

Au-delà du code source fourni, les projets KDevelop contiennent beaucoup de choses supplémentaires qui intéresseront le développeur. Parmi elles, on trouve :

- une icône de programme
- une mini-icône de programme
- un fichier .kdelnk
- un échantillon de fichier de documentation SGML
- un ensemble de documentations de l'API générées par les sources de l'architecture.

Mise à part la documentation de l'API, ces éléments du projet seront installés ensemble avec le binaire de l'application. Comme l'architecture de projet doit être aussi ouverte que possible, vous devrez adapter ces éléments aux objectifs de votre projet. Premièrement, vous devez éditer les icônes fournies. Cela donnera à votre application un indentifiant unique par lequel l'utilisateur peut déterminer visuellement votre application dans les menus du gestionnaire de fenêtres. Le fichier .kdelnk est un fichier qui installe votre application dans le menu `Applications` de `kpanel` (NdT : le tableau de bord de KDE). Il devra être édité pour définir le chemin d'installation que nous détaillerons ultérieurement dans ce manuel. Enfin, la documentation que vous voudrez fournir à l'utilisateur est écrite en SGML. Cela permet de créer très facilement des formats de sortie différents à partir de la même source. Par défaut, KDevelop propose de créer un ensemble de fichiers HTML à partir de cette source ; pour les projets KDE, il utilisera automatiquement le programme `ksgml2html` pour donner une apparence homogène, dans le style de KDE, à la documentation. Dans une section ultérieure, nous verrons comment éditer les sources SGML et ce qu'il faut faire pour l'installation vers l'utilisateur final.

Enfin, la documentation de l'API (Application Programming Interface) vous permet, ainsi qu'aux autres développeurs, d'aller rapidement dans le code et d'utiliser les classes sans avoir à deviner la finalité de chacune d'elles. Nous apprendrons comment étendre la documentation de l'API dans une étape ultérieure ; pour l'instant, il suffit de savoir que la documentation est générée par le programme `KDoc` qui traite les fichiers d'en-tête et crée les fichiers HTML, c'est pourquoi toute la documentation est placée dans les en-têtes.

Page suivante Page précédente Table des matières