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 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 //--- DrawingWindow ----------------------------------------------------
24
25 DrawingWindow::DrawingWindow(ThreadFunction f, int w, int h)
26     : QWidget()
27     , width(w)
28     , height(h)
29 {
30     initialize(f);
31 }
32
33 DrawingWindow::DrawingWindow(QWidget *parent,
34                              ThreadFunction f, int w, int h)
35     : QWidget(parent)
36     , width(w)
37     , height(h)
38 {
39     initialize(f);
40 }
41
42 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
43                              ThreadFunction f, int w, int h)
44     : QWidget(parent, flags)
45     , width(w)
46     , height(h)
47 {
48     initialize(f);
49 }
50
51 DrawingWindow::~DrawingWindow()
52 {
53     delete thread;
54     delete painter;
55     delete image;
56 }
57
58 void DrawingWindow::setColor(unsigned int color)
59 {
60     setColor(QColor::fromRgb(color));
61 }
62
63 void DrawingWindow::setColor(const char *name)
64 {
65     setColor(QColor(name));
66 }
67
68 void DrawingWindow::setColor(float red, float green, float blue)
69 {
70     setColor(QColor::fromRgbF(red, green, blue));
71 }
72
73 void DrawingWindow::setBgColor(unsigned int color)
74 {
75     setBgColor(QColor::fromRgb(color));
76 }
77
78 void DrawingWindow::setBgColor(const char *name)
79 {
80     setBgColor(QColor(name));
81 }
82
83 void DrawingWindow::setBgColor(float red, float green, float blue)
84 {
85     setBgColor(QColor::fromRgbF(red, green, blue));
86 }
87
88 void DrawingWindow::clearGraph()
89 {
90     safeLock(imageMutex);
91     painter->fillRect(image->rect(), getBgColor());
92     dirty();
93     safeUnlock(imageMutex);
94 }
95
96 void DrawingWindow::drawPoint(int x, int y)
97 {
98     safeLock(imageMutex);
99     painter->drawPoint(x, y);
100     dirty(x, y);
101     safeUnlock(imageMutex);
102 }
103
104 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
105 {
106     safeLock(imageMutex);
107     painter->drawLine(x1, y1, x2, y2);
108     dirty(x1, y1, x2, y2);
109     safeUnlock(imageMutex);
110 }
111
112 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
113 {
114     QRect r;
115     r.setCoords(x1, y1, x2 - 1, y2 - 1);
116     r = r.normalized();
117     safeLock(imageMutex);
118     painter->drawRect(r);
119     r.adjust(0, 0, 1, 1);
120     dirty(r);
121     safeUnlock(imageMutex);
122 }
123
124 void DrawingWindow::fillRect(int x1, int y1, int x2, int y2)
125 {
126     painter->setBrush(getColor());
127     drawRect(x1, y1, x2, y2);
128     painter->setBrush(Qt::NoBrush);
129 }
130
131 void DrawingWindow::drawCircle(int x, int y, int r)
132 {
133     QRect rect;
134     rect.setCoords(x - r, y - r, x + r - 1, y + r - 1);
135     safeLock(imageMutex);
136     painter->drawEllipse(rect);
137     rect.adjust(0, 0, 1, 1);
138     dirty(rect);
139     safeUnlock(imageMutex);
140 }
141
142 void DrawingWindow::fillCircle(int x, int y, int r)
143 {
144     painter->setBrush(getColor());
145     drawCircle(x, y, r);
146     painter->setBrush(Qt::NoBrush);
147 }
148
149 void DrawingWindow::drawText(int x, int y, const char *text, int flags)
150 {
151     QRect r(image->rect());
152     switch (flags & Qt::AlignHorizontal_Mask) {
153     case Qt::AlignRight:
154         r.setRight(x);
155         break;
156     case Qt::AlignHCenter:
157         if (x < width / 2)
158             r.setLeft(2 * x - width + 1);
159         else
160             r.setRight(2 * x);
161         break;
162     default:
163         r.setLeft(x);
164     }
165     switch (flags & Qt::AlignVertical_Mask) {
166     case Qt::AlignBottom:
167         r.setBottom(y);
168         break;
169     case Qt::AlignVCenter:
170         if (y < height / 2)
171             r.setTop(2 * y - height + 1);
172         else
173             r.setBottom(2 * y);
174         break;
175     default:
176         r.setTop(y);
177     }
178     safeLock(imageMutex);
179     painter->drawText(r, flags, text, &r);
180     dirty(r);
181     safeUnlock(imageMutex);
182 }
183
184 void DrawingWindow::drawTextBg(int x, int y, const char *text, int flags)
185 {
186     painter->setBackgroundMode(Qt::OpaqueMode);
187     drawText(x, y, text, flags);
188     painter->setBackgroundMode(Qt::TransparentMode);
189 }
190
191 unsigned int DrawingWindow::getPointColor(int x, int y)
192 {
193     return image->pixel(x, y);
194 }
195
196 bool DrawingWindow::sync(unsigned long time)
197 {
198     bool synced;
199     safeLock(syncMutex);
200     if (terminateThread) {
201         synced = false;
202     } else {
203         qApp->postEvent(this, new QEvent(QEvent::User));
204         synced = syncCondition.wait(&syncMutex, time);
205     }
206     safeUnlock(syncMutex);
207     return synced;
208 }
209
210 void DrawingWindow::closeGraph()
211 {
212     qApp->postEvent(this, new QEvent(QEvent::Type(QEvent::User + 1)));
213 }
214
215 void DrawingWindow::sleep(unsigned long secs)
216 {
217     DrawingThread::sleep(secs);
218 }
219
220 void DrawingWindow::msleep(unsigned long msecs)
221 {
222     DrawingThread::msleep(msecs);
223 }
224
225 void DrawingWindow::usleep(unsigned long usecs)
226 {
227     DrawingThread::usleep(usecs);
228 }
229
230 void DrawingWindow::closeEvent(QCloseEvent *ev)
231 {
232     timer.stop();
233     thread->terminate();
234     syncMutex.lock();
235     terminateThread = true;     // this flag is needed for the case
236                                 // where the following wakeAll() call
237                                 // occurs between the
238                                 // setTerminationEnable(false) and the
239                                 // mutex lock in safeLock() called
240                                 // from sync()
241     syncCondition.wakeAll();
242     syncMutex.unlock();
243     QWidget::closeEvent(ev);
244     thread->wait();
245 }
246
247 void DrawingWindow::customEvent(QEvent *ev)
248 {
249     switch ((int )ev->type()) {
250     case QEvent::User:
251         mayUpdate();
252         qApp->sendPostedEvents(this, QEvent::UpdateLater);
253         qApp->sendPostedEvents(this, QEvent::UpdateRequest);
254         qApp->sendPostedEvents(this, QEvent::Paint);
255         qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
256                             QEventLoop::ExcludeSocketNotifiers |
257                             QEventLoop::DeferredDeletion |
258                             QEventLoop::X11ExcludeTimers);
259         qApp->flush();
260         qApp->syncX();
261         syncMutex.lock();
262         syncCondition.wakeAll();
263         syncMutex.unlock();
264         break;
265     case QEvent::User + 1:
266         close();
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     timer.start(paintInterval, this);
299     thread->start_once(QThread::IdlePriority);
300     QWidget::showEvent(ev);
301 }
302
303 void DrawingWindow::timerEvent(QTimerEvent *ev)
304 {
305     if (ev->timerId() == timer.timerId()) {
306         mayUpdate();
307         timer.start(paintInterval, this);
308     } else {
309         QWidget::timerEvent(ev);
310     }
311 }
312
313 //--- DrawingWindow (private methods) ----------------------------------
314
315 void DrawingWindow::initialize(DrawingWindow::ThreadFunction f)
316 {
317     terminateThread = false;
318     lockCount = 0;
319     image = new QImage(width, height, QImage::Format_RGB32);
320     painter = new QPainter(image);
321     thread = new DrawingThread(*this, f);
322
323     setFocusPolicy(Qt::StrongFocus);
324     setFixedSize(image->size());
325     setAttribute(Qt::WA_OpaquePaintEvent);
326     setFocus();
327
328     setColor("black");
329     setBgColor("white");
330     clearGraph();
331
332     dirtyFlag = false;
333 }
334
335 inline
336 void DrawingWindow::setColor(const QColor& color)
337 {
338     QPen pen(painter->pen());
339     pen.setColor(color);
340     painter->setPen(pen);
341 }
342
343 inline
344 void DrawingWindow::setBgColor(const QColor& color)
345 {
346     painter->setBackground(color);
347 }
348
349 inline
350 QColor DrawingWindow::getColor()
351 {
352     return painter->pen().color();
353 }
354
355 inline
356 QColor DrawingWindow::getBgColor()
357 {
358     return painter->background().color();
359 }
360
361 inline
362 void DrawingWindow::safeLock(QMutex &mutex)
363 {
364     if (lockCount++ == 0)
365         thread->setTerminationEnabled(false);
366     mutex.lock();
367 }
368
369 inline
370 void DrawingWindow::safeUnlock(QMutex &mutex)
371 {
372     mutex.unlock();
373     if (--lockCount == 0)
374         thread->setTerminationEnabled(true);
375 }
376
377 inline
378 void DrawingWindow::dirty()
379 {
380     dirtyFlag = true;
381     dirtyRect = image->rect();
382 }
383
384 inline
385 void DrawingWindow::dirty(int x, int y)
386 {
387     dirty(QRect(x, y, 1, 1));
388 }
389
390 inline
391 void DrawingWindow::dirty(int x1, int y1, int x2, int y2)
392 {
393     QRect r;
394     r.setCoords(x1, y1, x2, y2);
395     dirty(r.normalized());
396 }
397
398 void DrawingWindow::dirty(const QRect &rect)
399 {
400     if (dirtyFlag) {
401         dirtyRect |= rect;
402     } else {
403         dirtyFlag = true;
404         dirtyRect = rect;
405     }
406 }
407
408 void DrawingWindow::mayUpdate()
409 {
410     imageMutex.lock();
411     bool dirty = dirtyFlag;
412     QRect rect = dirtyRect;
413     dirtyFlag = false;
414     imageMutex.unlock();
415     if (dirty)
416         update(rect);
417 }
418
419 //--- DrawingThread ----------------------------------------------------
420
421 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
422     : drawingWindow(w)
423     , threadFunction(f)
424     , started_once(false)
425 {
426 }
427
428 void DrawingThread::start_once(Priority priority)
429 {
430     if (!started_once) {
431         started_once = true;
432         start(priority);
433     }
434 }
435
436 void DrawingThread::run()
437 {
438     threadFunction(drawingWindow);
439 }