Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
b60eb06ccc4513b35e03dcf05ab44777e8c13d07
[graphlib.git] / DrawingWindow.cpp
1 #include "DrawingWindow.h"
2 #include <QApplication>
3 #include <QBasicTimer>
4 #include <QColor>
5 #include <QImage>
6 #include <QMutex>
7 #include <QPaintEvent>
8 #include <QPainter>
9 #include <QRect>
10 #include <QThread>
11 #include <QTimerEvent>
12 #include <QWaitCondition>
13
14 class DrawingThread: public QThread {
15 public:
16     DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f);
17     void start_once(Priority priority = InheritPriority);
18
19 protected:
20     void run();
21
22 private:
23     DrawingWindow &drawingWindow;
24     DrawingWindow::ThreadFunction threadFunction;
25     bool started_once;
26
27     friend class DrawingWindow;
28     friend class DrawingWindowPrivate;
29 };
30
31 class DrawingWindowPrivate {
32 public:
33     static const int paintInterval = 33;
34
35     DrawingWindow * const q;
36
37     QBasicTimer timer;
38     QMutex imageMutex;
39     QMutex paintMutex;
40     QWaitCondition paintCondition;
41     bool painted;
42     int lockCount;
43
44     QImage *image;
45     QPainter *painter;
46
47     QColor fgColor;
48     QColor bgColor;
49
50     bool dirtyFlag;
51     QRect dirtyRect;
52
53     DrawingThread *thread;
54
55     DrawingWindowPrivate(DrawingWindow *w,
56                          DrawingWindow::ThreadFunction f);
57     ~DrawingWindowPrivate();
58
59     void initialize();
60
61     void safeLock(QMutex &mutex);
62     void safeUnlock(QMutex &mutex);
63
64     void dirty();
65     void dirty(int x, int y);
66     void dirty(int x1, int y1, int x2, int y2);
67     void dirty(const QRect &rect);
68
69 };
70
71 //--- DrawingWindow ----------------------------------------------------
72
73 DrawingWindow::DrawingWindow(ThreadFunction f, int w, int h)
74     : QWidget()
75     , width(w)
76     , height(h)
77     , d(new DrawingWindowPrivate(this, f))
78 {
79     d->initialize();
80 }
81
82 DrawingWindow::DrawingWindow(QWidget *parent,
83                              ThreadFunction f, int w, int h)
84     : QWidget(parent)
85     , width(w)
86     , height(h)
87     , d(new DrawingWindowPrivate(this, f))
88 {
89     d->initialize();
90 }
91
92 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
93                              ThreadFunction f, int w, int h)
94     : QWidget(parent, flags)
95     , width(w)
96     , height(h)
97     , d(new DrawingWindowPrivate(this, f))
98 {
99     d->initialize();
100 }
101
102 DrawingWindow::~DrawingWindow()
103 {
104     delete d;
105 }
106
107 void DrawingWindow::setColor(float red, float green, float blue)
108 {
109     d->fgColor.setRgbF(red, green, blue);
110     QPen pen(d->painter->pen());
111     pen.setColor(d->fgColor);
112     d->painter->setPen(pen);
113 }
114
115 void DrawingWindow::setBgColor(float red, float green, float blue)
116 {
117     d->bgColor.setRgbF(red, green, blue);
118 }
119
120 void DrawingWindow::clearGraph()
121 {
122     d->safeLock(d->imageMutex);
123     d->painter->fillRect(d->image->rect(), d->bgColor);    
124     d->dirty();
125     d->safeUnlock(d->imageMutex);
126 }
127
128 void DrawingWindow::drawPoint(int x, int y)
129 {
130     d->safeLock(d->imageMutex);
131     d->painter->drawPoint(x, y);
132     d->dirty(x, y);
133     d->safeUnlock(d->imageMutex);
134 }
135
136 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
137 {
138     d->safeLock(d->imageMutex);
139     d->painter->drawLine(x1, y1, x2, y2);
140     d->dirty(x1, y1, x2, y2);
141     d->safeUnlock(d->imageMutex);
142 }
143
144 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
145 {
146     QRect r;
147     r.setCoords(x1, y1, x2, y2);
148     r = r.normalized();
149     d->safeLock(d->imageMutex);
150     d->painter->drawRect(r);
151     r.adjust(0, 0, 1, 1);
152     d->dirty(r);
153     d->safeUnlock(d->imageMutex);
154 }
155
156 bool DrawingWindow::sync(unsigned long time)
157 {
158     bool ret;
159     d->safeLock(d->paintMutex);
160     QApplication::postEvent(this, new QEvent(QEvent::User));
161     ret = d->paintCondition.wait(&d->paintMutex, time);
162     d->safeUnlock(d->paintMutex);
163     return ret;
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     d->timer.stop();
184     d->thread->terminate();
185     d->paintCondition.wakeAll();
186     QWidget::closeEvent(ev);
187     d->thread->wait();
188 }
189
190 void DrawingWindow::customEvent(QEvent *)
191 {
192     d->imageMutex.lock();
193     if (d->dirtyFlag) {
194         QRect r = d->dirtyRect;
195         d->dirtyFlag = false;
196         d->imageMutex.unlock();
197         repaint(r);
198     } else
199         d->imageMutex.unlock();
200     d->paintMutex.lock();
201     d->paintCondition.wakeAll();
202     d->paintMutex.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     d->imageMutex.lock();
224     QImage imageCopy(*d->image);
225     d->imageMutex.unlock();
226     QRect rect = ev->rect();
227     widgetPainter.drawImage(rect, imageCopy, rect);
228 }
229
230 void DrawingWindow::showEvent(QShowEvent *ev)
231 {
232     d->timer.start(d->paintInterval, this);
233     d->thread->start_once(QThread::IdlePriority);
234     QWidget::showEvent(ev);
235 }
236
237 void DrawingWindow::timerEvent(QTimerEvent *ev)
238 {
239     if (ev->timerId() == d->timer.timerId()) {
240         d->imageMutex.lock();
241         if (d->dirtyFlag) {
242             update(d->dirtyRect);
243             d->dirtyFlag = false;
244         }
245         d->imageMutex.unlock();
246         d->timer.start(d->paintInterval, this);
247     } else {
248         QWidget::timerEvent(ev);
249     }
250 }
251
252 //--- DrawingWindowPrivate ---------------------------------------------
253
254 DrawingWindowPrivate::DrawingWindowPrivate(DrawingWindow *w,
255                                            DrawingWindow::ThreadFunction f)
256     : q(w)
257     , lockCount(0)
258     , image(new QImage(q->width, q->height, QImage::Format_RGB32))
259     , painter(new QPainter(image))
260     , thread(new DrawingThread(*q, f))
261 {
262 }
263
264 void DrawingWindowPrivate::initialize()
265 {
266     q->setFocusPolicy(Qt::StrongFocus);
267     q->setFixedSize(image->size());
268     q->setAttribute(Qt::WA_OpaquePaintEvent);
269     q->setFocus();
270
271     q->setColor(0.0, 0.0, 0.0); // black
272     q->setBgColor(1.0, 1.0, 1.0); // white
273     q->clearGraph();
274
275     dirtyFlag = false;
276 }
277
278 DrawingWindowPrivate::~DrawingWindowPrivate()
279 {
280     delete thread;
281     delete painter;
282     delete image;
283 }
284
285 inline
286 void DrawingWindowPrivate::safeLock(QMutex &mutex)
287 {
288     if (lockCount++ == 0)
289         thread->setTerminationEnabled(false);
290     mutex.lock();
291 }
292
293 inline
294 void DrawingWindowPrivate::safeUnlock(QMutex &mutex)
295 {
296     mutex.unlock();
297     if (--lockCount == 0)
298         thread->setTerminationEnabled(true);
299 }
300
301 inline
302 void DrawingWindowPrivate::dirty()
303 {
304     dirtyFlag = true;
305     dirtyRect = image->rect();
306 }
307
308 inline
309 void DrawingWindowPrivate::dirty(int x, int y)
310 {
311     dirty(QRect(x, y, 1, 1));
312 }
313
314 inline
315 void DrawingWindowPrivate::dirty(int x1, int y1, int x2, int y2)
316 {
317     QRect r;
318     r.setCoords(x1, y1, x2, y2);
319     dirty(r.normalized());
320 }
321
322 void DrawingWindowPrivate::dirty(const QRect &rect)
323 {
324     if (dirtyFlag) {
325         dirtyRect |= rect;
326     } else {
327         dirtyFlag = true;
328         dirtyRect = rect;
329     }
330 }
331
332 //--- DrawingThread ----------------------------------------------------
333
334 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
335     : drawingWindow(w)
336     , threadFunction(f)
337     , started_once(false)
338 {
339 }
340
341 void DrawingThread::start_once(Priority priority)
342 {
343     if (!started_once) {
344         started_once = true;
345         start(priority);
346     }
347 }
348
349 void DrawingThread::run()
350 {
351     threadFunction(drawingWindow);
352 }