Logo AND Algorithmique Numérique Distribuée

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