Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
.
[graphlib.git] / DrawingWindow.cpp
1 /*
2  * Copyright (c) 2007, Arnaud Giersch <arnaud.giersch@iut-bm.univ-fcomte.fr>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote
14  *    products derived from this software without specific prior
15  *    written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "DrawingWindow.h"
31 #include <QApplication>
32 #include <QPaintEvent>
33 #include <QThread>
34 #include <QTimerEvent>
35
36 /*! \class DrawingWindow
37  *  \brief Fenêtre de dessin.
38  *
39  * \author Arnaud Giersch <arnaud.giersch@iut-bm.univ-fcomte.fr>
40  * \date novembre 2007
41  *
42  * Cette classe décrit un widget Qt permettant d'écrire des
43  * applications graphiques simples.  Pour cela, il faut définir une
44  * fonction de dessin.  Cette fonction ne retourne rien et prend comme
45  * unique paramètre une référence vers un objet de class
46  * DrawingWindow.
47  *
48  * La fonction devra ensuite être passée en paramètre pour les
49  * constructeurs de la classe, ainsi que les dimension requises pour
50  * la fenêtre graphique.  Le programme est ensuite compilé comme
51  * n'importe programme Qt.
52  *
53  * Concrètement, la fonction sera exécutée dans un nouveau thread,
54  * tandis que le thread principal s'occupera de la gestion des
55  * évènements et du rendu.
56  *
57  * <b>NB.</b> Pour toutes les méthodes de dessin, le coin en haut à gauche
58  * de la fenêtre a les coordonnées (0, 0).  Le coin en bas à droite de
59  * la fenêtre a les coordonnées (largeur - 1, hauteur - 1), si la
60  * fenêtre est de dimension largeur × hauteur.
61  */
62
63 /*! \example hello.cpp
64  *
65  * Voir le code source à la fin de la page.  Pour compiler et exécuter
66  * ce programme, il faut :
67  *
68  * <b>1. Créer le fichier \c hello.pro</b>
69  *
70  * Pour simplifier, ce fichier contient la liste des fichiers sources
71  * composant le programme.
72  *
73  * \include hello.pro
74  *
75  * <b>2. Créer le fichier \c Makefile avec la commande :</b>
76  *
77  * \verbatim qmake-qt4 hello.pro \endverbatim
78  * ou tout simplement :
79  * \verbatim qmake-qt4 \endverbatim
80  *
81  * <b>3. Compiler le programme avec la commande :</b>
82  *
83  * \verbatim make hello \endverbatim
84  * ou tout simplement :
85  * \verbatim make \endverbatim
86  *
87  * <b>4. Exécuter le programme avec la commande :</b>
88  *
89  * \verbatim ./exemple \endverbatim
90  *
91  * <b>Code source de l'exemple</b>
92  */
93
94 /*! \example exemple.cpp
95  *
96  * Un exemple un peu plus sophistiqué.
97  */
98
99 //! Classe de thread.
100 class DrawingThread: public QThread {
101 public:
102     DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f);
103     void start_once(Priority priority = InheritPriority);
104
105 protected:
106     void run();
107
108 private:
109     DrawingWindow &drawingWindow;
110     DrawingWindow::ThreadFunction threadFunction;
111     bool started_once;
112
113     friend class DrawingWindow;
114 };
115
116 enum UserEvents {
117     SyncRequest = QEvent::User, //!< Demande de synchronisation.
118     CloseRequest,               //!< Demande de fermeture de la fenêtre.
119     DrawTextRequest,            //!< Demande d'écriture de texte.
120 };
121
122 //! Demande de synchronisation.
123 class SyncRequestEvent: public QEvent {
124 public:
125     SyncRequestEvent(): QEvent(static_cast<QEvent::Type>(SyncRequest))
126     { }
127 };
128
129 //! Demande de fermeture de fenêtre.
130 class CloseRequestEvent: public QEvent {
131 public:
132     CloseRequestEvent(): QEvent(static_cast<QEvent::Type>(CloseRequest))
133     { }
134 };
135
136 //! Demande de tracé de texte. 
137 class DrawTextEvent: public QEvent {
138 public:
139     const int x;
140     const int y;
141     const char *text;
142     const int flags;
143     DrawTextEvent(int x_, int y_, const char* text_, int flags_)
144         : QEvent(static_cast<QEvent::Type>(DrawTextRequest))
145         , x(x_), y(y_), text(text_), flags(flags_)
146     { }
147 };
148
149 //--- DrawingWindow ----------------------------------------------------
150
151 /*! \file DrawingWindow.h
152  *  \brief Classe DrawingWindow.
153  */
154
155 /*! \typedef DrawingWindow::ThreadFunction
156  *  \brief Type de la fonction de dessin, passée en paramètre de construction.
157  */
158 /*! \var DrawingWindow::DEFAULT_WIDTH
159  *  \brief Largeur par défaut de la fenêtre.
160  */
161 /*! \var DrawingWindow::DEFAULT_HEIGHT
162  *  \brief Hauteur par défaut de la fenêtre.
163  */
164 /*! \var DrawingWindow::width
165  *  \brief Largeur de la fenêtre.
166  */
167 /*! \var DrawingWindow::height
168  *  \brief Hauteur de la fenêtre.
169  */
170 /*! \var DrawingWindow::paintInterval
171  *  \brief Intervalle de temps entre deux rendus (ms).
172  */
173
174 //! Constructeur.
175 /*!
176  * Construit une nouvelle fenêtre de dessin avec les dimensions
177  * passées en paramètres.  La fonction fun sera exécutée dans un
178  * nouveau thread.
179  *
180  * \param fun           fonction de dessin
181  * \param width_        largeur de la fenêtre
182  * \param height_       hauteur de la fenêtre
183  *
184  * \see QWidget
185  */
186 DrawingWindow::DrawingWindow(ThreadFunction fun, int width_, int height_)
187     : QWidget()
188     , width(width_)
189     , height(height_)
190 {
191     initialize(fun);
192 }
193
194 //! Constructeur.
195 /*!
196  * Construit un nouveau widget de dessin avec les dimensions passées
197  * en paramètres.  La fonction fun sera exécutée dans un nouveau
198  * thread.
199  *
200  * \param parent        widget parent
201  * \param fun           fonction de dessin
202  * \param width_        largeur de la fenêtre
203  * \param height_       hauteur de la fenêtre
204  *
205  * \see QWidget
206  */
207 DrawingWindow::DrawingWindow(QWidget *parent,
208                              ThreadFunction fun, int width_, int height_)
209     : QWidget(parent)
210     , width(width_)
211     , height(height_)
212 {
213     initialize(fun);
214 }
215
216 //! Constructeur.
217 /*!
218  * Construit un nouveau widget de dessin avec les dimensions passées
219  * en paramètres.  La fonction fun sera exécutée dans un nouveau
220  * thread.
221  *
222  * \param parent        widget parent
223  * \param flags         flags passés au constructeur de QWidget
224  * \param fun           fonction de dessin
225  * \param width_        largeur de la fenêtre
226  * \param height_       hauteur de la fenêtre
227  *
228  * \see QWidget
229  */
230 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
231                              ThreadFunction fun, int width_, int height_)
232     : QWidget(parent, flags)
233     , width(width_)
234     , height(height_)
235 {
236     initialize(fun);
237 }
238
239 //! Destructeur.
240 DrawingWindow::~DrawingWindow()
241 {
242     delete thread;
243     delete painter;
244     delete image;
245 }
246
247 //! Change la couleur de dessin.
248 /*!
249  * La couleur est un entier, tel que retourné par getPointColor.
250  * Normalement de la forme #00RRGGBB.
251  *
252  * \param color         couleur
253  *
254  * \see setColor(const char *), setColor(float, float, float),
255  *      setBgColor(unsigned int),
256  *      getPointColor
257  */
258 void DrawingWindow::setColor(unsigned int color)
259 {
260     setColor(QColor::fromRgb(color));
261 }
262
263 //! Change la couleur de dessin.
264 /*!
265  * Le nom de couleur est de la forme "black", "white", "red", "blue", ...
266  *
267  * \param name          nom de couleur
268  *
269  * \see setColor(unsigned int), setColor(float, float, float),
270  *      setBgColor(const char *)
271  * \see http://www.w3.org/TR/SVG/types.html#ColorKeywords
272  */
273 void DrawingWindow::setColor(const char *name)
274 {
275     setColor(QColor(name));
276 }
277
278 //! Change la couleur de dessin.
279 /*!
280  * Les composantes de rouge, vert et bleu de la couleur doivent être
281  * compris entre 0 et 1.  Si le trois composantes sont à 0, on obtient
282  * du noir; si les trois composantes sont à 1, on obtient du blanc.
283  *
284  * \param red           composante de rouge
285  * \param green         composante de vert
286  * \param blue          composante de bleu
287  *
288  * \see setColor(unsigned int), setColor(const char *),
289  *      setBgColor(float, float, float)
290  */
291 void DrawingWindow::setColor(float red, float green, float blue)
292 {
293     setColor(QColor::fromRgbF(red, green, blue));
294 }
295
296 //! Change la couleur de fond.
297 /*!
298  * \param color         couleur
299  *
300  * \see setBgColor(const char *), setBgColor(float, float, float),
301  *      setColor(unsigned int),
302  *      getPointColor,
303  *      clearGraph
304  */
305 void DrawingWindow::setBgColor(unsigned int color)
306 {
307     setBgColor(QColor::fromRgb(color));
308 }
309
310 //! Change la couleur de fond.
311 /*!
312  * \param name          nom de couleur
313  *
314  * \see setBgColor(unsigned int), setBgColor(float, float, float),
315  *      setColor(const char *),
316  *      clearGraph
317  * \see http://www.w3.org/TR/SVG/types.html#ColorKeywords
318  */
319 void DrawingWindow::setBgColor(const char *name)
320 {
321     setBgColor(QColor(name));
322 }
323
324 //! Change la couleur de fond.
325 /*!
326  * \param red           composante de rouge
327  * \param green         composante de vert
328  * \param blue          composante de bleu
329  *
330  * \see setBgColor(unsigned int), setBgColor(const char *),
331  *      setColor(float, float, float),
332  *      clearGraph
333  */
334 void DrawingWindow::setBgColor(float red, float green, float blue)
335 {
336     setBgColor(QColor::fromRgbF(red, green, blue));
337 }
338
339 //! Efface la fenêtre.
340 /*!
341  * La fenêtre est effacée avec la couleur de fond courante.
342  *
343  * \see setBgColor
344  */
345 void DrawingWindow::clearGraph()
346 {
347     safeLock(imageMutex);
348     painter->fillRect(image->rect(), getBgColor());
349     dirty();
350     safeUnlock(imageMutex);
351 }
352
353 //! Dessine un point.
354 /*!
355  * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de
356  * dessin courante.
357  *
358  * \param x, y          coordonnées du point
359  *
360  * \see setColor
361  */
362 void DrawingWindow::drawPoint(int x, int y)
363 {
364     safeLock(imageMutex);
365     painter->drawPoint(x, y);
366     dirty(x, y);
367     safeUnlock(imageMutex);
368 }
369
370 //! Dessine un segment.
371 /*!
372  * Dessine un segement de droite entre les coordonnées (x1, y1) et
373  * (x2, y2), avec la couleur de dessin courante.
374  *
375  * \param x1, y1        coordonnées d'une extrémité du segment
376  * \param x2, y2        coordonnées de l'autre extrémité du segment
377  *
378  * \see setColor
379  */
380 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
381 {
382     safeLock(imageMutex);
383     painter->drawLine(x1, y1, x2, y2);
384     dirty(x1, y1, x2, y2);
385     safeUnlock(imageMutex);
386 }
387
388 //! Dessine un rectangle.
389 /*!
390  * Dessine le rectangle parallèle aux axes et défini par les
391  * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
392  * la couleur de dessin courante.
393  *
394  * \param x1, y1        coordonnées d'un sommet du rectangle
395  * \param x2, y2        coordonnées du sommet opposé du rectangle
396  *
397  * \see fillRect, setColor
398  */
399 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
400 {
401     QRect r;
402     r.setCoords(x1, y1, x2 - 1, y2 - 1);
403     r = r.normalized();
404     safeLock(imageMutex);
405     painter->drawRect(r);
406     r.adjust(0, 0, 1, 1);
407     dirty(r);
408     safeUnlock(imageMutex);
409 }
410
411 //! Dessine un rectangle plein.
412 /*!
413  * Dessine le rectangle plein parallèle aux axes et défini par les
414  * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
415  * la couleur de dessin courante.
416  *
417  * \param x1, y1        coordonnées d'un sommet du rectangle
418  * \param x2, y2        coordonnées du sommet opposé du rectangle
419  *
420  * \see drawRect, setColor
421  */
422 void DrawingWindow::fillRect(int x1, int y1, int x2, int y2)
423 {
424     painter->setBrush(getColor());
425     drawRect(x1, y1, x2, y2);
426     painter->setBrush(Qt::NoBrush);
427 }
428
429 //! Dessine un cercle.
430 /*!
431  * Dessine un cercle de centre (x, y) et de rayon r.  Utilise la
432  * couleur de dessin courante.
433  *
434  * \param x, y          coordonnées du centre du cercle
435  * \param r             rayon du cercle
436  *
437  * \see fillCircle, setColor
438  */
439 void DrawingWindow::drawCircle(int x, int y, int r)
440 {
441     QRect rect;
442     rect.setCoords(x - r, y - r, x + r - 1, y + r - 1);
443     safeLock(imageMutex);
444     painter->drawEllipse(rect);
445     rect.adjust(0, 0, 1, 1);
446     dirty(rect);
447     safeUnlock(imageMutex);
448 }
449
450 //! Dessine un disque.
451 /*!
452  * Dessine un disque (cercle plein) de centre (x, y) et de rayon r.
453  * Utilise la couleur de dessin courante.
454  *
455  * \param x, y          coordonnées du centre du disque
456  * \param r             rayon du disque
457  *
458  * \see drawCircle, setColor
459  */
460 void DrawingWindow::fillCircle(int x, int y, int r)
461 {
462     painter->setBrush(getColor());
463     drawCircle(x, y, r);
464     painter->setBrush(Qt::NoBrush);
465 }
466
467 //! Écrit du texte.
468 /*!
469  * Écrit le texte text, aux coordonnées (x, y) et avec les paramètres
470  * d'alignement flags.  Le texte est écrit avec la couleur de dessin
471  * courante.  Les flags sont une combinaison (ou binaire) de
472  * Qt::AlignLeft, Qt::AligneRight, Qt::AlignHCenter, Qt::AlignTop,
473  * Qt::AlignBottom, Qt::AlignVCenter, Qt::AlignCenter.  Par défaut, le
474  * texte est aligné en haut à gauche.
475  *
476  * \param x, y          coordonnées du texte
477  * \param text          texte à écrire
478  * \param flags         paramètres d'alignement
479  *
480  * \see drawTextBg, setColor
481  * \see QPainter::drawText
482  */
483 void DrawingWindow::drawText(int x, int y, const char *text, int flags)
484 {
485     safeLock(syncMutex);
486     if (!terminateThread) {
487         qApp->postEvent(this, new DrawTextEvent(x, y, text, flags));
488         syncCondition.wait(&syncMutex);
489     }
490     safeUnlock(syncMutex);
491 }
492
493 //! Écrit du texte sur fond coloré.
494 /*!
495  * Écrit du texte comme drawText, mais l'arrière-plan est coloré avec
496  * la couleur de fond courante.
497  *
498  * \param x, y          coordonnées du texte
499  * \param text          texte à écrire
500  * \param flags         paramètres d'alignement
501  *
502  * \see drawText, setColor, setColorBg
503  */
504 void DrawingWindow::drawTextBg(int x, int y, const char *text, int flags)
505 {
506     painter->setBackgroundMode(Qt::OpaqueMode);
507     drawText(x, y, text, flags);
508     painter->setBackgroundMode(Qt::TransparentMode);
509 }
510
511
512 //! Retourne la couleur d'un pixel.
513 /*!
514  * Retourne la couleur du pixel de coordonnées (x, y).  La valeur
515  * retournée peut servir de paramètres à setColor(unsigned int) ou
516  * setBgColor(unsigned int).
517  *
518  * \param x, y          coordonnées du pixel
519  * \return              couleur du pixel
520  *
521  * \see setColor(unsigned int), setBgColor(unsigned int)
522  */
523 unsigned int DrawingWindow::getPointColor(int x, int y)
524 {
525     return image->pixel(x, y);
526 }
527
528 //! Synchronise le contenu de la fenêtre.
529 /*!
530  * Pour des raisons d'efficacités, le résultat des fonctions de dessin
531  * n'est pas affiché immédiatement.  L'appel à sync permet de
532  * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
533  * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
534  * soit à jour.
535  *
536  * \param time          durée maximale de l'attente
537  * \return              true si la fenêtre a pu être synchronisée
538  */
539 bool DrawingWindow::sync(unsigned long time)
540 {
541     bool synced;
542     safeLock(syncMutex);
543     if (terminateThread) {
544         synced = false;
545     } else {
546         qApp->postEvent(this, new SyncRequestEvent());
547         synced = syncCondition.wait(&syncMutex, time);
548     }
549     safeUnlock(syncMutex);
550     return synced;
551 }
552
553 //! Ferme la fenêtre graphique.
554 void DrawingWindow::closeGraph()
555 {
556     qApp->postEvent(this, new CloseRequestEvent());
557 }
558
559 //! Suspend l'exécution pendant un certain temps.
560 /*!
561  * \param secs          temps d'attente en seconde
562  */
563 void DrawingWindow::sleep(unsigned long secs)
564 {
565     DrawingThread::sleep(secs);
566 }
567
568 //! Suspend l'exécution pendant un certain temps.
569 /*!
570  * \param msecs          temps d'attente en millisecondes
571  */
572 void DrawingWindow::msleep(unsigned long msecs)
573 {
574     DrawingThread::msleep(msecs);
575 }
576
577 //! Suspend l'exécution pendant un certain temps.
578 /*!
579  * \param usecs          temps d'attente en microsecondes
580  */
581 void DrawingWindow::usleep(unsigned long usecs)
582 {
583     DrawingThread::usleep(usecs);
584 }
585
586 /*!
587  * \see QWidget
588  */
589 void DrawingWindow::closeEvent(QCloseEvent *ev)
590 {
591     timer.stop();
592     thread->terminate();
593     syncMutex.lock();
594     terminateThread = true;     // this flag is needed for the case
595                                 // where the following wakeAll() call
596                                 // occurs between the
597                                 // setTerminationEnable(false) and the
598                                 // mutex lock in safeLock() called
599                                 // from sync()
600     syncCondition.wakeAll();
601     syncMutex.unlock();
602     QWidget::closeEvent(ev);
603     thread->wait();
604 }
605
606 /*!
607  * \see QWidget
608  */
609 void DrawingWindow::customEvent(QEvent *ev)
610 {
611     switch ((int )ev->type()) {
612     case SyncRequest:
613         realSync();
614         break;
615     case CloseRequest:
616         close();
617         break;
618     case DrawTextRequest:
619         DrawTextEvent* tev = dynamic_cast<DrawTextEvent *>(ev);
620         realDrawText(tev->x, tev->y, tev->text, tev->flags);
621         break;
622     }
623 }
624
625 /*!
626  * \see QWidget
627  */
628 void DrawingWindow::keyPressEvent(QKeyEvent *ev)
629 {
630     bool accept = true;
631     switch (ev->key()) {
632     case Qt::Key_Escape:
633         close();
634         break;
635     default:
636         accept = false;
637         break;
638     }
639     if (accept)
640         ev->accept();
641 }
642
643 /*!
644  * \see QWidget
645  */
646 void DrawingWindow::paintEvent(QPaintEvent *ev)
647 {
648     QPainter widgetPainter(this);
649     imageMutex.lock();
650     QImage imageCopy(*image);
651     imageMutex.unlock();
652     QRect rect = ev->rect();
653     widgetPainter.drawImage(rect, imageCopy, rect);
654 }
655
656 /*!
657  * \see QWidget
658  */
659 void DrawingWindow::showEvent(QShowEvent *ev)
660 {
661     QWidget::showEvent(ev);
662     qApp->flush();
663     qApp->syncX();
664     timer.start(paintInterval, this);
665     thread->start_once(QThread::IdlePriority);
666 }
667
668 /*!
669  * \see QWidget
670  */
671 void DrawingWindow::timerEvent(QTimerEvent *ev)
672 {
673     if (ev->timerId() == timer.timerId()) {
674         mayUpdate();
675         timer.start(paintInterval, this);
676     } else {
677         QWidget::timerEvent(ev);
678     }
679 }
680
681 //--- DrawingWindow (private methods) ----------------------------------
682
683 //! Fonction d'initialisation.
684 /*!
685  * Fonction appelée par les différents constructeurs.
686  *
687  * \param fun           fonction de dessin
688  */
689 void DrawingWindow::initialize(DrawingWindow::ThreadFunction fun)
690 {
691     terminateThread = false;
692     lockCount = 0;
693     image = new QImage(width, height, QImage::Format_RGB32);
694     painter = new QPainter(image);
695     thread = new DrawingThread(*this, fun);
696
697     setFocusPolicy(Qt::StrongFocus);
698     setFixedSize(image->size());
699     setAttribute(Qt::WA_OpaquePaintEvent);
700     setFocus();
701
702     setColor("black");
703     setBgColor("white");
704     clearGraph();
705
706     dirtyFlag = false;
707 }
708
709 //! Change la couleur de dessin.
710 /*!
711  * \param color                 couleur
712  */
713 inline
714 void DrawingWindow::setColor(const QColor& color)
715 {
716     QPen pen(painter->pen());
717     pen.setColor(color);
718     painter->setPen(pen);
719 }
720
721 //! Change la couleur de fond.
722 /*!
723  * \param color                 couleur
724  */
725 inline
726 void DrawingWindow::setBgColor(const QColor& color)
727 {
728     painter->setBackground(color);
729 }
730
731 //! Retourne la couleur de dessin courante.
732 /*!
733  * \return              couleur de dessin courante
734  */
735 inline
736 QColor DrawingWindow::getColor()
737 {
738     return painter->pen().color();
739 }
740
741 //! Retourne la couleur de fond courante.
742 /*!
743  * \return              couleur de fond courante
744  */
745 inline
746 QColor DrawingWindow::getBgColor()
747 {
748     return painter->background().color();
749 }
750
751 //! Verrouille un mutex.
752 /*!
753  * S'assure que le thread courant ne peut pas être terminé s'il
754  * détient un mutex.  Pendant de safeUnlock.
755  *
756  * \param mutex         le mutex à verrouiller
757  *
758  * \see safeUnlock
759  */
760 inline
761 void DrawingWindow::safeLock(QMutex &mutex)
762 {
763     if (lockCount++ == 0)
764         thread->setTerminationEnabled(false);
765     mutex.lock();
766 }
767
768 //! Déverrouille un mutex.
769 /*!
770  * S'assure que le thread courant ne peut pas être terminé s'il
771  * détient un mutex.  Pendant de safeLock.
772  *
773  * \param mutex         le mutex à déverrouiller
774  *
775  * \see safeLock
776  */
777 inline
778 void DrawingWindow::safeUnlock(QMutex &mutex)
779 {
780     mutex.unlock();
781     if (--lockCount == 0)
782         thread->setTerminationEnabled(true);
783 }
784
785 //! Marque l'image entière comme non à jour.
786 inline
787 void DrawingWindow::dirty()
788 {
789     dirtyFlag = true;
790     dirtyRect = image->rect();
791 }
792
793 //! Marque un point de l'image comme non à jour.
794 /*!
795  * \param x, y          coordonnées du point
796  */
797 inline
798 void DrawingWindow::dirty(int x, int y)
799 {
800     dirty(QRect(x, y, 1, 1));
801 }
802
803 //! Marque une zone de l'image comme non à jour.
804 /*!
805  * La zone est définie par un rectangle dont les coordonnées de deux
806  * sommets oppposés sont données.
807  *
808  * \param x1, y1        coordonnées d'un sommet du rectangle
809  * \param x2, y2        coordonnées du sommet opposé du rectangle
810  */
811 inline
812 void DrawingWindow::dirty(int x1, int y1, int x2, int y2)
813 {
814     QRect r;
815     r.setCoords(x1, y1, x2, y2);
816     dirty(r.normalized());
817 }
818
819 //! Marque une zone de l'image comme non à jour.
820 /*!
821  * \param rect          rectangle délimitant la zone
822  */
823 void DrawingWindow::dirty(const QRect &rect)
824 {
825     if (dirtyFlag) {
826         dirtyRect |= rect;
827     } else {
828         dirtyFlag = true;
829         dirtyRect = rect;
830     }
831 }
832
833 //! Génère un update si besoin.
834 /*!
835  * Génère une demande de mise à jour de la fenêtre (appel à update)
836  * s'il y en a besoin.
837  */
838 void DrawingWindow::mayUpdate()
839 {
840     imageMutex.lock();
841     bool dirty = dirtyFlag;
842     QRect rect = dirtyRect;
843     dirtyFlag = false;
844     imageMutex.unlock();
845     if (dirty)
846         update(rect);
847 }
848
849 //! Fonction bas-niveau pour sync.
850 /*!
851  * Fonction de synchronisation dans le thread principal.
852  *
853  * \see sync, customEvent
854  */
855 void DrawingWindow::realSync()
856 {
857     mayUpdate();
858     qApp->sendPostedEvents(this, QEvent::UpdateLater);
859     qApp->sendPostedEvents(this, QEvent::UpdateRequest);
860     qApp->sendPostedEvents(this, QEvent::Paint);
861     qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
862                         QEventLoop::ExcludeSocketNotifiers |
863                         QEventLoop::DeferredDeletion |
864                         QEventLoop::X11ExcludeTimers);
865     qApp->flush();
866     qApp->syncX();
867     syncMutex.lock();
868     syncCondition.wakeAll();
869     syncMutex.unlock();
870 }
871
872 //! Fonction bas-niveau pour drawText.
873 /*!
874  * Le rendu de texte doit être fait dans le thread principal.  D'où
875  * les manipulations tordues et la synchronisation qui s'en suit.
876  *
877  * \param x, y, text, flags     cf. drawText
878  *
879  * \see drawText, customEvent
880  */
881 void DrawingWindow::realDrawText(int x, int y, const char *text, int flags)
882 {
883     QRect r(image->rect());
884     switch (flags & Qt::AlignHorizontal_Mask) {
885     case Qt::AlignRight:
886         r.setRight(x);
887         break;
888     case Qt::AlignHCenter:
889         if (x < width / 2)
890             r.setLeft(2 * x - width + 1);
891         else
892             r.setRight(2 * x);
893         break;
894     default:
895         r.setLeft(x);
896     }
897     switch (flags & Qt::AlignVertical_Mask) {
898     case Qt::AlignBottom:
899         r.setBottom(y);
900         break;
901     case Qt::AlignVCenter:
902         if (y < height / 2)
903             r.setTop(2 * y - height + 1);
904         else
905             r.setBottom(2 * y);
906         break;
907     default:
908         r.setTop(y);
909     }
910     syncMutex.lock();
911     painter->drawText(r, flags, text, &r);
912     dirty(r);
913     syncCondition.wakeAll();
914     syncMutex.unlock();
915 }
916
917 //--- DrawingThread ----------------------------------------------------
918
919 //! Constructeur.
920 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
921     : drawingWindow(w)
922     , threadFunction(f)
923     , started_once(false)
924 {
925 }
926
927 //! Démarre le thread si ce n'a pas encore été fait.
928 void DrawingThread::start_once(Priority priority)
929 {
930     if (!started_once) {
931         started_once = true;
932         start(priority);
933     }
934 }
935
936 //! La vraie fonction pour le thread.
937 void DrawingThread::run()
938 {
939     threadFunction(drawingWindow);
940 }