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