Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
5013bc182571650b4f47610bb22e8bd3e8ce5d4b
[graphlib.git] / DrawingWindow.cpp
1 #include "DrawingWindow.h"
2 #include <QApplication>
3 #include <QBasicTimer>
4 #include <QColor>
5 #include <QImage>
6 #include <QMutex>
7 #include <QPaintEvent>
8 #include <QPainter>
9 #include <QRect>
10 #include <QThread>
11 #include <QTimerEvent>
12 #include <QWaitCondition>
13
14 class DrawingThread: public QThread {
15 public:
16     DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f);
17     void start_once(Priority priority = InheritPriority);
18
19 protected:
20     void run();
21
22 private:
23     DrawingWindow &drawingWindow;
24     DrawingWindow::ThreadFunction threadFunction;
25     bool started_once;
26
27     friend class DrawingWindow;
28     friend class DrawingWindowPrivate;
29 };
30
31 class DrawingWindowPrivate {
32 public:
33     static const int paintInterval = 33;
34
35     DrawingWindow * const q;
36
37     QBasicTimer timer;
38     QMutex imageMutex;
39     QMutex syncMutex;
40     QWaitCondition syncCondition;
41     bool terminateThread;
42     int lockCount;
43
44     QImage *image;
45     QPainter *painter;
46
47     QColor fgColor;
48     QColor bgColor;
49
50     bool dirtyFlag;
51     QRect dirtyRect;
52
53     DrawingThread *thread;
54
55     DrawingWindowPrivate(DrawingWindow *w,
56                          DrawingWindow::ThreadFunction f);
57     ~DrawingWindowPrivate();
58
59     void initialize();
60
61     void applyColor();
62
63     void safeLock(QMutex &mutex);
64     void safeUnlock(QMutex &mutex);
65
66     void dirty();
67     void dirty(int x, int y);
68     void dirty(int x1, int y1, int x2, int y2);
69     void dirty(const QRect &rect);
70
71     void update();
72 };
73
74 //--- DrawingWindow ----------------------------------------------------
75
76 DrawingWindow::DrawingWindow(ThreadFunction f, int w, int h)
77     : QWidget()
78     , width(w)
79     , height(h)
80     , d(new DrawingWindowPrivate(this, f))
81 {
82     d->initialize();
83 }
84
85 DrawingWindow::DrawingWindow(QWidget *parent,
86                              ThreadFunction f, int w, int h)
87     : QWidget(parent)
88     , width(w)
89     , height(h)
90     , d(new DrawingWindowPrivate(this, f))
91 {
92     d->initialize();
93 }
94
95 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
96                              ThreadFunction f, int w, int h)
97     : QWidget(parent, flags)
98     , width(w)
99     , height(h)
100     , d(new DrawingWindowPrivate(this, f))
101 {
102     d->initialize();
103 }
104
105 DrawingWindow::~DrawingWindow()
106 {
107     delete d;
108 }
109
110 void DrawingWindow::setColor(float red, float green, float blue)
111 {
112     d->fgColor.setRgbF(red, green, blue);
113     d->applyColor();
114 }
115
116 void DrawingWindow::setColor(const char *name)
117 {
118     d->fgColor.setNamedColor(name);
119     d->applyColor();
120 }
121
122 void DrawingWindow::setBgColor(float red, float green, float blue)
123 {
124     d->bgColor.setRgbF(red, green, blue);
125 }
126
127 void DrawingWindow::setBgColor(const char *name)
128 {
129     d->bgColor.setNamedColor(name);
130 }
131
132 void DrawingWindow::clearGraph()
133 {
134     d->safeLock(d->imageMutex);
135     d->painter->fillRect(d->image->rect(), d->bgColor);    
136     d->dirty();
137     d->safeUnlock(d->imageMutex);
138 }
139
140 void DrawingWindow::drawPoint(int x, int y)
141 {
142     d->safeLock(d->imageMutex);
143     d->painter->drawPoint(x, y);
144     d->dirty(x, y);
145     d->safeUnlock(d->imageMutex);
146 }
147
148 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
149 {
150     d->safeLock(d->imageMutex);
151     d->painter->drawLine(x1, y1, x2, y2);
152     d->dirty(x1, y1, x2, y2);
153     d->safeUnlock(d->imageMutex);
154 }
155
156 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
157 {
158     QRect r;
159     r.setCoords(x1, y1, x2 - 1, y2 - 1);
160     r = r.normalized();
161     d->safeLock(d->imageMutex);
162     d->painter->drawRect(r);
163     r.adjust(0, 0, 1, 1);
164     d->dirty(r);
165     d->safeUnlock(d->imageMutex);
166 }
167
168 void DrawingWindow::fillRect(int x1, int y1, int x2, int y2)
169 {
170     d->painter->setBrush(d->fgColor);
171     drawRect(x1, y1, x2, y2);
172     d->painter->setBrush(Qt::NoBrush);
173 }
174
175 void DrawingWindow::drawCircle(int x, int y, int r)
176 {
177     QRect rect;
178     rect.setCoords(x - r, y - r, x + r - 1, y + r - 1);
179     d->safeLock(d->imageMutex);
180     d->painter->drawEllipse(rect);
181     rect.adjust(0, 0, 1, 1);
182     d->dirty(rect);
183     d->safeUnlock(d->imageMutex);
184 }
185
186 void DrawingWindow::fillCircle(int x, int y, int r)
187 {
188     d->painter->setBrush(d->fgColor);
189     drawCircle(x, y, r);
190     d->painter->setBrush(Qt::NoBrush);
191 }
192
193 bool DrawingWindow::sync(unsigned long time)
194 {
195     bool synced;
196     d->safeLock(d->syncMutex);
197     if (d->terminateThread) {
198         synced = false;
199     } else {
200         qApp->postEvent(this, new QEvent(QEvent::User));
201         synced = d->syncCondition.wait(&d->syncMutex, time);
202     }
203     d->safeUnlock(d->syncMutex);
204     return synced;
205 }
206
207 void DrawingWindow::sleep(unsigned long secs)
208 {
209     DrawingThread::sleep(secs);
210 }
211
212 void DrawingWindow::msleep(unsigned long msecs)
213 {
214     DrawingThread::msleep(msecs);
215 }
216
217 void DrawingWindow::usleep(unsigned long usecs)
218 {
219     DrawingThread::usleep(usecs);
220 }
221
222 void DrawingWindow::closeEvent(QCloseEvent *ev)
223 {
224     d->timer.stop();
225     d->thread->terminate();
226     d->syncMutex.lock();
227     d->terminateThread = true;  // this flag is needed for the case
228                                 // where the following wakeAll() call
229                                 // occurs between the
230                                 // setTerminationEnable(false) and the
231                                 // mutex lock in safeLock() called
232                                 // from sync()
233     d->syncCondition.wakeAll();
234     d->syncMutex.unlock();
235     QWidget::closeEvent(ev);
236     d->thread->wait();
237 }
238
239 void DrawingWindow::customEvent(QEvent *)
240 {
241     d->update();
242     qApp->sendPostedEvents(this, QEvent::UpdateLater);
243     qApp->sendPostedEvents(this, QEvent::UpdateRequest);
244     qApp->sendPostedEvents(this, QEvent::Paint);
245     qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
246                         QEventLoop::ExcludeSocketNotifiers |
247                         QEventLoop::DeferredDeletion |
248                         QEventLoop::X11ExcludeTimers);
249     qApp->flush();
250     qApp->syncX();
251     d->syncMutex.lock();
252     d->syncCondition.wakeAll();
253     d->syncMutex.unlock();
254 }
255
256 void DrawingWindow::keyPressEvent(QKeyEvent *ev)
257 {
258     bool accept = true;
259     switch (ev->key()) {
260     case Qt::Key_Escape:
261         close();
262         break;
263     default:
264         accept = false;
265         break;
266     }
267     if (accept)
268         ev->accept();
269 }
270
271 void DrawingWindow::paintEvent(QPaintEvent *ev)
272 {
273     QPainter widgetPainter(this);
274     d->imageMutex.lock();
275     QImage imageCopy(*d->image);
276     d->imageMutex.unlock();
277     QRect rect = ev->rect();
278     widgetPainter.drawImage(rect, imageCopy, rect);
279 }
280
281 void DrawingWindow::showEvent(QShowEvent *ev)
282 {
283     d->timer.start(d->paintInterval, this);
284     d->thread->start_once(QThread::IdlePriority);
285     QWidget::showEvent(ev);
286 }
287
288 void DrawingWindow::timerEvent(QTimerEvent *ev)
289 {
290     if (ev->timerId() == d->timer.timerId()) {
291         d->update();
292         d->timer.start(d->paintInterval, this);
293     } else {
294         QWidget::timerEvent(ev);
295     }
296 }
297
298 //--- DrawingWindowPrivate ---------------------------------------------
299
300 DrawingWindowPrivate::DrawingWindowPrivate(DrawingWindow *w,
301                                            DrawingWindow::ThreadFunction f)
302     : q(w)
303     , terminateThread(false)
304     , lockCount(0)
305     , image(new QImage(q->width, q->height, QImage::Format_RGB32))
306     , painter(new QPainter(image))
307     , thread(new DrawingThread(*q, f))
308 {
309 }
310
311 void DrawingWindowPrivate::initialize()
312 {
313     q->setFocusPolicy(Qt::StrongFocus);
314     q->setFixedSize(image->size());
315     q->setAttribute(Qt::WA_OpaquePaintEvent);
316     q->setFocus();
317
318     q->setColor("black");
319     q->setBgColor("white");
320     q->clearGraph();
321
322     dirtyFlag = false;
323 }
324
325 DrawingWindowPrivate::~DrawingWindowPrivate()
326 {
327     delete thread;
328     delete painter;
329     delete image;
330 }
331
332 inline
333 void DrawingWindowPrivate::applyColor()
334 {
335     QPen pen(painter->pen());
336     pen.setColor(fgColor);
337     painter->setPen(pen);
338 }
339
340 inline
341 void DrawingWindowPrivate::safeLock(QMutex &mutex)
342 {
343     if (lockCount++ == 0)
344         thread->setTerminationEnabled(false);
345     mutex.lock();
346 }
347
348 inline
349 void DrawingWindowPrivate::safeUnlock(QMutex &mutex)
350 {
351     mutex.unlock();
352     if (--lockCount == 0)
353         thread->setTerminationEnabled(true);
354 }
355
356 inline
357 void DrawingWindowPrivate::dirty()
358 {
359     dirtyFlag = true;
360     dirtyRect = image->rect();
361 }
362
363 inline
364 void DrawingWindowPrivate::dirty(int x, int y)
365 {
366     dirty(QRect(x, y, 1, 1));
367 }
368
369 inline
370 void DrawingWindowPrivate::dirty(int x1, int y1, int x2, int y2)
371 {
372     QRect r;
373     r.setCoords(x1, y1, x2, y2);
374     dirty(r.normalized());
375 }
376
377 void DrawingWindowPrivate::dirty(const QRect &rect)
378 {
379     if (dirtyFlag) {
380         dirtyRect |= rect;
381     } else {
382         dirtyFlag = true;
383         dirtyRect = rect;
384     }
385 }
386
387 void DrawingWindowPrivate::update()
388 {
389     imageMutex.lock();
390     bool dirty = dirtyFlag;
391     QRect rect = dirtyRect;
392     dirtyFlag = false;
393     imageMutex.unlock();
394     if (dirty)
395         q->update(rect);
396 }
397
398 //--- DrawingThread ----------------------------------------------------
399
400 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
401     : drawingWindow(w)
402     , threadFunction(f)
403     , started_once(false)
404 {
405 }
406
407 void DrawingThread::start_once(Priority priority)
408 {
409     if (!started_once) {
410         started_once = true;
411         start(priority);
412     }
413 }
414
415 void DrawingThread::run()
416 {
417     threadFunction(drawingWindow);
418 }