Logo AND Algorithmique Numérique Distribuée

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