Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
9af145ffee6d68bd209cae5b66dccf3b8e956a78
[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 QCloseEvent());
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     qDebug(">>>CLOSING<<<\n");
189     char x = 'A';
190     qDebug(">>> %c <<<\n", x++);
191     timer.stop();
192     qDebug(">>> %c <<<\n", x++);
193     thread->terminate();
194     qDebug(">>> %c <<<\n", x++);
195     syncMutex.lock();
196     qDebug(">>> %c <<<\n", x++);
197     terminateThread = true;     // this flag is needed for the case
198                                 // where the following wakeAll() call
199                                 // occurs between the
200                                 // setTerminationEnable(false) and the
201                                 // mutex lock in safeLock() called
202                                 // from sync()
203     qDebug(">>> %c <<<\n", x++);
204     syncCondition.wakeAll();
205     qDebug(">>> %c <<<\n", x++);
206     syncMutex.unlock();
207     qDebug(">>> %c <<<\n", x++);
208     QWidget::closeEvent(ev);
209     qDebug(">>> %c <<<\n", x++);
210     thread->wait();
211     qDebug(">>> %c <<<\n", x++);
212     qDebug(">>>CLOSED<<<\n");
213 }
214
215 void DrawingWindow::customEvent(QEvent *)
216 {
217     mayUpdate();
218     qApp->sendPostedEvents(this, QEvent::UpdateLater);
219     qApp->sendPostedEvents(this, QEvent::UpdateRequest);
220     qApp->sendPostedEvents(this, QEvent::Paint);
221     qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
222                         QEventLoop::ExcludeSocketNotifiers |
223                         QEventLoop::DeferredDeletion |
224                         QEventLoop::X11ExcludeTimers);
225     qApp->flush();
226     qApp->syncX();
227     syncMutex.lock();
228     syncCondition.wakeAll();
229     syncMutex.unlock();
230 }
231
232 void DrawingWindow::keyPressEvent(QKeyEvent *ev)
233 {
234     bool accept = true;
235     switch (ev->key()) {
236     case Qt::Key_Escape:
237         close();
238         break;
239     default:
240         accept = false;
241         break;
242     }
243     if (accept)
244         ev->accept();
245 }
246
247 void DrawingWindow::paintEvent(QPaintEvent *ev)
248 {
249     QPainter widgetPainter(this);
250     imageMutex.lock();
251     QImage imageCopy(*image);
252     imageMutex.unlock();
253     QRect rect = ev->rect();
254     widgetPainter.drawImage(rect, imageCopy, rect);
255 }
256
257 void DrawingWindow::showEvent(QShowEvent *ev)
258 {
259     timer.start(paintInterval, this);
260     thread->start_once(QThread::IdlePriority);
261     QWidget::showEvent(ev);
262 }
263
264 void DrawingWindow::timerEvent(QTimerEvent *ev)
265 {
266     if (ev->timerId() == timer.timerId()) {
267         mayUpdate();
268         timer.start(paintInterval, this);
269     } else {
270         QWidget::timerEvent(ev);
271     }
272 }
273
274 //--- DrawingWindow (private methods) ----------------------------------
275
276 void DrawingWindow::initialize(DrawingWindow::ThreadFunction f)
277 {
278     terminateThread = false;
279     lockCount = 0;
280     image = new QImage(width, height, QImage::Format_RGB32);
281     painter = new QPainter(image);
282     thread = new DrawingThread(*this, f);
283
284     setFocusPolicy(Qt::StrongFocus);
285     setFixedSize(image->size());
286     setAttribute(Qt::WA_OpaquePaintEvent);
287     setFocus();
288
289     setColor("black");
290     setBgColor("white");
291     clearGraph();
292
293     dirtyFlag = false;
294 }
295
296 inline
297 void DrawingWindow::applyColor()
298 {
299     QPen pen(painter->pen());
300     pen.setColor(fgColor);
301     painter->setPen(pen);
302 }
303
304 inline
305 void DrawingWindow::safeLock(QMutex &mutex)
306 {
307     if (lockCount++ == 0)
308         thread->setTerminationEnabled(false);
309     mutex.lock();
310 }
311
312 inline
313 void DrawingWindow::safeUnlock(QMutex &mutex)
314 {
315     mutex.unlock();
316     if (--lockCount == 0)
317         thread->setTerminationEnabled(true);
318 }
319
320 inline
321 void DrawingWindow::dirty()
322 {
323     dirtyFlag = true;
324     dirtyRect = image->rect();
325 }
326
327 inline
328 void DrawingWindow::dirty(int x, int y)
329 {
330     dirty(QRect(x, y, 1, 1));
331 }
332
333 inline
334 void DrawingWindow::dirty(int x1, int y1, int x2, int y2)
335 {
336     QRect r;
337     r.setCoords(x1, y1, x2, y2);
338     dirty(r.normalized());
339 }
340
341 void DrawingWindow::dirty(const QRect &rect)
342 {
343     if (dirtyFlag) {
344         dirtyRect |= rect;
345     } else {
346         dirtyFlag = true;
347         dirtyRect = rect;
348     }
349 }
350
351 void DrawingWindow::mayUpdate()
352 {
353     imageMutex.lock();
354     bool dirty = dirtyFlag;
355     QRect rect = dirtyRect;
356     dirtyFlag = false;
357     imageMutex.unlock();
358     if (dirty)
359         update(rect);
360 }
361
362 //--- DrawingThread ----------------------------------------------------
363
364 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
365     : drawingWindow(w)
366     , threadFunction(f)
367     , started_once(false)
368 {
369 }
370
371 void DrawingThread::start_once(Priority priority)
372 {
373     if (!started_once) {
374         started_once = true;
375         start(priority);
376     }
377 }
378
379 void DrawingThread::run()
380 {
381     threadFunction(drawingWindow);
382 }