Logo AND Algorithmique Numérique Distribuée

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