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