Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Hide protected methods in generated doc.
[graphlib.git] / DrawingWindow.cpp
index 7c63c86..3234441 100644 (file)
@@ -1,9 +1,110 @@
+/*
+ * Copyright (c) 2007-2013, Arnaud Giersch <arnaud.giersch@iut-bm.univ-fcomte.fr>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 #include "DrawingWindow.h"
 #include <QApplication>
 #include <QPaintEvent>
 #include <QThread>
 #include <QTimerEvent>
 
+/*! \class DrawingWindow
+ *  \brief Fenêtre de dessin.
+ *
+ * \author Arnaud Giersch <arnaud.giersch@iut-bm.univ-fcomte.fr>
+ * \date 2007-2013
+ *
+ * Cette classe décrit un widget Qt permettant d'écrire des
+ * applications graphiques simples.  Pour cela, il faut définir une
+ * fonction de dessin.  Cette fonction ne retourne rien et prend comme
+ * unique paramètre une référence vers un objet de class
+ * DrawingWindow.
+ *
+ * La fonction devra ensuite être passée en paramètre pour les
+ * constructeurs de la classe, ainsi que les dimension requises pour
+ * la fenêtre graphique.  Le programme est ensuite compilé comme
+ * n'importe quel programme Qt.
+ *
+ * Concrètement, la fonction sera exécutée dans un nouveau thread,
+ * tandis que le thread principal s'occupera de la gestion des
+ * évènements et du rendu dans la fenêtre.
+ *
+ * <b>NB.</b> Pour toutes les méthodes de dessin, le coin en haut à gauche
+ * de la fenêtre a les coordonnées (0, 0).  Le coin en bas à droite de
+ * la fenêtre a les coordonnées (largeur - 1, hauteur - 1), si la
+ * fenêtre est de dimension largeur × hauteur.
+ *
+ * Un appui sur la touche &lt;Esc&gt; provoque la fermeture de la fenêtre.
+ * Comme pour la plupart des applications, il est également possible
+ * de fermer la fenêtre via le gestionnaire de fenêtres.
+ *
+ * Il est possible, dans une application, d'ouvrir plusieurs fenêtres,
+ * avec des fonctions de dessin éventuellement différentes.
+ * L'application se terminera normalement lorsque la dernière fenêtre
+ * sera fermée.
+ */
+
+/*! \example hello.cpp
+ *
+ * Voir le code source à la fin de la page.  Pour compiler et exécuter
+ * ce programme, il faut :
+ *
+ * <b>1. Créer le fichier \c hello.pro</b>
+ *
+ * Pour simplifier, ce fichier contient la liste des fichiers sources
+ * composant le programme.
+ *
+ * \include hello.pro
+ *
+ * <b>2. Créer le fichier \c Makefile avec la commande :</b>
+ *
+ * \verbatim qmake-qt4 hello.pro \endverbatim
+ * ou tout simplement :
+ * \verbatim qmake-qt4 \endverbatim
+ *
+ * <b>3. Compiler le programme avec la commande :</b>
+ *
+ * \verbatim make hello \endverbatim
+ * ou tout simplement :
+ * \verbatim make \endverbatim
+ *
+ * <b>4. Exécuter le programme avec la commande :</b>
+ *
+ * \verbatim ./hello \endverbatim
+ *
+ * <b>Code source de l'exemple</b>
+ */
+
+/*! \example exemple.cpp
+ *
+ * Un exemple un peu plus sophistiqué.
+ */
+
 //! Classe de thread.
 class DrawingThread: public QThread {
 public:
@@ -23,8 +124,8 @@ private:
 
 enum UserEvents {
     SyncRequest = QEvent::User, //!< Demande de synchronisation.
-    CloseRequest,               //!< Demande de fermeture de la fenêtre.
-    DrawTextRequest,            //!< Demande d'écriture de texte.
+    CloseRequest,               //!< Demande de fermeture de la fenêtre.
+    DrawTextRequest,            //!< Demande d'écriture de texte.
 };
 
 //! Demande de synchronisation.
@@ -34,21 +135,21 @@ public:
     { }
 };
 
-//! Demande de fermeture de fenêtre.
+//! Demande de fermeture de fenêtre.
 class CloseRequestEvent: public QEvent {
 public:
     CloseRequestEvent(): QEvent(static_cast<QEvent::Type>(CloseRequest))
     { }
 };
 
-//! Demande de tracé de texte. 
+//! Demande de tracé de texte.
 class DrawTextEvent: public QEvent {
 public:
     const int x;
     const int y;
     const char *text;
     const int flags;
-    DrawTextEvent(int x_, int y_, const chartext_, int flags_)
+    DrawTextEvent(int x_, int y_, const char *text_, int flags_)
         : QEvent(static_cast<QEvent::Type>(DrawTextRequest))
         , x(x_), y(y_), text(text_), flags(flags_)
     { }
@@ -56,26 +157,24 @@ public:
 
 //--- DrawingWindow ----------------------------------------------------
 
-/*! \class DrawingWindow
- *  \brief Fenêtre de dessin.
- *
- * Bla bla bla
+/*! \file DrawingWindow.h
+ *  \brief Classe DrawingWindow.
  */
 
 /*! \typedef DrawingWindow::ThreadFunction
- *  \brief Type de la fonction de dessin, passée en paramètre de construction.
+ *  \brief Type de la fonction de dessin, passée en paramètre de construction.
  */
 /*! \var DrawingWindow::DEFAULT_WIDTH
- *  \brief Largeur par défaut de la fenêtre.
+ *  \brief Largeur par défaut de la fenêtre.
  */
 /*! \var DrawingWindow::DEFAULT_HEIGHT
- *  \brief Hauteur par défaut de la fenêtre.
+ *  \brief Hauteur par défaut de la fenêtre.
  */
 /*! \var DrawingWindow::width
- *  \brief Largeur de la fenêtre.
+ *  \brief Largeur de la fenêtre.
  */
 /*! \var DrawingWindow::height
- *  \brief Hauteur de la fenêtre.
+ *  \brief Hauteur de la fenêtre.
  */
 /*! \var DrawingWindow::paintInterval
  *  \brief Intervalle de temps entre deux rendus (ms).
@@ -83,13 +182,13 @@ public:
 
 //! Constructeur.
 /*!
- * Construit une nouvelle fenêtre de dessin avec les dimensions
- * passées en paramètres.  La fonction fun sera exécutée dans un
+ * Construit une nouvelle fenêtre de dessin avec les dimensions
+ * passées en paramètres.  La fonction fun sera exécutée dans un
  * nouveau thread.
  *
  * \param fun           fonction de dessin
- * \param width_        largeur de la fenêtre
- * \param height_       hauteur de la fenêtre
+ * \param width_        largeur de la fenêtre
+ * \param height_       hauteur de la fenêtre
  *
  * \see QWidget
  */
@@ -103,14 +202,14 @@ DrawingWindow::DrawingWindow(ThreadFunction fun, int width_, int height_)
 
 //! Constructeur.
 /*!
- * Construit un nouveau widget de dessin avec les dimensions passées
- * en paramètres.  La fonction fun sera exécutée dans un nouveau
+ * Construit un nouveau widget de dessin avec les dimensions passées
+ * en paramètres.  La fonction fun sera exécutée dans un nouveau
  * thread.
  *
  * \param parent        widget parent
  * \param fun           fonction de dessin
- * \param width_        largeur de la fenêtre
- * \param height_       hauteur de la fenêtre
+ * \param width_        largeur de la fenêtre
+ * \param height_       hauteur de la fenêtre
  *
  * \see QWidget
  */
@@ -125,15 +224,15 @@ DrawingWindow::DrawingWindow(QWidget *parent,
 
 //! Constructeur.
 /*!
- * Construit un nouveau widget de dessin avec les dimensions passées
- * en paramètres.  La fonction fun sera exécutée dans un nouveau
+ * Construit un nouveau widget de dessin avec les dimensions passées
+ * en paramètres.  La fonction fun sera exécutée dans un nouveau
  * thread.
  *
  * \param parent        widget parent
- * \param flags         flags passés au constructeur de QWidget
+ * \param flags         flags passés au constructeur de QWidget
  * \param fun           fonction de dessin
- * \param width_        largeur de la fenêtre
- * \param height_       hauteur de la fenêtre
+ * \param width_        largeur de la fenêtre
+ * \param height_       hauteur de la fenêtre
  *
  * \see QWidget
  */
@@ -156,7 +255,7 @@ DrawingWindow::~DrawingWindow()
 
 //! Change la couleur de dessin.
 /*!
- * La couleur est un entier, tel que retourné par getPointColor.
+ * La couleur est un entier, tel que retourné par getPointColor.
  * Normalement de la forme #00RRGGBB.
  *
  * \param color         couleur
@@ -187,9 +286,9 @@ void DrawingWindow::setColor(const char *name)
 
 //! Change la couleur de dessin.
 /*!
- * Les composantes de rouge, vert et bleu de la couleur doivent être
- * compris entre 0 et 1.  Si le trois composantes sont à 0, on obtient
- * du noir; si les trois composantes sont à 1, on obtient du blanc.
+ * Les composantes de rouge, vert et bleu de la couleur doivent être
+ * compris entre 0 et 1.  Si le trois composantes sont à 0, on obtient
+ * du noir; si les trois composantes sont à 1, on obtient du blanc.
  *
  * \param red           composante de rouge
  * \param green         composante de vert
@@ -246,9 +345,54 @@ void DrawingWindow::setBgColor(float red, float green, float blue)
     setBgColor(QColor::fromRgbF(red, green, blue));
 }
 
-//! Efface la fenêtre.
+//! Change l'épaisseur du pinceau
+/*!
+ * Le pinceau à une épaisseur de 1 par défaut.
+ *
+ * \param width         épaisseur du pinceau
+ */
+void DrawingWindow::setPenWidth(int width)
+{
+    QPen pen(painter->pen());
+    pen.setWidth(width);
+    painter->setPen(pen);
+}
+
+//! Retourne la fonte courante utilisée pour dessiner du texte.
+/*!
+ * \see QFont, setFont
+ */
+const QFont &DrawingWindow::getFont() const
+{
+    return painter->font();
+}
+
+//! Applique une nouvelle font pour dessiner du texte.
 /*!
- * La fenêtre est effacée avec la couleur de fond courante.
+ * \see QFont, getFont
+ */
+void DrawingWindow::setFont(const QFont &font)
+{
+    painter->setFont(font);
+}
+
+//! Active ou non l'antialiasing.
+/*!
+ * Permet de lisser le dessin.
+ * Fonctionnalité désactivée par défaut.
+ *
+ * \param state         état de l'antialiasing
+ *
+ * \bug                 expérimental
+ */
+void DrawingWindow::setAntialiasing(bool state)
+{
+    painter->setRenderHint(QPainter::Antialiasing, state);
+}
+
+//! Efface la fenêtre.
+/*!
+ * La fenêtre est effacée avec la couleur de fond courante.
  *
  * \see setBgColor
  */
@@ -262,10 +406,10 @@ void DrawingWindow::clearGraph()
 
 //! Dessine un point.
 /*!
- * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de
+ * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de
  * dessin courante.
  *
- * \param x, y          coordonnées du point
+ * \param x, y          coordonnées du point
  *
  * \see setColor
  */
@@ -279,53 +423,61 @@ void DrawingWindow::drawPoint(int x, int y)
 
 //! Dessine un segment.
 /*!
- * Dessine un segement de droite entre les coordonnées (x1, y1) et
+ * Dessine un segement de droite entre les coordonnées (x1, y1) et
  * (x2, y2), avec la couleur de dessin courante.
  *
- * \param x1, y1        coordonnées d'une extrémité du segment
- * \param x2, y2        coordonnées de l'autre extrémité du segment
+ * \param x1, y1        coordonnées d'une extrémité du segment
+ * \param x2, y2        coordonnées de l'autre extrémité du segment
  *
  * \see setColor
  */
 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
 {
-    safeLock(imageMutex);
-    painter->drawLine(x1, y1, x2, y2);
-    dirty(x1, y1, x2, y2);
-    safeUnlock(imageMutex);
+    if (x1 == x2 && y1 == y2) {
+        drawPoint(x1, y1);
+    } else {
+        safeLock(imageMutex);
+        painter->drawLine(x1, y1, x2, y2);
+        dirty(x1, y1, x2, y2);
+        safeUnlock(imageMutex);
+    }
 }
 
 //! Dessine un rectangle.
 /*!
- * Dessine le rectangle parallèle aux axes et défini par les
- * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
+ * Dessine le rectangle parallèle aux axes et défini par les
+ * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
  * la couleur de dessin courante.
  *
- * \param x1, y1        coordonnées d'un sommet du rectangle
- * \param x2, y2        coordonnées du sommet opposé du rectangle
+ * \param x1, y1        coordonnées d'un sommet du rectangle
+ * \param x2, y2        coordonnées du sommet opposé du rectangle
  *
  * \see fillRect, setColor
  */
 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
 {
-    QRect r;
-    r.setCoords(x1, y1, x2 - 1, y2 - 1);
-    r = r.normalized();
-    safeLock(imageMutex);
-    painter->drawRect(r);
-    r.adjust(0, 0, 1, 1);
-    dirty(r);
-    safeUnlock(imageMutex);
+    if (x1 == x2 && y1 == y2) {
+        drawPoint(x1, y1);
+    } else {
+        QRect r;
+        r.setCoords(x1, y1, x2 - 1, y2 - 1);
+        r = r.normalized();
+        safeLock(imageMutex);
+        painter->drawRect(r);
+        r.adjust(0, 0, 1, 1);
+        dirty(r);
+        safeUnlock(imageMutex);
+    }
 }
 
 //! Dessine un rectangle plein.
 /*!
- * Dessine le rectangle plein parallèle aux axes et défini par les
- * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
+ * Dessine le rectangle plein parallèle aux axes et défini par les
+ * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
  * la couleur de dessin courante.
  *
- * \param x1, y1        coordonnées d'un sommet du rectangle
- * \param x2, y2        coordonnées du sommet opposé du rectangle
+ * \param x1, y1        coordonnées d'un sommet du rectangle
+ * \param x2, y2        coordonnées du sommet opposé du rectangle
  *
  * \see drawRect, setColor
  */
@@ -341,7 +493,7 @@ void DrawingWindow::fillRect(int x1, int y1, int x2, int y2)
  * Dessine un cercle de centre (x, y) et de rayon r.  Utilise la
  * couleur de dessin courante.
  *
- * \param x, y          coordonnées du centre du cercle
+ * \param x, y          coordonnées du centre du cercle
  * \param r             rayon du cercle
  *
  * \see fillCircle, setColor
@@ -362,7 +514,7 @@ void DrawingWindow::drawCircle(int x, int y, int r)
  * Dessine un disque (cercle plein) de centre (x, y) et de rayon r.
  * Utilise la couleur de dessin courante.
  *
- * \param x, y          coordonnées du centre du disque
+ * \param x, y          coordonnées du centre du disque
  * \param r             rayon du disque
  *
  * \see drawCircle, setColor
@@ -374,19 +526,61 @@ void DrawingWindow::fillCircle(int x, int y, int r)
     painter->setBrush(Qt::NoBrush);
 }
 
-//! Écrit du texte.
+//! Dessine un triangle.
+/*!
+ * Dessine un triangle défini par les coordonnées de ses sommets:
+ * (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de dessin
+ * courante.
+ *
+ * \param x1, y1        coordonnées du premier sommet du triangle
+ * \param x2, y2        coordonnées du deuxième sommet du triangle
+ * \param x3, y3        coordonnées du troisième sommet du triangle
+ *
+ * \see fillTriangle, setColor
+ */
+void DrawingWindow::drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3)
+{
+    QPolygon poly(3);
+    poly.putPoints(0, 3, x1, y1, x2, y2, x3, y3);
+    safeLock(imageMutex);
+    painter->drawConvexPolygon(poly);
+    dirty(poly.boundingRect());
+    safeUnlock(imageMutex);
+}
+
+//! Dessine un triangle plein.
+/*!
+ * Dessine un triangle plein défini par les coordonnées de ses
+ * sommets: (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de
+ * dessin courante.
+ *
+ * \param x1, y1        coordonnées du premier sommet du triangle
+ * \param x2, y2        coordonnées du deuxième sommet du triangle
+ * \param x3, y3        coordonnées du troisième sommet du triangle
+ *
+ * \see drawTriangle, setColor
+ */
+void DrawingWindow::fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3)
+{
+    painter->setBrush(getColor());
+    drawTriangle(x1, y1, x2, y2, x3, y3);
+    painter->setBrush(Qt::NoBrush);
+}
+
+//! Écrit du texte.
 /*!
- * Écrit le texte text, aux coordonnées (x, y) et avec les paramètres
- * d'alignement flags.  Le texte est écrit avec la couleur de dessin
+ * Écrit le texte text, aux coordonnées (x, y) et avec les paramètres
+ * d'alignement flags.  Le texte est écrit avec la couleur de dessin
  * courante.  Les flags sont une combinaison (ou binaire) de
  * Qt::AlignLeft, Qt::AligneRight, Qt::AlignHCenter, Qt::AlignTop,
- * Qt::AlignBottom, Qt::AlignVCenter, Qt::AlignCenter.  Par défaut, le
- * texte est aligné en haut à gauche.
+ * Qt::AlignBottom, Qt::AlignVCenter, Qt::AlignCenter.  Par défaut, le
+ * texte est aligné en haut à gauche.
  *
- * \param x, y          coordonnées du texte
- * \param text          texte à écrire
- * \param flags         paramètres d'alignement
+ * \param x, y          coordonnées du texte
+ * \param text          texte à écrire
+ * \param flags         paramètres d'alignement
  *
+ * \see drawText(int, int, const std::string &, int)
  * \see drawTextBg, setColor
  * \see QPainter::drawText
  */
@@ -400,15 +594,25 @@ void DrawingWindow::drawText(int x, int y, const char *text, int flags)
     safeUnlock(syncMutex);
 }
 
-//! Écrit du texte sur fond coloré.
+//! Écrit du texte.
+/*!
+ * \see drawText(int, int, const char *, int)
+ */
+void DrawingWindow::drawText(int x, int y, const std::string &text, int flags)
+{
+    drawText(x, y, text.c_str(), flags);
+}
+
+//! Écrit du texte sur fond coloré.
 /*!
- * Écrit du texte comme drawText, mais l'arrière-plan est coloré avec
+ * Écrit du texte comme drawText, mais l'arrière-plan est coloré avec
  * la couleur de fond courante.
  *
- * \param x, y          coordonnées du texte
- * \param text          texte à écrire
- * \param flags         paramètres d'alignement
+ * \param x, y          coordonnées du texte
+ * \param text          texte à écrire
+ * \param flags         paramètres d'alignement
  *
+ * \see drawTextBg(int, int, const std::string &, int)
  * \see drawText, setColor, setColorBg
  */
 void DrawingWindow::drawTextBg(int x, int y, const char *text, int flags)
@@ -418,33 +622,81 @@ void DrawingWindow::drawTextBg(int x, int y, const char *text, int flags)
     painter->setBackgroundMode(Qt::TransparentMode);
 }
 
+//! Écrit du texte sur fond coloré.
+/*!
+ * \see drawTextBg(int, int, const char *, int)
+ */
+void DrawingWindow::drawTextBg(int x, int y, const std::string &text, int flags)
+{
+    drawTextBg(x, y, text.c_str(), flags);
+}
 
 //! Retourne la couleur d'un pixel.
 /*!
- * Retourne la couleur du pixel de coordonnées (x, y).  La valeur
- * retournée peut servir de paramètres à setColor(unsigned int) ou
+ * Retourne la couleur du pixel de coordonnées (x, y).  La valeur
+ * retournée peut servir de paramètres à setColor(unsigned int) ou
  * setBgColor(unsigned int).
  *
- * \param x, y          coordonnées du pixel
+ * \param x, y          coordonnées du pixel
  * \return              couleur du pixel
  *
  * \see setColor(unsigned int), setBgColor(unsigned int)
  */
-unsigned int DrawingWindow::getPointColor(int x, int y)
+unsigned int DrawingWindow::getPointColor(int x, int y) const
 {
     return image->pixel(x, y);
 }
 
-//! Synchronise le contenu de la fenêtre.
+//! Attend l'appui sur un des boutons de la souris.
+/*!
+ * Attend l'appui sur un des boutons de la souris.  Retourne le bouton
+ * qui a été pressé et les coordonnées du pointeur de souris à ce
+ * moment-là.
+ *
+ * \param x, y          coordonnées du pointeur de souris
+ * \param button        numéro du bouton qui a été pressé
+ *                      (1: gauche, 2: droit, 3: milieu, 0 sinon)
+ * \param time          durée maximale de l'attente
+ * \return              true si un bouton a été pressé
+ *
+ * \bug                 expérimental
+ */
+bool DrawingWindow::waitMousePress(int &x, int &y, int &button,
+                                   unsigned long time)
+{
+    bool pressed;
+    safeLock(inputMutex);
+    if (terminateThread) {
+        pressed = false;
+    } else {
+        pressed = inputCondition.wait(&inputMutex, time) && !terminateThread;
+        if (pressed) {
+            x = mousePos.x();
+            y = mousePos.y();
+            if (mouseButton & Qt::LeftButton)
+                button = 1;
+            else if (mouseButton & Qt::RightButton)
+                button = 2;
+            else if (mouseButton & Qt::MidButton)
+                button = 3;
+            else
+                button = 0;
+        }
+    }
+    safeUnlock(inputMutex);
+    return pressed;
+}
+
+//! Synchronise le contenu de la fenêtre.
 /*!
- * Pour des raisons d'efficacités, le résultat des fonctions de dessin
- * n'est pas affiché immédiatement.  L'appel à sync permet de
- * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
- * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
- * soit à jour.
+ * Pour des raisons d'efficacités, le résultat des fonctions de dessin
+ * n'est pas affiché immédiatement.  L'appel à sync permet de
+ * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
+ * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
+ * soit à jour.
  *
- * \param time          durée maximale de l'attente
- * \return              true si la fenêtre a pu être synchronisée
+ * \param time          durée maximale de l'attente
+ * \return              true si la fenêtre a pu être synchronisée
  */
 bool DrawingWindow::sync(unsigned long time)
 {
@@ -460,13 +712,13 @@ bool DrawingWindow::sync(unsigned long time)
     return synced;
 }
 
-//! Ferme la fenêtre graphique.
+//! Ferme la fenêtre graphique.
 void DrawingWindow::closeGraph()
 {
     qApp->postEvent(this, new CloseRequestEvent());
 }
 
-//! Suspend l'exécution pendant un certain temps.
+//! Suspend l'exécution pendant un certain temps.
 /*!
  * \param secs          temps d'attente en seconde
  */
@@ -475,7 +727,7 @@ void DrawingWindow::sleep(unsigned long secs)
     DrawingThread::sleep(secs);
 }
 
-//! Suspend l'exécution pendant un certain temps.
+//! Suspend l'exécution pendant un certain temps.
 /*!
  * \param msecs          temps d'attente en millisecondes
  */
@@ -484,7 +736,7 @@ void DrawingWindow::msleep(unsigned long msecs)
     DrawingThread::msleep(msecs);
 }
 
-//! Suspend l'exécution pendant un certain temps.
+//! Suspend l'exécution pendant un certain temps.
 /*!
  * \param usecs          temps d'attente en microsecondes
  */
@@ -493,14 +745,18 @@ void DrawingWindow::usleep(unsigned long usecs)
     DrawingThread::usleep(usecs);
 }
 
+//--- DrawingWindow (protected methods) --------------------------------
+//! \cond show_protected
+
 /*!
  * \see QWidget
  */
 void DrawingWindow::closeEvent(QCloseEvent *ev)
 {
     timer.stop();
-    thread->terminate();
+    thread->exit();
     syncMutex.lock();
+    inputMutex.lock();
     terminateThread = true;     // this flag is needed for the case
                                 // where the following wakeAll() call
                                 // occurs between the
@@ -508,9 +764,14 @@ void DrawingWindow::closeEvent(QCloseEvent *ev)
                                 // mutex lock in safeLock() called
                                 // from sync()
     syncCondition.wakeAll();
+    inputCondition.wakeAll();
+    inputMutex.unlock();
     syncMutex.unlock();
     QWidget::closeEvent(ev);
-    thread->wait();
+    if (!thread->wait(250)) {
+        thread->terminate();
+        thread->wait();
+    }
 }
 
 /*!
@@ -526,7 +787,7 @@ void DrawingWindow::customEvent(QEvent *ev)
         close();
         break;
     case DrawTextRequest:
-        DrawTextEventtev = dynamic_cast<DrawTextEvent *>(ev);
+        DrawTextEvent *tev = dynamic_cast<DrawTextEvent *>(ev);
         realDrawText(tev->x, tev->y, tev->text, tev->flags);
         break;
     }
@@ -534,20 +795,28 @@ void DrawingWindow::customEvent(QEvent *ev)
 
 /*!
  * \see QWidget
+ *
+ * \bug                 expérimental
+ */
+void DrawingWindow::mousePressEvent(QMouseEvent *ev)
+{
+    inputMutex.lock();
+    mousePos = ev->pos();
+    mouseButton = ev->button();
+    ev->accept();
+    inputCondition.wakeAll();
+    inputMutex.unlock();
+}
+
+/*!
+ * \see QWidget
  */
 void DrawingWindow::keyPressEvent(QKeyEvent *ev)
 {
-    bool accept = true;
-    switch (ev->key()) {
-    case Qt::Key_Escape:
+    if (ev->key() == Qt::Key_Escape) {
+        ev->accept();
         close();
-        break;
-    default:
-        accept = false;
-        break;
     }
-    if (accept)
-        ev->accept();
 }
 
 /*!
@@ -588,11 +857,13 @@ void DrawingWindow::timerEvent(QTimerEvent *ev)
     }
 }
 
+// \endcond
+
 //--- DrawingWindow (private methods) ----------------------------------
 
 //! Fonction d'initialisation.
 /*!
- * Fonction appelée par les différents constructeurs.
+ * Fonction appelée par les différents constructeurs.
  *
  * \param fun           fonction de dessin
  */
@@ -621,7 +892,7 @@ void DrawingWindow::initialize(DrawingWindow::ThreadFunction fun)
  * \param color                 couleur
  */
 inline
-void DrawingWindow::setColor(const QColorcolor)
+void DrawingWindow::setColor(const QColor &color)
 {
     QPen pen(painter->pen());
     pen.setColor(color);
@@ -633,7 +904,7 @@ void DrawingWindow::setColor(const QColor& color)
  * \param color                 couleur
  */
 inline
-void DrawingWindow::setBgColor(const QColorcolor)
+void DrawingWindow::setBgColor(const QColor &color)
 {
     painter->setBackground(color);
 }
@@ -660,10 +931,10 @@ QColor DrawingWindow::getBgColor()
 
 //! Verrouille un mutex.
 /*!
- * S'assure que le thread courant ne peut pas être terminé s'il
- * détient un mutex.  Pendant de safeUnlock.
+ * S'assure que le thread courant ne peut pas être terminé s'il
+ * détient un mutex.  Pendant de safeUnlock.
  *
- * \param mutex         le mutex à verrouiller
+ * \param mutex         le mutex à verrouiller
  *
  * \see safeUnlock
  */
@@ -675,12 +946,12 @@ void DrawingWindow::safeLock(QMutex &mutex)
     mutex.lock();
 }
 
-//! Déverrouille un mutex.
+//! Déverrouille un mutex.
 /*!
- * S'assure que le thread courant ne peut pas être terminé s'il
- * détient un mutex.  Pendant de safeLock.
+ * S'assure que le thread courant ne peut pas être terminé s'il
+ * détient un mutex.  Pendant de safeLock.
  *
- * \param mutex         le mutex à déverrouiller
+ * \param mutex         le mutex à déverrouiller
  *
  * \see safeLock
  */
@@ -692,7 +963,7 @@ void DrawingWindow::safeUnlock(QMutex &mutex)
         thread->setTerminationEnabled(true);
 }
 
-//! Marque l'image entière comme non à jour.
+//! Marque l'image entière comme non à jour.
 inline
 void DrawingWindow::dirty()
 {
@@ -700,9 +971,9 @@ void DrawingWindow::dirty()
     dirtyRect = image->rect();
 }
 
-//! Marque un point de l'image comme non à jour.
+//! Marque un point de l'image comme non à jour.
 /*!
- * \param x, y          coordonnées du point
+ * \param x, y          coordonnées du point
  */
 inline
 void DrawingWindow::dirty(int x, int y)
@@ -710,13 +981,13 @@ void DrawingWindow::dirty(int x, int y)
     dirty(QRect(x, y, 1, 1));
 }
 
-//! Marque une zone de l'image comme non à jour.
+//! Marque une zone de l'image comme non à jour.
 /*!
- * La zone est définie par un rectangle dont les coordonnées de deux
- * sommets oppposés sont données.
+ * La zone est définie par un rectangle dont les coordonnées de deux
+ * sommets oppposés sont données.
  *
- * \param x1, y1        coordonnées d'un sommet du rectangle
- * \param x2, y2        coordonnées du sommet opposé du rectangle
+ * \param x1, y1        coordonnées d'un sommet du rectangle
+ * \param x2, y2        coordonnées du sommet opposé du rectangle
  */
 inline
 void DrawingWindow::dirty(int x1, int y1, int x2, int y2)
@@ -726,9 +997,9 @@ void DrawingWindow::dirty(int x1, int y1, int x2, int y2)
     dirty(r.normalized());
 }
 
-//! Marque une zone de l'image comme non à jour.
+//! Marque une zone de l'image comme non à jour.
 /*!
- * \param rect          rectangle délimitant la zone
+ * \param rect          rectangle délimitant la zone
  */
 void DrawingWindow::dirty(const QRect &rect)
 {
@@ -740,9 +1011,9 @@ void DrawingWindow::dirty(const QRect &rect)
     }
 }
 
-//! Génère un update si besoin.
+//! Génère un update si besoin.
 /*!
- * Génère une demande de mise à jour de la fenêtre (appel à update)
+ * Génère une demande de mise à jour de la fenêtre (appel à update)
  * s'il y en a besoin.
  */
 void DrawingWindow::mayUpdate()
@@ -781,7 +1052,7 @@ void DrawingWindow::realSync()
 
 //! Fonction bas-niveau pour drawText.
 /*!
- * Le rendu de texte doit être fait dans le thread principal.  D'où
+ * Le rendu de texte doit être fait dans le thread principal.  D'où
  * les manipulations tordues et la synchronisation qui s'en suit.
  *
  * \param x, y, text, flags     cf. drawText
@@ -818,7 +1089,7 @@ void DrawingWindow::realDrawText(int x, int y, const char *text, int flags)
         r.setTop(y);
     }
     syncMutex.lock();
-    painter->drawText(r, flags, text, &r);
+    painter->drawText(r, flags, QString::fromUtf8(text), &r);
     dirty(r);
     syncCondition.wakeAll();
     syncMutex.unlock();
@@ -834,7 +1105,7 @@ DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
 {
 }
 
-//! Démarre le thread si ce n'a pas encore été fait.
+//! Démarre le thread si ce n'a pas encore été fait.
 void DrawingThread::start_once(Priority priority)
 {
     if (!started_once) {