>La bibliothèque tdecore est l'environnement d'applications de base de tout programme basé sur KDE. Elle fournit l'accès au système de configuration, la gestion de la ligne de commande, le chargement et la manipulation des icônes, certains types spéciaux de communication entre processus, la gestion des fichiers et divers autres utilitaires. </para
> fournit de nombreux widgets et boîtes de dialogue standard dont Qt ne dispose pas ou qui ont davantage de fonctionnalités que leurs contreparties Qt. Elle contient également plusieurs widgets qui sont sous-classés à partir de ceux de Qt et sont mieux intégrés au bureau KDE en respectant les préférences de l'utilisateur. </para
> contient des ressources pour les entrées/sorties asynchrones, transparentes vis-à-vis du réseau et un accès à la gestion des types mime. Elle fournit aussi les boîtes de dialogue des fichiers KDE et ses classes d'aide. </para
></listitem>
</varlistentry>
<varlistentry>
<term
><ulink url="kdeapi:kjs/index.html"
>kjs</ulink
></term>
<listitem
><para
>La bibliothèque <literal
>kjs</literal
> fournit une implémentation de JavaScript. </para
>Traitement des arguments en ligne de commande. </para>
</formalpara
></listitem>
</itemizedlist>
<para
>Paramètres de configuration—accès à la base de données de configuration hiérarchique de KDE, aux réglages globaux et aux ressources des applications. </para>
>Un fragment d'interface graphique se composant d'une collection d'actions et d'une arborescence DOM représentant leur emplacement dans l'interface graphique. </para>
>Le modèle d'imagerie bas niveau de Qt est basé sur les possibilités fournies par X11 et d'autres systèmes de fenêtrage pour lesquels des ports Qt existent. Mais il les étend également en implémentant des fonctionnalités additionnelles telles que les transformations affines arbitraires pour le texte et les pixmaps. </para>
<para
>La classe graphique centrale pour le dessin en 2D avec Qt est <ulink url="kdeapi:qt/QPainter"
>QPainter</ulink
>. Elle peut dessiner sur un <ulink url="kdeapi:qt/QPaintDevice"
>QPaintDevice</ulink
>. Il y a trois périphériques de dessin possibles implémentés: l'un est <ulink url="kdeapi:qt/QWidget"
>QWidget</ulink
> qui représente un widget sur l'écran. Le deuxième est <ulink url="kdeapi:qt/QPrinter"
>QPrinter</ulink
> qui représente une imprimante et produit une sortie Postscript. Le troisième est la classe <ulink url="kdeapi:qt/QPicture"
>QPicture</ulink
> qui enregistre les commandes de dessin, peut les enregistrer sur disque et les lire plus tard. Un format possible de stockage pour le dessin est le standard SVG du W3C. </para>
<para
>Il est donc possible de réutiliser le code de rendu pour afficher un widget pour l'impression, avec les mêmes fonctionnalités prises en charge. Bien sûr, en pratique, le code est utilisé dans un contexte légèrement différent. Le dessin sur un widget est presque exclusivement exécuté dans la méthode paintEvent() d'une classe de widget. </para>
<programlisting
>void FooWidget::paintEvent()
{
QPainter p(this);
// Configurer le pinceau
// Utiliser le pinceau
}
</programlisting>
<para
>En dessinant sur une imprimante, vous devez veiller à employer QPrinter::newPage() pour terminer une page et en commencer une nouvelle—chose qui naturellement n'a rien de pertinent lorsqu'il s'agit de dessiner des widgets. De plus, au moment de l'impression vous pouvez être amené à employer la <ulink url="kdeapi:qt/QPaintDeviceMetrics"
>métrique du périphérique</ulink
> afin d'en calculer les coordonnées. </para>
</simplesect>
<simplesect id="qpainter-transformations">
<title
>Transformations</title>
<para
>Lorsqu'on se sert de QPainter, ce dernier trace par défaut le système de coordonnées naturel du périphérique utilisé. Cela signifie que si vous dessinez le long de l'axe horizontal une ligne d'une longueur de 10unités, elle sera tracée sur l'écran comme une ligne horizontale d'une longueur de 10pixels. Cependant, QPainter peut appliquer des transformations affines arbitraires avant de véritablement rendre les formes et les courbes. Une transformation affine met en correspondance les coordonnées x et y linéairement en x' et y' en conséquence </para>
>La matrice 3x3 dans cette équation peut être définie avec QPainter::setWorldMatrix() et elle est de type <ulink url="kdeapi:qt/QWMatrix"
>QWMatrix</ulink
>. Normalement, il s'agit de la matrice identité, &cad; que m11 et m22 sont égales à un, et les autres paramètres sont nuls. Il y a essentiellement trois groupes différents de transformations: </para>
<itemizedlist>
<listitem
><formalpara>
<title
>Translations</title>
<para
>Celles-ci déplacent tous les points d'un objet d'une quantité fixe dans une certaine direction. Une matrice de translation peut être obtenue en appelant la méthode m.translate(dx, dy) pour une QWMatrix. Ceci correspond à la matrice </para>
>Celle-ci étire ou rétrécit les coordonnées d'un objet, en le rendant plus gros ou plus petit sans le distordre. Une transformation de changement d'échelle peut être appliquée à une QWMatrix en appelant m.scale(sx, sy). Ceci correspond à la matrice </para>
>En attribuant à l'un de ces paramètres une valeur négative, on peut réaliser une mise en miroir du système de coordonnées. </para>
</listitem>
<listitem
><formalpara>
<title
>Glissement</title>
<para
>Une distorsion du système de coordonnées avec deux paramètres. Une transformation de glissement peut être appliquée en appelant m.shear(sh, sv), correspondant à la matrice </para>
>Celle-ci fait tourner un objet. Une transformation de rotation peut être appliquée en appelant m.rotate(alpha). Notez que l'angle doit être indiqué en degrés, non sous un angle mathématique! La matrice correspondante est </para>
>Les transformations peuvent être combinées en multipliant les matrices élémentaires. Notez que les opérations sur les matrices ne sont pas commutatives en général et, par conséquent, l'effet combiné d'une concaténation dépend de l'ordre dans lequel les matrices sont multipliées. </para>
</simplesect>
<simplesect id="qpainter-strokeattributes">
<title
>Définition des attributs de frappe</title>
<para
>Le rendu des lignes, courbes et contours des polygones peut être modifié en définissant un crayon spécial avec QPainter::setPen(). L'argument de cette fonction est un objet <ulink url="kdeapi:qt/QPen"
>QPen</ulink
>. Les propriétés qui y sont enregistrées sont un style, une couleur, un style de jointure et un style de capuchon. </para>
<para
>Le style de crayon est un membre de l'énumération <ulink url="kdeapi:qt/Qt#PenStyle-enum"
>Qt::PenStyle</ulink
>, et peut prendre une des valeurs suivantes : </para>
>Le style de jointure est un membre de l'énumération <ulink url="kdeapi:qt/Qt#PenJoinStyle-enum"
>Qt::PenJoinStyle</ulink
>. Il spécifie comment est tracée la jonction entre des lignes mutliples qui sont reliées l'une à l'autre. Il peut prendre une des valeurs suivantes: </para>
>Le style de remplissage des polygones, cercles ou rectangles peut être modifié en définissant une brosse spéciale avec QPainter::setBrush(). Cette fonction prend un objet <ulink url="kdeapi:qt/QBrush"
>QBrush</ulink
> comme argument. Les brosses peuvent être construites de quatre manières différentes: </para>
<itemizedlist>
<listitem>
<para
>QBrush::QBrush()—crée une brosse qui ne remplit pas les formes.</para>
</listitem>
<listitem>
<para
>QBrush::QBrush(BrushStyle)—crée une brosse noire avec un des motifs par défaut illustrés ci-dessous.</para>
</listitem>
<listitem>
<para
>QBrush::QBrush(const QColor &, BrushStyle)—crée une brosse colorée avec un des motifs illustrés ci-dessous.</para>
</listitem>
<listitem>
<para
>QBrush::QBrush(const QColor &, const QPixmap)—crée une brosse colorée avec le motif personnalisé que vous indiquez comme second paramètre.</para>
</listitem>
</itemizedlist>
<para
>Un style de brosse par défaut est un membre de l'énumération <ulink url="kdeapi:qt/Qt#BrushStyle-enum"
>Qt::BrushStyle</ulink
>. Voici une illustration de tous les motifs prédéfinis: </para>
>Une autre manière de personnaliser le comportement de la brosse est d'utiliser la fonction QPainter::setBrushOrigin(). </para>
</simplesect>
<simplesect id="qpainter-color">
<title
>Couleur</title>
<para
>Les couleurs jouent un rôle à la fois lors de la frappe des courbes et lors du remplissage des formes. Dans Qt, les couleurs sont représentées par la classe <ulink url="kdeapi:qt/QColor"
>QColor</ulink
>. Qt ne prend en charge aucune fonctionnalité graphique avancée comme les profils de couleur ICC (<emphasis
>International Color Consortium</emphasis
>) et la correction des couleurs. Les couleurs sont habituellement construites en spécifiant leurs composantes rouge, verte et bleue, puisque le modèle RVB est la manière dont sont composés les pixels sur un moniteur. </para>
<para
>Il est également possible d'utiliser une teinte, une saturation et une valeur. Cette représentation HSV est ce dont vous vous servez dans la boîte des couleurs Gtk, &pex; dans Le GIMP. Ici, la teinte correspond à l'angle sur la roue de couleurs, alors que la saturation correspond à la distance depuis le centre du cercle. La valeur peut être choisie avec un curseur séparé. </para>
</simplesect>
<simplesect id="qpainter-paintsettings">
<title
>Autres paramètres</title>
<para
>Normalement, lorsque vous peignez sur un périphérique de peinture, les pixels que vous dessinez remplacent ceux qui s'y trouvaient auparavant. Cela signifie que lorsque vous peignez une certaine région avec une couleur rouge et que vous peignez la même région avec une couleur verte par la suite, seule la couleur bleue sera visible. Le modèle d'imagerie de Qt ne prend pas en charge la transparence, &cad; une manière de fondre l'avant-plan peint avec l'arrière-plan. Cependant, il y a un moyen simple de combiner arrière-plan et avant-plan avec des opérateurs booléens. La méthode QPainter::setRasterOp() définit l'opérateur utilisé, qui provient de l'énumération <ulink url="kdeapi:qt/Qt#RasterOp-enum"
>RasterOp</ulink
>. </para>
<para
>La valeur par défaut est CopyROP, qui ignore l'arrière-plan. Un autre choix courant est XorROP. Si vous tracez une ligne noire avec cet opérateur sur une image colorée, la zone couverte est alors inversée. Cet effet est par exemple permet de créer les sélections d'étirement dans les programmes de manipulation d'image connus sous l'expression «fourmis en marche». </para>
</simplesect>
<simplesect id="qpainter-primitives">
<title
>Traçage des primitives graphiques</title>
<para
>Dans ce qui suit, nous répertorions les éléments graphiques élémentaires que gère QPainter. La plupart d'entre eux existe en plusieurs versions saturées qui prennent un nombre différent d'arguments. Par exemple, les méthodes qui portent sur les rectangles prennent soit un <ulink url="kdeapi:qt/QRect"
>QRect</ulink
> comme argument, soit un ensemble de quatre entiers. </para>
<itemizedlist>
<listitem>
<para
>Traçage d'un seul point—drawPoint().</para>
</listitem>
<listitem>
<para
>Traçage des lignes—drawLine(), drawLineSegments() et drawPolyLine().</para>
</listitem>
<listitem>
<para
>Traçage et remplissage des rectangles—drawRect(), drawRoundRect(), fillRect() et eraseRect().</para>
</listitem>
<listitem>
<para
>Traçage et remplissage des cercles, des ellipses et de parties de ceux-ci—drawEllipse(), drawArc(), drawPie et drawChord().</para>
</listitem>
<listitem>
<para
>Traçage et remplissage des polygones en général—drawPolygon().</para>
</listitem>
<listitem>
<para
>Traçage des courbes de Bézier—drawQuadBezier() [drawCubicBezier dans Qt3.0].</para>
</listitem>
</itemizedlist>
</simplesect>
<simplesect id="qpainter-pixmaps">
<title
>Traçage des pixmaps et des images</title>
<para
>Qt fournit deux classes très différentes pour représenter les images. </para>
<para
><ulink url="kdeapi:qt/QPixmap"
>QPixmap</ulink
> corrrespond directement aux objets pixmaps dans X11. Les pixmaps sont des objets côté serveur et peuvent—sur une carte graphique moderne—même être enregistrés directement dans la mémoire de la carte. Ce comportement la rend <emphasis
>très</emphasis
> efficace pour transférer les pixmaps à l'écran. Les pixmaps agissent aussi comme l'équivalent d'un hors-écran de widgets—la classe QPixmap étant une sous-classe de QPaintDevice, vous pouvez dessiner dessus avec un QPainter. Les opérations de dessin élémentaires sont habituellement accélérées par les cartes graphiques modernes. Par conséquent, un motif d'usage courant est d'utiliser les pixmaps pour le double tamponnement. Cela signifie que, au lieu de peindre directement sur un widget, vous peignez sur un objet pixmap temporaire et que vous employez la fonction <ulink url="kdeapi:qt/QPaintDevice#bitBlt-1"
>bitBlt</ulink
> pour transférer le pixmap au widget. Pour des retraçages complexes, cette astuce permet d'éviter le papillottement. </para>
<para
>En revanche, les objets <ulink url="kdeapi:qt/QImage"
>QImage</ulink
> résident côté client. Ils se distinguent en fournissant un accès direct aux pixels de l'image. Ce comportement explique leur utilisation dans la manipulation des images, ainsi que les tâches comme le chargement et l'enregistrement sur disque (la méthode load() de QPixmap considère QImage comme une étape intermédiaire). Par ailleurs, le traçage d'une image sur un widget est une opération relativement coûteuse car elle implique un transfert vers le serveur X qui peut prendre du temps, en particulier pour les images de grandes dimensions et pour les serveurs distants. En fonction de la profondeur de couleur, la conversion de QImage en QPixmap peut aussi exiger un tramage. </para>
</simplesect>
<simplesect id="qpainter-drawingtext">
<title
>Traçage du texte</title>
<para
>Le texte peut être tracé avec une des variantes saturées de la méthode QPainter::drawText(). Celles-ci dessinent une QString soit à un point donné, soit dans un rectangle donné, en utilisant la police définie par QPainter::setFont(). Il y a également un paramètre qui prend une combinaison OU exclusif de certaines drapeaux à partir des énumérations <ulink url="kdeapi:qt/Qt#AlignmentFlags-enum"
>Qt::AlignmentFlags</ulink
> et <ulink url="kdeapi:qt/Qt#TextFlags-enum"
>Qt::TextFlags</ulink
> </para>
<para
>En commençant par la version3.0, Qt tient compte de la disposition complète du texte, même pour les langues qui s'écrivent de droite à gauche. </para>
<para
>Une manière plus sophistiquée d'afficher du texte marqué est la classe <ulink url="kdeapi:qt/QSimpleRichText"
>QSimpleRichText</ulink
>. Les objets de cette classe peuvent être construits avec un élément de texte à l'aide d'un sous-ensemble des marqueurs HTML, qui est assez riche et fournit même des tableaux. Le style du texte peut être personnalisé par l'emploi d'une <ulink url="kdeapi/qt/QStyleSheet"
>QStyleSheet</ulink
> (la documentation des marqueurs se trouve également ici). Une fois l'objet texte enrichi construit, il peut être rendu sur un widget ou un autre périphérique de traçage avec la méthode QSimpleRichText::draw(). </para>
</simplesect>
</sect1>
<sect1 id="graphics-qcanvas">
<title
>Graphiques structurés avec QCanvas</title>
<para
>QPainter offre un modèle d'imagerie puissant pour peindre sur les widgets et les pixmaps. Toutefois, son utilisation peut être fastidieuse. Chaque fois que votre widget reçoit un événement peinture, il doit analyser la QPaintEvent::region() ou la QPaintEvent::rect() qui doit être redessinée. Puis il lui faut configurer un QPainter et peindre tous les objets qui se chevauchent sur cette région. Imaginez par exemple un programme de dessin vectoriel qui permet de faire glisser des objets comme les polygones, les cercles et les groupes de ceux-ci tout autour. Chaque fois que ces objets se déplacent un peu, l'événement souris du widget déclenche un événement peinture pour l'ensemble de la zone couverte par les objets dans leur ancienne position et dans leur nouvelle position. La découverte des retraçages nécessaires et leur exécution d'une manière efficace peut être difficile et peut entraîner un conflit avec la structure orientée objet du code source du programme. </para>
<para
>À titre d'alternative, Qt contient la classe <ulink url="kdeapi:qt/QCanvas"
>QCanvas</ulink
> dans laquelle vous placerez des objets graphiques comme les polygones, le texte, les pixmaps. Vous pouvez également fournir des éléments additionnels en sous-classant <ulink url="kdeapi:qt/QCanvasItem"
>QCanvasItem</ulink
> ou une de ses sous-classes plus spécialisées. Un canevas peut être représenté sur l'écran par un ou plusieurs widgets de la classe <ulink url="kdeapi:qt/QCanvas"
>QCanvasView</ulink
> que vous devez sous-classer afin de gérer les interactions utilisateur. Qt tient compte de tous les retraçages des objets de la vue, si elles sont occasionnées par le widget exposé, les nouveaux objets créés ou modifiés, voire d'autres choses. En utilisant le double tamponnement, ceci peut être effectué d'une manière efficace et sans papillotement. </para>
<para
>Les éléments du canevas peuvent se chevaucher les uns les autres. Dans ce cas, celui qui est visible dépend de l'ordre que QCanvasItem::setZ() peut affecter. Les éléments peuvent aussi être rendus visibles ou invisibles. Vous pouvez également fournir un arrière-plan à dessiner «derrière» tous les éléments et un avant-plan. Pour associer des événements souris à des objets, dans le canevas, il y a la méthode QCanvas::collisions() qui retourne une liste des éléments se chevauchant à un point donné. Voici une capture d'écran d'une vue du canevas en action: </para>
<mediaobject>
<imageobject
><imagedata fileref="canvas.png"/></imageobject>
</mediaobject>
<para
>Ici, le maillage est dessiné en arrière-plan. De plus, il y a un élément QCanvasText et un QCanvasPolygon violet. Le papillon est un QCanvasPixmap. Il a des zones transparentes de façon à ce que vous puissiez voir les éléments sous-jacents à travers lui. </para>
<para
>Un didactitiel sur l'utilisation de QCanvas pour écrire des jeux basés sur des objets images se trouve <ulink url="http://zez.org/article/articleview/2/1/"
>ici</ulink
>. </para>
</sect1>
<sect1 id="graphics-qglwidget">
<title
>Graphiques en 3D avec OpenGL</title>
<simplesect id="qglwidget-lowlevel">
<title
>Interface bas niveau</title>
<para
>Le standard <emphasis
>de facto</emphasis
> pour le rendu des graphiques en 3D aujourd'hui est <ulink url="http://www.opengl.org"
>OpenGL</ulink
>. Les implémentations de ces spécifications sont présentes dans MicrosoftWindows, MacOSX, XFree86 et gèrent souvent les fonctionnalités d'accélération matérielle qu'offrent les cartes graphiques modernes. OpenGL lui-même ne se consacre qu'au rendu sur une zone spécifiée du tampon de trame grâce à un <emphasis
>contexte GL</emphasis
> et n'a aucune interaction avec la boîte à outils de l'environnement </para>
<para
>Qt offre le widget <ulink url="kdeapi:qt/QGLWidget"
>QGLWidget</ulink
> qui encapsule une fenêtre avec un contexte GL associé. Vous l'utiliserez essentiellement en le sous-classant et en réimplémentant certaines méthodes. </para>
<itemizedlist>
<listitem
><para
>Au lieu de réimplémenter paintEvent() et d'utiliser QPainter pour dessiner le contenu du widget, annulez paintGL() et utilisez les commandes GL pour rendre une scène. QLWidget prendra soin de faire de son contexte GL le contexte actuel avant que paintGL() ne soit appelé, et il l'éliminera par la suite. </para
></listitem>
<listitem
><para
>La méthode virtuelle initializeGL() est appelée immédiatement avant la première fois où resizeGL() ou paintGL() est appelées. Elle peut servir à construire des listes d'affichage pour des objets et procéder à quelques initialisations. </para
></listitem>
<listitem
><para
>Au lieu de réimplémenter resizeEvent(), annulez resizeGL(). Cette dernière peut servir pour définir la fenêtre d'affichage de manière appropriée. </para
></listitem>
<listitem
><para
>Au lieu d'appeler update() quand l'état de la scène a changé—&pex; quand vous l'animez avec un minuteur—appelez updateGL(). Cette action déclenchera un retraçage. </para
></listitem>
</itemizedlist>
<para
>En général, QGLWidget se comporte tout comme n'importe quel autre widget, &cad; &pex; que vous pouvez traiter les événements souris comme d'habitude, redimensionner le widget et le combiner avec d'autres dans une topologie. </para>
<mediaobject>
<imageobject
><imagedata fileref="opengl.png"/></imageobject>
</mediaobject>
<para
>Qt contient quelques exemples de l'utilisation de QGLWidget dans son exemple <literal
> un ensemble de didactitiels; d'autres informations et une référence d'OpenGL sont disponibles sur la <ulink url="http://www.opengl.org"
>page d'accueil d'OpenGL</ulink
>. </para>
</simplesect>
<simplesect id="qglwidget-highlevel">
<title
>Interfaces haut niveau</title>
<para
>OpenGL est une interface assez bas niveau pour dessiner des graphiques en 3D. De la même manière que QCanvas donne au programmeur une interface de plus haut niveau avec des détails, des objets et leurs propriétés, il y a également des interfaces haut niveau pour les graphiques en 3D. Une des interfaces les plus connues est OpenInventor. Technologie à l'origine développée par SGI, il existe aujourd'hui l'implémentation opensource <ulink url="http://www.coin3d.org"
>Coin</ulink
>, complétée par l'association d'une boîte à outils à Qt appelée SoQt. </para>
<para
>Le concept fondamental d'OpenInventor est celui d'une <emphasis
>scène</emphasis
>. Une scène peut être chargée depuis un disque et enregistrée dans un format spécial étroitement lié à <ulink url="http://www.vrml.org"
>VRML</ulink
>. Une scène consiste en une collection d'objets appelés <emphasis
>nœuds</emphasis
>. Inventor fournit déjà une riche collection de nœuds réutilisables, tels que des cubes, des cylindres et des mailles, en plus de sources lumineuses, de matériaux, de caméras, &etc; Les nœuds sont représentés par des classes C++ et peuvent être combinés et sous-classés. </para>
<para
>Vous trouverez une introduction à Inventor <ulink url="http://www.motifzone.com/tmd/articles/OpenInventor/OpenInventor.html"
>ici</ulink
> (en général, vous pouvez substituer toutes les mentions de SoXt par SoQt dans cet article). </para>
</simplesect>
</sect1>
</chapter>
<chapter id="userinterface">
<title
>Interface utilisateur</title>
<sect1 id="userinterface-actionpattern">
<title
>Le motif de l'action</title>
<para
></para>
</sect1>
<sect1 id="userinterface-xmlgui">
<title
>Définition des menus et des barres d'outils dans XML</title>
<simplesect id="xmlgui-intro">
<title
>Introduction</title>
<para
>Alors que le <link linkend="userinterface-actionpattern"
>motif des actions</link
> permet d'encapsuler les actions déclenchées par l'utilisateur dans un objet qui peut être «enfiché» quelque part dans les barres de menus ou les barres d'outils, il ne résoud pas par lui-même le problème de la construction des menus proprement dits. En particulier, vous aurez à construire tous les menus contextuels en code C++ et à insérer explicitement les actions dans un certain ordre, à l'étude du guide de style pour les actions standard. Ceci complique la tâche de l'utilisateur pour personnaliser les menus ou changer les raccourcis pour les adapter à ses besoins, sans modifier le code source. </para>
<para
>Ce problème est résolu par un ensemble de classes appelé <literal
>XMLGUI</literal
>. En susbtance, celui-ci sépare les actions (codées en C++) de leur apparance dans les barres de menus et les barres d'outils (codées en XML). Sans modifier aucun code source, les menus peuvent être simplement personnalisés en ajustant un fichier XML. En outre, il permet de s'assurer que les actions standard (telles que <menuchoice
><guimenu
>Fichier</guimenu
><guimenuitem
>Ouvrir</guimenuitem
></menuchoice
> ou <menuchoice
><guimenu
>Aide</guimenu
><guimenuitem
>À propos de</guimenuitem
></menuchoice
>) apparaissent dans les endroits suggérés par le guide de style. XMLGUI est particulièrement important pour les programmes modulaires, dans lesquels les éléments apparaissant dans la barre de menus peuvent provenir de nombreux modules externes (<emphasis
> et gère donc XMLGUI en dehors de l'ordinateur. Toutes les actions créées en son sein doivent avoir la <literal
>actionCollection()</literal
> du client comme parent. Un appel à <literal
>createGUI()</literal
> construira ensuite l'ensemble complet des barres de menus et d'outils défini dans le fichier XML des applications (par convention, avec le suffixe <literal
>ui.rc</literal
>). </para>
</simplesect>
<simplesect id="xmlgui-kviewexample">
<title
>Un exemple: un menu dans KView</title>
<para
>Dans ce qui suit, nous prenons l'afficheur d'images <application
>KView</application
> de KDE à titre d'exemple. Il a un fichier <literal
>ui.rc</literal
> nommé <filename
>kviewui.rc</filename
> qui est installé avec le fragment <filename
>Makefile.am</filename
> </para>
<programlisting
>rcdir = $(kde_datadir)/kview
rc_DATA = kviewui.rc
</programlisting>
<para
>Voici un extrait du fichier <filename
>kviewui.rc</filename
>. Pour la simplicité, nous n'afficherons que la définition du menu <guimenu
>Affichage</guimenu
>. </para>
<programlisting
><!DOCTYPE kpartgui>
<kpartgui name="kview">
<MenuBar>
<Menu name="affichage" >
<Action name="zoom50" />
<Action name="zoom100" />
<Action name="zoom200" />
<Action name="zoomMaxpect" />
<Separator/>
<Action name="plein écran" />
</Menu>
</MenuBar>
</kpartgui>
</programlisting>
<para
>La partie correspondante de la configuration en C++ est: </para>
>. L'élément le plus externe du fichier contient le nom d'instance de l'application comme attribut. Il peut également contenir un numéro de version de la forme «version=2». Ce détail est utile lorsque vous diffusez de nouvelles versions d'une application avec une structure de menu modifiée, &pex; avec davantage de fonctionnalités. Si vous gonflez le numéro de version du fichier <literal
>ui.rc</literal
>, KDE s'assure que toute version personnalisée du fichier est éliminée et que le nouveau fichier est utilisé à la place. </para>
<para
>La ligne suivante, <literal
><MenuBar></literal
>, contient une déclaration d'une barre de menus. Vous pouvez aussi insérer n'importe quel nombre de déclarations <literal
><ToolBar></literal
> afin de créer quelques barres d'outils. Le menu contient un sous-menu avec le nom «affichage». Ce nom est déjà prédéfini et ainsi, vous voyez une version traduite du mot « View» dans la capture d'écran. Si vous déclarez vos propres sous-menus, ajoutez le titre explicitement. Par exemple, <application
>KView</application
> a un sous-menu avec le titre « Image» déclaré comme suit: </para>
> de l'application pour que les traducteurs puissent le traiter. Notez que vous devez écrire le marqueur d'accélérateur « & » sous la forme conforme à XML, « &amp; ». </para>
<para
>Revenons à notre exemple. Le menu <guimenu
>Affichage</guimenu
> de <application
>KView</application
> contient quelques actions personnalisées: <literal
>zoom50</literal
>, <literal
>zoom100</literal
>, <literal
>zoom200</literal
>, <literal
>zoomMaxpect</literal
> et <literal
>fullscreen</literal
>, déclarées avec un élément <literal
><Action></literal
>. Le séparateur dans les captures d'écran correspond à l'élément <literal
><Separator></literal
>. </para>
<para
>Vous noterez que certains éléments de menus n'ont pas d'élément correspondant dans le fichier XML. Ce sont des <emphasis
>. Quand vous créez de telles actions dans votre application (comme dans l'exemple C++ ci-dessus), elles sont automatiquement insérées dans une position imposée et éventuellement avec une icône et une touche de raccourci. Consultez ces emplacements dans le fichier <filename
>La première chose que nous remarquons est qu'il y a beaucoup plus d'attributs que pour les barres de menus. Celles-ci comprennent: </para>
<itemizedlist>
<listitem
><para
><literal
>fullWidth</literal
>: indique à XMLGUI que la barre d'outils a la même largeur que la fenêtre de premier niveau. Si celle-ci est «false», la barre d'outils prend seulement l'espace nécessaire et les autres barres d'outils sont placées sur la même ligne. </para
></listitem>
<listitem
><para
><literal
>newline</literal
>: ceci est en rapport avec l'option ci-dessus. Si «newline» est «true», la barre d'outils commence une nouvelle ligne. Sinon elle peut être placée dans la même ligne, associée à la barre d'outils précédente. </para
></listitem>
<listitem
><para
><literal
>noEdit</literal
>: normalement, les barres d'outils peuvent être personnalisées par l'utilisateur, &pex; dans <menuchoice
><guimenu
>Configuration</guimenu
> <guimenuitem
>Configurer les barres d'outils</guimenuitem
></menuchoice
> dans <application
>Konqueror</application
>. Le fait de définir cette option à «true» marque cette barre d'outils comme non modifiable. Ceci est important pour les barres d'outils qui sont remplis d'éléments au moment de l'exécution, &pex; la barre d'outils des signets de <application
>Konqueror</application
>. </para
></listitem>
<listitem
><para
><literal
>iconText</literal
>: indique à XMLGUI d'afficher le texte de l'action qui suit l'icône. Normalement, le texte n'est affiché que sous la forme d'une bulle d'aide lorsque le curseur de la souris reste sur l'icône un instant. Des valeurs possibles pour cet attribut sont «icononly» (affiche uniquement l'icône), «textonly» (affiche uniquement le texte), «icontextright» (affiche le texte sur le côté droit de l'icône) et «icontextbottom» (affiche le texte au-dessous de l'icône). </para
></listitem>
<listitem
><para
><literal
>hidden</literal
>: si ceci est «true», la barre d'outils n'est pas visible initialement et doit être activée par un élément de menu. </para
></listitem>
<listitem
><para
><literal
>position</literal
>: la valeur par défaut pour cet attribut est «top», ce qui signifie que la barre d'outils est positionnée sous la barre de menus. Pour les programmes dotés de nombreux outils, comme les programmes graphiques, il peut être intéressant de remplacer ceci par «left», «right» ou «bottom». </para
></listitem>
</itemizedlist>
</simplesect>
<simplesect id="xmlgui-dynamical">
<title
>Menus dynamiques</title>
<para
>À l'évidence, un fichier XML ne peut contenir qu'une description statique d'une interface utilisateur. Souvent, il y a des menus qui changent au moment de l'exécution. Par exemple, le menu <guimenu
>Document</guimenu
> de <application
>Konqueror</application
> contient un ensemble d'éléments <guimenuitem
>Ouvrir avec quelque chose</guimenuitem
> avec les applications capables de charger un fichier avec un type MIME donné. Chaque fois que le document affiché change, la liste des éléments du menu est mise à jour. XMLGUI est préparée à gérer ce type de cas avec la notion de <emphasis
>listes d'actions</emphasis
>. Une liste d'actions est déclarée comme un seul élément dans le fichier XML mais se compose de plusieurs actions qui sont «enfichées» dans le menu au moment de l'exécution. L'exemple ci-dessus est mis en œuvre avec la déclaration suivante dans le fichier XML de <application
>Konqueror</application
>: </para>
<programlisting
><Menu name="filchier">
<text>&amp;Location</text>
...
<ActionList name="openwith">
...
</Menu>
</programlisting>
<para
>La fonction <function
>KXMLGUIClient::plugActionList()</function
> est alors utilisée pour ajouter des actions à afficher, alors que la fonction <function
>KXMLGuiClient::unplugActionList()</function
> supprime toutes les fonctions «enfichées». Voici à quoi ressemble la routine responsable de la mise à jour: </para>
<programlisting
>void MainWindow::updateOpenWithActions()
{
unplugActionList("openwith");
openWithActions.clear();
for ( /* itérer sur les services pertinents */ ) {
>Notez que, contrairement aux actions statiques, celles qui sont créées ici <emphasis
>ne sont pas</emphasis
> construites avec la collection des actions comme parent et que vous êtes responsable de leur suppression en propre. La manière la plus souple pour ce faire est d'utiliser <literal
>openWithActions.setAutoDelete(true)</literal
> dans l'exemple ci-dessus. </para>
</simplesect>
<simplesect id="xmlgui-contextmenus">
<title
>Menus contextuels</title>
<para
>Les exemples ci-dessus ne contenaient que des cas où étaient créées une barre de menus et des barres d'outils d'une fenêtre principale. Ici, les processus de construction de ces conteneurs vous sont complètement cachés derrière l'appel <function
>createGUI()</function
> (sauf si vous avez des conteneurs personnalisés). Toutefois, il y a des cas où vous serez amené à construire d'autres conteneurs et à les doter de définitions d'interfaces graphiques provenant du fichier XML. Les menus contextuels en sont un exemple. Afin d'obtenir un pointeur vers un menu contextuel, vous devez le demander à la fabrique du client: </para>
> utilisée précédemment examine le fichier XML pour savoir si elle y trouve un conteneur du nom donné. Ainsi, voici à quoi pourrait ressembler une définition possible: </para>
<programlisting
>...
<Menu name="context_popup">
<Action name="file_add"/>
<Action name="file_remove"/>
</Menu>
...
</programlisting>
</simplesect>
</sect1>
<sect1 id="help">
<title
>Mise à disposition d'une aide en ligne</title>
<para
>Le fait de rendre un programme facile et intuitif à utiliser implique une large palette de fonctions habituellement appelées «aide en ligne». L'aide en ligne a plusieurs buts, partiellement en conflit: d'une part, elle doit fournir à l'utilisateur des réponses à la question «Comment puis-je effectuer telle ou telle tâche? », de l'autre, elle doit l'aider à explorer l'application et à trouver les fonctionnalités qu'il ne connaît pas encore. Il est important de reconnaître que cet objectif peut être atteint en offrant plusieurs niveaux d'aide: </para>
<itemizedlist>
<listitem
><para
>Les bulles d'aide sont de petites étiquettes qui apparaissent sur les éléments de l'interface utilisateur quand la souris y reste quelques secondes. Elles sont particulièrement importantes pour les barres d'outils où les icônes ne sont pas toujours suffisantes pour expliquer la finalité d'un bouton. </para
></listitem>
<listitem
><para
>L'aide «Qu'est-ce que c'est? » est souvent une explication plus longue et plus fournie d'un widget ou d'un élément de menu. Elle est également plus incertaine à utiliser. Dans les boîtes de dialogue, on peut l'invoquer de deux façons: soit en appuyant sur <keycombo
><keycap
>Maj</keycap
><keycap
>F1</keycap
></keycombo
>, soit en cliquant sur le point d'interrogation dans la barre de titre (où la prise en charge de cette dernière dépend du gestionnaire de fenêtres). Le pointeur de la souris se transforme alors en point d'interrogation et la fenêtre d'aide apparaît quand on clique sur un élément de l'interface utilisateur. L'aide «Qu'est-ce que c'est? » est d'ordinaire activée dans la barre d'outils par un bouton contenant une flèche et un point d'interrogation. </para
></listitem>
<listitem
><para
>Le problème avec cette approche est que l'utilisateur ne peut pas voir si un widget fournit de l'aide ou non. Lorsqu'il active le bouton en forme de point d'interrogation et qu'il n'obtient aucune fenêtre d'aide en cliquant un élément de l'interface utilisateur, il est très rapidement frustré. </para>
<para
>L'avantage des fenêtres d'aide «Qu'est-ce que c'est? » telles qu'elles sont fournies par Qt et KDE est qu'elles peuvent contenir du <ulink url="kdeapi:qt/QStyleSheet"
>texte enrichi</ulink
>, &cad; différentes polices, du texte en gras et en italique, voire des images et des tableaux. </para>
<para
>Un exemple de l'aide «Qu'est-ce que c'est? » : </para>
>Pour finir, tous les programmes devraient avoir un manuel. Un manuel s'affiche en principe dans <application
>KHelpCenter</application
> en activant le menu <guimenu
>Aide</guimenu
>. Cela signifie qu'une application supplémentaire complète apparaît et distrait l'utilisateur de son travail. En conséquence, la consultation du manuel ne devrait être nécessaire que si d'autres fonctions comme les bulles d'aide et l'aide «Qu'est-ce que c'est? » ne sont pas suffisantes. Naturellement un manuel a l'avantage de ne pas décrire un seul aspect isolé de l'application dans un contexte plus spacieux. Les manuels de KDE sont écrits à l'aide du langage de marquage <ulink url="http://i18n.kde.org"
>DocBook</ulink
>. </para
></listitem>
</itemizedlist>
<para
>Du point de vue du programmeur, Qt fournit un moyen aisé d'utiliser l'API pour l'aide en ligne. Pour affecter une bulle d'aide à un widget, faites appel à la classe <ulink url="kdeapi:qt/QToolTip"
>QToolTip</ulink
>. </para>
<programlisting
>QToolTip::add(w, i18n("Ce widget ne fait rien"))
</programlisting>
<para
>Si les barres de menus et les barres d'outils sont créées à l'aide du <ulink url="actionpattern.html"
>Ici, il est également possible d'affecter un texte qui est affiché dans la barre d'état lorsque l'élément du menu respectif est mis en évidence: </para>
<programlisting
>action->setStatusText(i18n("Supprime le fichier marqué"))
</programlisting>
<para
>L'API pour l'aide «Qu'est-ce que c'est? » est très similaire. Dans les boîtes de dialogue, utilisez le code suivant: </para>
>. Pour afficher le manuel de votre application, utilisez simplement </para>
<programlisting
>kapp->invokeHelp()
</programlisting>
<para
>Cette commande affiche la première page avec la table des matières. Si vous voulez n'afficher qu'une certaine section du manuel, vous pouvez fournir un argument supplémentaire à <function
>invokeHelp()</function
>, déterminant l'ancrage vers lequel le navigateur saute. </para>
</sect1>
</chapter>
<chapter id="components">
<title
>Composants et services</title>
<sect1 id="components-services">
<title
>Services KDE</title>
<simplesect id="services-whatarekdeservices">
<title
>Que sont les services KDE?</title>
<para
>La notion de <emphasis
>service</emphasis
> est un concept fondamental dans l'architecture modulaire de KDE. Il n'y aucune implémentation technique stricte associée à ce terme—les services peuvent être des modules externes (<emphasis
>plugins</emphasis
>) sous la forme de bibliothèques partagées, ou bien il peut s'agir de programmes contrôlés <emphasis
>via</emphasis
> <ulink url="dcop.html"
>DCOP</ulink
>. En se proclamant être d'un certain <emphasis
>type de service</emphasis
>, un service promet d'implémenter certaines API ou fonctionnalités. En termes C++, on peut penser à un type de service comme à une classe d'abstraction, et à un service comme à une implémentation de cette interface. </para>
<para
>L'avantage de cette séparation est clair: une application utilisant un type de service n'a pas à en connaître les implémentations possibles. Elle se contente d'employer les API associées au type de service. De cette manière, le service utilisé peut être modifié sans affecter l'application. En outre, l'utilisateur peut configurer les services qu'il préfère pour certaines fonctionnalités. </para>
<para
>Quelques exemples: </para>
<itemizedlist>
<listitem
><para
>Le moteur de rendu HTML utilisé dans <application
>Konqueror</application
> est un composant intégrable qui implémente les types de services <literal
>KParts/ReadOnlyPart</literal
> et <literal
>Navigateur/Affichage</literal
>. </para
></listitem>
<listitem
><para
>Dans <application
>KDevelop</application
> HEAD, la majorité des fonctionnalités est conditionnée en modules externes avec le type de service <literal
>KDevelop/Part</literal
>. Au démarrage, tous les services de ce type sont chargés, de telle sorte que vous puissiez étendre l'EDi d'une manière très souple. </para
></listitem>
<listitem
><para
>Dans l'affichage Icône, <application
>Konqueror</application
> affiche—si activé—des représentations miniatures des images, des pages HTML, des fichiers PDF et texte. Cette capacité peut être étendue. Si vous souhaitez qu'elle affiche des aperçus des images de vos propres fichiers de données avec tel ou tel type MIME, mettez en œuvre un service avec un type de service <classname
>ThumbCreator</classname
>. </para
></listitem>
</itemizedlist>
<para
>À l'évidence, un service n'est pas seulement caractérisé par les types de services qu'il met en œuvre, mais aussi par quelques <emphasis
>propriétés</emphasis
>. Par exemple, un ThumbCreator ne se se contente pas d'implémenter la classe C++ avec le type <classname
>ThumbCreator</classname
>, il a aussi une liste de types MIME dont il est responsable. De la même manière, les «parties» de KDevelop ont comme propriété le langage de programmation qu'elles prennent en charge. Lorsqu'une application demande un type de service, elle peut également dresser la liste des contraintes sur les propriétés du service. Dans l'exemple ci-dessus, quand KDevelop charge les modules externes pour un projet Java, il demande seulement ceux qui ont Java comme propriété de langage de programmation. À cette fin, KDE contient un <emphasis
>courtier</emphasis
> à part entière dans le style de CORBA, doté d'un langage de requête complexe. </para>
</simplesect>
<simplesect id="services-definingservicetypes">
<title
>Définition des types de services</title>
<para
>Les nouveaux types de services sont ajoutés en installant une description dans le dossier <filename
>En plus des lignes habituelles, cet exemple explique comment déclarer qu'un service a certaines propriétés. Chaque définition de propriété correspond à un groupe <literal
>[PropertyDef::name]</literal
> dans le fichier de configuration. Dans ce groupe, la ligne <literal
>Type</literal
> déclare le type de la propriété. Les types possibles sont tout ce qui peut être enregistré dans une <ulink url="kdeapi:qt/QVariant"
> est une ligne importante. Elle contient le nom de la bibliothèque libtool (sans l'extension <literal
>.la</literal
>). Elle fixe également (avec le préfixe <literal
>init_</literal
> ajouté au début) le nom du symbole exporté dans la bibliothèque qui retourne une fabrique d'objets. Pour l'exemple ci-dessus, la bibliothèque doit contenir la fonction suivante: </para>
<programlisting
>extern "C" {
void *init_libkdevdoxygen()
{
return new DoxygenFactory;
}
};
</programlisting>
<para
>Le type de la classe de fabrique <classname
>DoxygenFactory</classname
> dépend du type de service spécifique que le service met en œuvre. Dans notre exemple d'un module externe KDevelop, la fabrique doit être une <classname
>À partir de ce moment, la suite des opérations dépend à nouveau du type de service. Pour les modules externes génériques, vous créerez des objets avec la méthode <ulink url="kdeapi:tdecore/KLibFactory.html#ref3"
cout << "Le service n'implémente pas la fabrique correcte" << endl;
}
</programlisting>
</simplesect>
<simplesect id="services-definingdcopservices">
<title
>Définition des services DCOP</title>
<para
>Un service DCOP est habituellement mis en œuvre comme un programme qui est démarré lorsqu'il est nécessaire. Il entre ensuite dans une boucle et écoute les connexions DCOP. Il peut s'agir d'un programme interactif, mais il se peut aussi qu'il s'exécute complètement ou pour une partie de sa durée de vie comme un démon en arrière-plan, sans que l'utilisateur le remarque. Un exemple de ce type de démon est <literal
>, qui met en œuvre une interaction utilisateur comme boîte de dialogue de progression pour la bibliothèque TDEIO. L'avantage d'un tel démon centralisé dans ce contexte est que &pex; la progression du téléchargement de différents fichiers peut s'afficher dans une seule fenêtre, même si ces téléchargements ont été lancés à partir de différentes applications. </para>
>Un service DCOP est défini différemment depuis un service de bibliothèque partagée. Bien sûr, il ne spécifie pas une bibliothèque, mais plutôt un exécutable. En outre, les services DCOP ne spécifient pas de ligne ServiceType parce qu'ils sont habituellement démarrés par leur nom. À titre de propriétés additionnelles, il contient deux lignes: </para>
<para
><literal
>X-DCOP-ServiceType</literal
> spécifie la manière dont le service est démarré. La valeur <literal
>Unique</literal
> indique que le service ne doit pas être démarré plus d'une fois. Cela signifie que si vous tentez de démarrer ce service (&pex; <emphasis
>, KDE vérifie s'il est déjà enregistré avec DCOP et utilise le service en cours d'exécution. S'il n'est pas encore enregistré, KDE le démarre et attend jusqu'à ce qu'il soit enregistré. Ainsi, vous pouvez immédiatement envoyer des appels DCOP au service. Dans un tel cas, le service devra être mis en œuvre en tant que <ulink url="kdeapi:tdecore/KUniqueApplication.html"
> indique que, comme des instances multiples du service peuvent coexister, toute tentative pour démarrer le service créera un autre processus. En dernière possibilité, on peut employer la valeur <literal
>None</literal
>. Dans ce cas, un démarrage du service n'attendra pas jusqu'à ce qu'il soit enregistré avec DCOP. </para>
> devrait normalement être défini à «false». Sinon, quand le programme est démarré, la barre de tâches affiche une notification de démarrage ou, en fonction des réglages de l'utilisateur, le curseur est changé. </para>
>Notez que l'exemple d'un appel DCOP donné utilise ici la mise en ordre explicite des arguments. Vous serez souvent amené à employer de préférence un élément de remplacement généré par dcopidl2cpp parce qu'il est plus simple et moins sujet aux erreurs. </para>
<para
>Dans l'exemple donné ici, le service a été démarré «par nom», &cad; que le premier argument vers <function
>Tous ces appels prennent une liste d'URL comme deuxième argument, fourni au service sur la ligne de commande. Le troisième argument est un pointeur vers une <classname
>QString</classname
>. Si le démarrage du service échoue, cet argument devient un message d'erreur traduit. </para>
</simplesect>
</sect1>
<sect1 id="components-mime">
<title
>Types MIME</title>
<simplesect id="mime-whataremimetypes">
<title
>Que sont les types MIME?</title>
<para
>Les types MIME servent à décrire le type de contenu de blocs de fichiers ou de données. Au départ, ils ont été introduits afin d'autoriser l'envoi des images ou des fichiers son &etc; par courrier électronique (MIME signifie «Multipurpose Internet Mail Extensions», extensions multimédia de messagerie Internet). Plus tard, les navigateurs ont également fait appel à ce système pour déterminer comment présenter à l'utilisateur les données envoyées par un serveur web. Par exemple, une page HTML a un type MIME «text/html», un fichier PostScript «application/postscript». Dans KDE, ce concept s'emploie à divers endroits: </para>
<itemizedlist>
<listitem
><para
>Dans l'affichage Icône de <application
>Konqueror</application
>, les fichiers sont représentés par des icônes. Chaque type MIME a une certaine icône associée illustrée ici. </para
></listitem>
<listitem
><para
>Quand vous cliquez sur l'icône d'un fichier ou le nom d'un fichier dans <application
>Konqueror</application
>, soit le fichier s'affiche dans une vue intégrée, soit une application associée au type du fichier est ouverte. </para
></listitem>
<listitem
><para
>Quand vous faites un glisser-déposer de certaines données d'une application à l'autre (ou au sein de la même application), la cible de dépôt peut choisir de n'accepter que certains types de données. De surcroît, elle gérera les données images différemment des données textuelles. </para
></listitem>
<listitem
><para
>Les données du presse-papiers ont un type MIME. Traditionnellement, les programmes X ne gèrent que les pixmaps ou les textes, mais avec Qt, il n'y a aucune restriction sur le type des données. </para
></listitem>
</itemizedlist>
<para
>À la lumière des exemples précédents, il est clair que la gestion MIME est un problème complexe. Tout d'abord, il est nécessaire d'établir une correspondance depuis les noms des fichiers vers les types MIME. KDE franchit une étape supplémentaire en permettant même au contenu des fichiers d'être mis en correspondance vers les types MIME, pour les cas où le nom du fichier n'est pas disponible. En second lieu, il est nécessaire de mettre en correspondance les types MIME vers les applications ou les bibliothèques qui peuvent afficher ou modifier un fichier avec un certain type ou bien en créer une image miniature. </para>
<para
>Il y a diverses API pour découvrir le type MIME des données ou des fichiers. En général, vous devez arriver à un certain compromis vitesse/fiabilité. Cherchez le type d'un fichier en n'examinant que son nom de fichier (&cad; dans la plupart des cas, l'extension du nom du fichier). Par exemple, un fichier <filename
>foo.jpg</filename
> est normalement «image/jpeg». Dans les cas où l'extension est retirée, ce n'est pas sans risque et vous devez vraiment vérifier le contenu du fichier. Cette opération prend assurément plus de temps, en particulier pour les fichiers qu'il faut télécharger <emphasis
>via</emphasis
> HTTP au préalable. La méthode basée sur le contenu dépend du fichier <filename
> et est par conséquent difficile à étendre. Mais en général, les informations sur le type MIME peuvent être aisément mises à la disposition du système en installant un fichier <literal
>.desktop</literal
>: il est efficacement et commodément accessible grâce aux bibliothèques KDE. </para>
</simplesect>
<simplesect id="mime-definingmimetypes">
<title
>Définition des types MIME</title>
<para
>Définissons un type <literal
>«application/x-foo»</literal
> pour notre nouveau programme <application
>foobar</application
>. À cette fin, vous devez écrire un fichier <filename
>. (Il s'agit de l'emplacement habituel, qui peut différer entre les distributions). Cette opération peut s'effectuer en ajoutant les lignes suivantes au <filename
>Makefile.am</filename
>: </para>
<programlisting
>mimedir = $(kde_mimedir)/application
mime_DATA = foo.desktop
EXTRA_DIST = $(mime_DATA)
</programlisting>
<para
>Voici à quoi devrait ressembler le fichier <filename
>foo.desktop</filename
>: </para>
<programlisting
>[Desktop Entry]
Type=MimeType
MimeType=application/x-foo
Icon=fooicon
Patterns=*.foo;
DefaultApp=foobar
Comment[fr]=Fichier de données Foo
Comment[en]=Foo Data File
</programlisting>
<para
>La ligne <literal
>«Comment»</literal
> est supposée être traduite. Du fait que le fichier <filename
>.desktop</filename
> spécifie une icône, installez aussi une icône <filename
>fooicon.png</filename
>, qui représente le fichier, &pex; dans <application
>Dans les bibliothèques KDE, un tel type de définition est mis en correspondance vers une instance de la classe <ulink url="kdeapi:tdeio/KMimeType.html"
>La méthode la plus rapide pour déterminer le type d'un fichier est <function
>KMimeType::findByURL()</function
>. Celle-ci cherche la chaîne d'URL et, le plus souvent, détermine le type à partir de l'extension. Pour certains protocoles (&pex;, http, man, info), ce mécanisme n'est pas employé. Par exemple, les scripts CGI sur les serveurs web écrits en Perl ont souvent l'extension <literal
>.pl</literal
> indiquant un type <literal
>"text/x-perl"</literal
>. Toutefois, le fichier que livre le serveur est la sortie de ce script, qui est normalement du HTML. Pour un tel cas, <function
>KMimeType::findByURL()</function
> retourne le type MIME <literal
>"application/octet-stream"</literal
> (disponible grâce à <function
>KMimeType::defaultMimeType()</function
>), indiquant qu'il est impossible de découvrir le type. </para>
<programlisting
>KMimeType::Ptr type = KMimeType::findByURL("/home/bernd/foobar.jpg");
if (type->name() == KMimeType::defaultMimeType())
cout << "Impossible de découvrir le type" << endl;
>Vous pouvez être amené à trouver un type MIME à partir du contenu du fichier au lieu du nom du fichier. Ce procédé est plus fiable mais aussi plus lent, puisqu'il exige la lecture d'une partie du fichier. Pour ce faire, faites appel à la classe <ulink url="kdeapi:tdeio/KMimeMagic.html"
>Bien sûr, même KMimeMagic n'est capable de déterminer un type de fichier qu'à partir du contenu d'un fichier local. Pour les fichiers distants, il y a une autre possibilité: </para>
>Cette fonction démarre une tâche TDEIO pour télécharger une partie du fichier et le vérifie. Notez qu'elle peut être très lente et bloque le programme. Normalement, vous ne l'utiliserez que si <function
>Par ailleurs, si vous ne voulez pas bloquer votre application, vous pouvez également démarrer explicitement la tâche TDEIO et vous connecter à quelques-uns de ses signaux: </para>
>Mise en correspondance d'un type MIME vers une application ou un service</title>
<para
>Lorsqu'une application est installée, elle prépare un fichier <literal
>.desktop</literal
> contenant une liste de types MIME que cette application peut charger. De la même manière, les composants comme KParts rendent cette information disponible au moyen de leurs fichiers <literal
>.desktop</literal
> de services. Il y a donc en général plusieurs programmes et composants pour traiter un type MIME donné. Vous pouvez obtenir ce genre de liste depuis la classe <classname
>La plupart du temps, la liste de toutes les offres de services n'a aucun intérêt pour une combinaison type MIME et type de service. Il y a une fonction de confort qui vous donne uniquement l'offre de service ayant la plus grande préférence: </para>
>Exécution d'une URL. Celle-ci cherche le type de l'URL et démarre le programme préféré de l'utilisateur et associé à ce type. </para>
<programlisting
>KURL url("http://dot.kde.org");
new KRun(url);
</programlisting>
</simplesect>
</sect1>
<sect1 id="nettransparency">
<title
>Transparence réseau</title>
<simplesect id="nettransparency-intro">
<title
>Introduction</title>
<para
>À l'ère du World Wide Web, il est d'une importance capitale que les applications bureautiques puissent accéder aux ressources sur l'internet: elles devront être capables de télécharger des fichiers sur un serveur web, écrire des fichiers sur un serveur ftp ou lire des messages sur un serveur de messagerie. Souvent, la capacité à accéder à des fichiers quel que soit leur emplacement est appelée <emphasis
>transparence réseau</emphasis
>. </para>
<para
>Dans le passé, différentes approches de ces objectifs ont été mises en œuvre. L'ancien système de fichiers NFS est une tentative pour implémenter la transparence réseau au niveau de l'API POSIX. Bien que cette approche fonctionne parfaitement dans des réseaux locaux,étroitement couplés, elle ne convient pas pour les ressources dont l'accès manque de fiabilité et souffre d'une éventuelle lenteur. Ici, l'<emphasis
>asynchronicité</emphasis
> est importante. Pendant que vous attendez que votre navigateur web télécharge une page, l'interface utilisateur ne devra pas se bloquer. De plus, le rendu de la page ne devrait pas commencer quand la page est complètement disponible, mais devrait être régulièrement actualisé au fur et à mesure que les données arrivent. </para>
> d'E/S. Une tâche peut copier ou supprimer des fichiers, ou toute autre chose similaire. Dès lors qu'une tâche est démarrée, elle s'exécute en arrière-plan et ne bloque pas l'application. Toute communication depuis la tâche à nouveau vers l'application—comme la remise des données ou les informations de progression—s'effectue alors qu'elle est intégrée à la boucle d'événement Qt. </para>
<para
>L'opération d'arrière-plan est réalisée en démarrant les <emphasis
>ioslaves</emphasis
> pour exécuter certaines tâches. Les «ioslaves» sont démarrés en tant que processus séparés et communiquent avec eux grâce à des sockets du domaine UNIX. De cette manière, aucun traitement multiprocessus (<emphasis
>multi-threading</emphasis
>) n'est nécessaire et les esclaves instables ne peuvent pas «planter» l'application qui les utilise. </para>
<para
>Les emplacements des fichiers sont exprimés par les URL bien connues. Mais dans KDE, les URL ne se contentent pas d'étendre la plage des fichiers adressables au-delà du système de fichiers local. Ce dernier va aussi dans la direction opposée—&pex;, vous pouvez explorer des archives «tar». Ceci est réalisable grâce à l'imbrication des URL. Par exemple, un fichier dans une archive tar sur un serveur http pourrait avoir l'URL </para>
>Dans la majorité des cas, les tâches sont créées en appelant des fonctions dans l'espace de noms TDEIO. Celles-ci prennent une ou deux URL comme arguments, voire d'autres paramètres nécessaires. Lorsque la tâche est terminée, elle émet le signal <literal
>Découvre certaines informations sur le fichier, comme sa taille, l'heure de sa modification et ses permissions. Les informations peuvent être obtenues à partir de TDEIO::StatJob::statResult() lorsque la tâche est terminée. </para
>Les tâches TDEIO::stat() et TDEIO::listDir() retournent toutes deux leur résultat sous la forme d'un type UDSEntry, UDSEntryList, respectivement. Cette dernière est définie en tant que QValueList<UDSEntry>. L'acronyme UDS signifie «Universal Directory Service» (service universel de dossiers). Le principe sous-jacent est que l'élément du dossier ne transporte que les informations qu'un ioslave peut fournir, sans plus. Par exemple, l'esclave http ne fournit aucune information sur les droits d'accès ou sur les propriétaires des fichiers. En revanche, un UDSEntry est une liste de UDSAtoms. Chaque atome fournit une partie spécifique d'information. Il se compose d'un type enregistré dans m_uds et soit d'une valeur entière dans m_long, soit d'une valeur chaîne dans m_str, en fonction du type. </para>
>Les types suivants sont actuellement définis: </para>
<itemizedlist>
<listitem
><para
>UDS_SIZE (integer)—taille du fichier. </para
></listitem>
<listitem
><para
>UDS_USER (string)—utilisateur propriétaire du fichier. </para
></listitem>
<listitem
><para
>UDS_GROUP (string)—groupe propriétaire du fichier. </para
></listitem>
<listitem
><para
>UDS_NAME (string)—nom du fichier. </para
></listitem>
<listitem
><para
>UDS_ACCESS (integer)—droits d'accès du fichier, comme &pex; enregistrés par la fonction libc stat() dans le champ st_mode. </para
></listitem>
<listitem
><para
>UDS_FILE_TYPE (integer)—le type du fichier, comme &pex; enregistré par stat() dans le champ st_mode. Par conséquent, vous pouvez faire appel aux macros libc habituelles comme S_ISDIR pour tester cette valeur. Notez que les données fournies par les ioslaves correspondent à stat(), non lstat(), &cad; que dans le cas de liens symboliques, le type du fichier est ici le type du fichier sur lequel pointe le lien, non le lien lui-même. </para
></listitem>
<listitem
><para
>UDS_LINK_DEST (string)—dans le cas d'un lien symbolique, le nom du fichier sur lequel pointe le fichier. </para
></listitem>
<listitem
><para
>UDS_MODIFICATION_TIME (integer)—l'heure (comme dans le type time_t) de la dernière modification du fichier, comme &pex; enregistré par stat() dans le champ st_mtime. </para
></listitem>
<listitem
><para
>UDS_ACCESS_TIME (integer)—l'heure du dernier accès au fichier, comme &pex; enregistré par stat() dans le champ st_atime. </para
></listitem>
<listitem
><para
>UDS_CREATION_TIME (integer)—l'heure à laquelle le fichier a été créé, comme &pex; enregistré par stat() dans le champ st_ctime. </para
></listitem>
<listitem
><para
>UDS_URL (string)—fournit l'URL d'un fichier, s'il ne s'agit pas simplement de la concaténation de l'URL d'un dossier et d'un nom de fichier. </para
></listitem>
<listitem
><para
>UDS_MIME_TYPE (string)—type MIME du fichier </para
></listitem>
<listitem
><para
>UDS_GUESSED_MIME_TYPE (string)—type MIME du fichier comme le devine l'esclave. La différence avec le type précédent est que celui fourni ici ne devra pas être considéré comme fiable (parce que le fait de le déterminer d'une manière sûre serait trop coûteux). Par exemple, la classe KRun vérifie explicitement le type MIME s'il n'a pas d'informations dignes de foi. </para
></listitem>
</itemizedlist>
<para
>Bien que la manière d'enregister des informations sur les fichiers dans un <classname
>UDSEntry</classname
> soit souple et pratique du point de vue de l'ioslave, son utilisation pour le programmeur d'applications est fastidieuse. Par exemple, pour découvrir le type MIME du fichier, vous devez itérer sur tous les atomes et tester si <literal
>m_uds</literal
> est <literal
>UDS_MIME_TYPE</literal
>. Heureusement il y a une API beaucoup plus facile à utiliser: la classe <classname
>Souvent, l'API asynchrone de TDEIO est trop complexe à employer et donc, la mise en œuvre de l'asynchronicité complète n'est pas une priorité. Par exemple, dans un programme qui ne peut gérer qu'un fichier document à la fois, il y a de toute façon très peu de choses à faire pendant que le programme télécharge un fichier. Pour ces cas peu complexes, il y a une API beaucoup plus simple, sous la forme d'un ensemble de fonctions statiques dans TDEIO::NetAccess. Par exemple, pour copier un fichier, utilisez </para>
>La fonction revient à l'état antérieur une fois que le processus complet de copie est terminé. De plus, cette méthode fournit une boîte de dialogue de progression et s'assure que les processus d'application retracent les événements. </para>
<para
>La fonction <function
>download()</function
>, en combinaison avec <function
>removeTempFile()</function
> est particulièrement intéressante. La première télécharge un fichier depuis l'URL donnée et l'enregistre dans un fichier temporaire avec un nom unique. Ce nom est enregistré dans le second argument. <emphasis
>Si</emphasis
> l'URL est locale, le fichier n'est pas téléchargé et, en revanche, le second argument est défini au nom du fichier local. La fonction <function
>removeTempFile()</function
> supprime le fichier donné par son argument si le fichier est le résultat d'un téléchargement précédent. Si tel n'est pas le cas, elle ne fait rien. Ainsi, le fragment de code suivant est un moyen très aisé d'utiliser le chargement des fichiers, quel que soit leur emplacement: </para>
>Comme on peut le voir plus haut, l'interface aux tâches IO est assez abstraite et ne prend en considération aucun échange d'informations entre l'application et l'<emphasis
>ioslave</emphasis
> spécifique au protocole: ce n'est pas toujours approprié. Par exemple, vous pouvez fournir certains paramètres à l'esclave HTTP pour contrôler le comportement de sa mise en cache ou envoyer un groupe de cookies avec la requête. Le concept de métadonnées a été introduit pour satisfaire cette exigence. Lorsqu'une tâche est créée, vous pouvez la configurer en lui ajoutant des métadonnées. Chaque élément de métadonnées se compose d'une paire clé/valeur. Par exemple, pour empêcher l'esclave HTTP de charger une page web depuis son cache, utilisez: </para>
>La même technique s'emploie dans l'autre sens, &cad; pour la communication depuis l'esclave vers l'application. La méhode <function
>Job::queryMetaData()</function
> demande la valeur de la clé remise par l'esclave. Pour l'esclave HTTP, un tel exemple est la clé <literal
>«modified»</literal
>, qui contient une représentation sous forme de chaîne de la date de la dernière modification de la page web. Voici un exemple de la manière dont vous pouvez l'utiliser: </para>
>Lorsque vous employez l'API TDEIO, vous n'avez d'ordinaire pas à vous préoccuper des détails du démarrage des IOslaves et de la communication avec ces derniers. Le cas normal d'utilisation est de démarrer une tâche et avec certains paramètres, ainsi que de gérer les signaux que la tâche émet. </para>
>En coulisses, le scénario est beaucoup plus compliqué. Dès que vous créez une tâche, elle est mise en file d'attente. Quand l'application revient vers la boucle d'événement, TDEIO alloue les processus esclaves pour les tâches contenues dans la file d'attente. Pour la première tâche démarrée, c'est trivial: un IOslave est démarré pour le protocole approprié. Toutefois, une fois que la tâche (comme un téléchargement depuis un serveur http) est terminée, elle n'est pas tuée immédiatement. Elle est au contraire placée dans un groupe d'esclaves inactifs et tuée après un certain temps d'inactivité (le plus souvent, 3minutes). Si une nouvelle requête pour le même protocole et le même hôte arrive, l'esclave est réutilisé. L'avantage évident est que, pour une série de tâches concernant le même hôte, le coût nécessaire à la création de nouveaux processus et éventuellement à l'obtention d'une procédure d'authentification est économisé. </para>
>Naturellement, la réutilisation n'est possible que si l'esclave existant a déjà terminé sa tâche précédente. Quand une nouvelle requête arrive alors qu'un processus esclave existant s'exécute encore, un nouveau processus doit être démarré et utilisé. Lors de l'usage de l'API dans les exemples précédents, il n'y a aucune limite à la création de nouveaux processus esclaves: si vous démarrez une série consécutive de téléchargements pour 20fichiers différents, alors TDEIO démarre 20processus esclaves. Ce dispositif visant à affecter des esclaves aux tâches est appelé <emphasis
>. Ce n'est pas toujours celui qui est le plus approprié, car il peut exiger beaucoup de mémoire et placer une charge élevée à la fois sur les machines clientes et serveurs. </para>
<para
>Il y a donc un autre moyen: <emphasis
>planifier</emphasis
> les tâches. Si vous le faites, seul un nombre limité (actuellement, 3) de processus esclaves pour un protocole sera créé. Si vous créez davantage de tâches, elles sont placées dans une file d'attente et traitées lorsqu'un processus esclave devient inactif. Voici comment s'effectue l'opération: </para>
>. Par exemple, pour l'esclave IMAP, il n'est pas judicieux de démarrer de multiples processus pour le même serveur. Une seule connexion IMAP à la fois devra être imposée. Dans ce cas, l'application doit accepter explicitement la notion d'esclave. Il faut qu'elle désalloue un esclave pour une certaine connexion puis qu'elle affecte toutes les tâches qui obtiendront la même connexion au même esclave. Cette opération peut à nouveau être réalisée à l'aide du TDEIO::Scheduler: </para>
>Vous pouvez déconnecter l'esclave uniquement après que toutes les tâches qui lui sont affectées sont garanties être terminées. </para>
</simplesect>
<simplesect id="nettransparency-definingslaves">
<title
>Définition d'un ioslave</title>
<para
>Dans ce qui suit, nous verrons comment vous pouvez ajouter un nouveau ioslave au système. Par analogie avec les services, les nouveaux ioslaves sont annoncés au système en installant un petit fichier de configuration. Le fragment de Makefile.am suivant installa le protocole ftp: </para>
<programlisting
>protocoldir = $(kde_servicesdir)
protocol_DATA = ftp.protocol
EXTRA_DIST = $(mime_DATA)
</programlisting>
<para
>Voici le contenu du fichier ftp.protocol: </para>
> définit de quel protocole cet esclave est responsable. <literal
>«exec»</literal
> est (contrairement à ce que vous pourriez attendre naïvement), le nom de la bibliothèque qui implémente l'esclave. Quand ce dernier est supposé démarrer, l'exécutable <command
> est démarré, lequel à son tour charge cette bibliothèque dans son espace d'adressage. Donc, en pratique, vous pouvez imaginer l'esclave en cours d'exécution comme un processus séparé, bien qu'il soit implémenté en tant que bibliothèque. L'avantage de ce mécanisme est qu'il économise une grande quantité de mémoire et réduit le temps qu'exige l'éditeur de liens au moment de l'exécution. </para>
<para
>Les lignes «input» et «output» ne sont pas utilisées actuellement. </para>
>, les lignes restantes définissent les capacités qu'a l'esclave. En général, les fonctionnalités qu'un esclave doit implémenter sont beaucoup plus simples que celles que l'API TDEIO fournit pour l'application. La raison à cela est que les tâches complexes sont planifiées pour quelques sous-tâches. Par exemple, pour répertorier un dossier récursivement, une tâche sera démarrée pour le dossier de premier niveau. Puis, pour chaque sous-dossier faisant l'objet d'un rapport, de nouvelles sous-tâches sont démarrées. Dans TDEIO, un planificateur veille à ce qu'il n'y ait pas trop de tâches actives au même moment. De la même manière, pour copier un fichier au sein d'un protocole qui ne prend pas en charge la copie directement (comme le protocole <literal
> doit annoncer les actions que son esclave prend en charge. </para>
<para
>Du fait que les esclaves sont chargés à titre de bibliothèques partagées mais constituent des programmes autonomes, l'environnement de leur code paraît un peu différent des modules externes (<emphasis
>plugins</emphasis
>) des bibliothèques partagées normales. La fonction appelée pour démarrer l'esclave est dénommée <function
>kdemain()</function
>. Elle effectue quelques initialisations puis entre dans une boucle d'événement et attend les requêtes de l'application qui l'utilise. Voici à quoi elle ressemble: </para>
<programlisting
>extern "C" { int kdemain(int argc, char **argv); }
>. Pour ces opérations, TDEIO détermine automatiquement si elles sont prises en charge ou non (&cad; que l'implémentation par défaut retourne une erreur). </para>
>Toutes ces implémentations devront se terminer par un ou deux appels: si l'opération a réussi, elles devront appeler <literal
>finished()</literal
>. Si une erreur s'est produite, <literal
>error()</literal
> devra être appelée avec un code d'erreur comme premier argument et une chaîne dans le second. Les codes d'erreur possibles sont répertoriés sous forme d'énumération <type
> afin de paramétrer le message d'erreur humainement lisible. </para>
<para
>Pour les esclaves qui correspondent aux protocoles de réseau, il pourrait être intéressant de réimplémenter la méthode <function
>SlaveBase::setHost()</function
>. Cette dernière est appelée pour indiquer au processus esclave l'hôte et le port, ainsi que le nom d'utilisateur et le mot de passe pour se connecter. En général, les métadonnées définies par l'application peuvent être interrogées par <function
>SlaveBase::metaData()</function
>. Vous pouvez vérifier l'existence des métadonnées d'une certaine clé avec <function
>SlaveBase::hasMetaData()</function
>. </para>
</simplesect>
<simplesect id="nettransparency-communication">
<title
>Communication à nouveau avec l'application</title>
<para
>Diverses actions mises en œuvre dans un esclave doivent d'une certaine manière communiquer les données en retour à l'application à l'aide du processus esclave: </para>
<itemizedlist>
<listitem
><para
><function
>get()</function
> envoie des blocs de données. Pour ce faire, elle utilise <function
>data()</function
>, qui prend un <classname
>QByteArray</classname
> comme argument. Bien sûr, vous n'avez pas besoin d'envoyer toutes les données à la fois. Si vous envoyez un gros fichier, appelez <function
>data()</function
> avec des blocs de données plus petits, de façon à ce que l'application puisse les traiter. Appelez <function
>finished()</function
> quand le transfert est terminé. </para
></listitem>
<listitem
><para
><function
>listDir()</function
> signale des informations sur les éléments d'un dossier. À cette fin, appelez <function
>, que nous aborderons ci-après. Utilisez <function
>statEntry()</function
> pour envoyer un tel élément à l'application. </para
></listitem>
<listitem
><para
><function
>mimetype()</function
> appelle <function
>mimeType()</function
> avec un argument chaîne. </para
></listitem>
<listitem
><para
><function
>get()</function
> et <function
>copy()</function
> peuvent être amenées à fournir des informations sur la progression. Les méthodes <function
>totalSize()</function
>, <function
>processedSize()</function
>, <function
>speed()</function
> s'en chargent. La taille totale et la taille traitée sont signalées sous forme d'octets, la vitesse en octets par seconde. </para
></listitem>
<listitem
><para
>Vous pouvez envoyer des paires clé/valeur arbitraires de métadonnées avec <function
>setMetaData()</function
>. </para
></listitem>
</itemizedlist>
</simplesect>
<simplesect id="nettransparency-interacting">
<title
>Interaction avec l'utilisateur</title>
<para
>Parfois, un esclave doit interagir avec l'utilisateur. Des exemples incluent les messages d'information, les boîtes de dialogue d'authentification et de confirmation quand un fichier est sur le point d'être écrasé. </para>
<itemizedlist>
<listitem
><para
><function
>infoMessage()</function
>—il s'agit d'un message de retour d'informations, comme un message «Retrieving data from <host> » depuis l'esclave http, qui est souvent affiché dans la barre d'état du programme. Côté application, cette méthode correspond au signal <function
>—affiche un avertissement dans une boîte de message avec <function
>KMessageBox::information()</function
>. Si une boîte de message est encore ouverte depuis un précédent appel de warning() provenant du même processus esclave, il ne se produit rien. </para
></listitem>
<listitem
><para
><function
>messageBox()</function
>—cette méthode est plus riche que la précédente. Elle permet d'ouvrir une boîte de message avec du texte et une légende, voire quelques boutons. Reportez-vous à l'énumération <type
>SlaveBase::MessageBoxType</type
> à titre de référence. </para
></listitem>
<listitem
><para
><function
>openPassDlg()</function
>—ouvre une boîte de dialogue pour la saisie du nom d'utilisateur et du mot de passe. </para