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