- Clear textselection on toolchange also for empty text
[konversation:konversation.git] / src / dcc / whiteboardpaintarea.cpp
1 /*
2   This program is free software; you can redistribute it and/or modify
3   it under the terms of the GNU General Public License as published by
4   the Free Software Foundation; either version 2 of the License, or
5   (at your option) any later version.
6 */
7
8 /*
9   Copyright (C) 2009-2010 Bernd Buschinski <b.buschinski@web.de>
10 */
11
12 #include "whiteboardpaintarea.h"
13
14 //is this still needed?
15 #ifdef Q_CC_MSVC
16 #    define _USE_MATH_DEFINES
17 #endif
18
19 #include <cmath>
20
21 #include <QPainter>
22 #include <QPixmap>
23 #include <QResizeEvent>
24 #include <QPen>
25 #include <QStack>
26
27 #include <KDebug>
28
29 namespace Konversation
30 {
31     namespace DCC
32     {
33         static const int InvalidLastPos = -32546;
34
35         WhiteBoardPaintArea::WhiteBoardPaintArea(QWidget* parent)
36             : QWidget(parent),
37               m_mousePressed(false),
38               m_lastPos(InvalidLastPos, InvalidLastPos),
39               m_tool(WhiteBoardGlobals::Pencil),
40               m_foregroundColor(Qt::black),
41               m_backgroundColor(Qt::white),
42               m_penWidth(1)
43         {
44             m_imagePixmap = new QPixmap(width(), height());
45             m_imagePixmap->fill(Qt::white);
46             m_overlayPixmap = new QPixmap(width(), height());
47             m_overlayPixmap->fill(Qt::transparent);
48
49             // because not all gfx driver can handle bigger images
50             setMaximumSize(WhiteBoardGlobals::MaxImageSize, WhiteBoardGlobals::MaxImageSize);
51             setMinimumSize(100,100);
52             setCursor(Qt::CrossCursor);
53         }
54
55         WhiteBoardPaintArea::~WhiteBoardPaintArea()
56         {
57             delete m_imagePixmap;
58             delete m_overlayPixmap;
59         }
60
61         void WhiteBoardPaintArea::setTool(WhiteBoardGlobals::WhiteBoardTool tool)
62         {
63             //kDebug() << "newtool" << tool;
64             if (tool != m_tool && (m_tool == WhiteBoardGlobals::Text || m_tool == WhiteBoardGlobals::TextExtended))
65             {
66                 finishText();
67                 makeLastPosInvalid();
68                 update();
69             }
70             m_tool = tool;
71         }
72
73         void WhiteBoardPaintArea::setForegroundColor(const QColor& color)
74         {
75             m_foregroundColor = color;
76             if (m_tool == WhiteBoardGlobals::Text || m_tool == WhiteBoardGlobals::TextExtended)
77             {
78                 if (isLastPosValid())
79                 {
80                     m_overlayPixmap->fill(Qt::transparent);
81                     text(m_overlayPixmap, m_font, m_foregroundColor, m_backgroundColor, m_lastPos.x(), m_lastPos.y(), m_writtenText, true, m_tool);
82                     update();
83                 }
84             }
85         }
86
87         void WhiteBoardPaintArea::setBackgroundColor(const QColor& color)
88         {
89             m_backgroundColor = color;
90         }
91
92         void WhiteBoardPaintArea::swapColors(const QColor& newForeground, const QColor& newBackground)
93         {
94             m_foregroundColor = newForeground;
95             m_backgroundColor = newBackground;
96             if (m_tool == WhiteBoardGlobals::Text || m_tool == WhiteBoardGlobals::TextExtended)
97             {
98                 if (isLastPosValid())
99                 {
100                     m_overlayPixmap->fill(Qt::transparent);
101                     text(m_overlayPixmap, m_font, m_foregroundColor, m_backgroundColor, m_lastPos.x(), m_lastPos.y(), m_writtenText, true, m_tool);
102                     update();
103                 }
104             }
105         }
106
107         void WhiteBoardPaintArea::setPenWidth(int width)
108         {
109             m_penWidth = width;
110         }
111
112         void WhiteBoardPaintArea::setFont(const QFont& font)
113         {
114             m_font = font;
115             if (isLastPosValid() && m_tool == WhiteBoardGlobals::TextExtended)
116             {
117                 m_overlayPixmap->fill(Qt::transparent);
118                 text(m_overlayPixmap, m_font, m_foregroundColor, m_backgroundColor, m_lastPos.x(), m_lastPos.y(), m_writtenText, true, m_tool);
119                 update();
120             }
121         }
122
123         void WhiteBoardPaintArea::clear()
124         {
125             m_imagePixmap->fill(Qt::white);
126             m_overlayPixmap->fill(Qt::transparent);
127             m_mousePressed = false;
128             makeLastPosInvalid();
129             update();
130         }
131
132         void WhiteBoardPaintArea::drawLine(int lineWidth, const QColor& penColor, const QColor& brushColor,
133                                            int xFrom, int yFrom, int xTo, int yTo)
134         {
135             checkImageSize(xFrom, yFrom, xTo, yTo, lineWidth);
136             QPainter tPaint(m_imagePixmap);
137             tPaint.setPen(getPen(penColor, lineWidth, WhiteBoardGlobals::Line));
138             tPaint.setBrush(brushColor);
139
140             // drawLine with lineWidth=1 creates a dot
141             // but drawLine with lineWith>1 creates nothing (with Qt 4.6.1, Bug?)
142             // check if start==endPos and use drawPoint in this case
143             if (xFrom == xTo && yFrom == yTo)
144             {
145                 tPaint.drawPoint(xFrom, yFrom);
146             }
147             else
148             {
149                 tPaint.drawLine(xFrom, yFrom, xTo, yTo);
150             }
151             tPaint.end();
152             update();
153         }
154
155         void WhiteBoardPaintArea::drawRectangle(int lineWidth, const QColor& penColor,
156                                                 int xFrom, int yFrom, int xTo, int yTo)
157         {
158             checkImageSize(xFrom, yFrom, xTo, yTo, lineWidth);
159             QPainter tPaint(m_imagePixmap);
160             tPaint.setPen(getPen(penColor, lineWidth, WhiteBoardGlobals::Rectangle));
161             tPaint.drawRect(xFrom, yFrom, xTo-xFrom, yTo-yFrom);
162             tPaint.end();
163             update();
164         }
165
166         void WhiteBoardPaintArea::drawFilledRectangle(int lineWidth, const QColor& penColor, const QColor& brushColor,
167                                                       int xFrom, int yFrom, int xTo, int yTo)
168         {
169             checkImageSize(xFrom, yFrom, xTo, yTo, lineWidth);
170             QPainter tPaint(m_imagePixmap);
171             tPaint.setPen(getPen(penColor, lineWidth, WhiteBoardGlobals::FilledRectangle));
172             tPaint.setBrush(brushColor);
173             tPaint.drawRect(xFrom, yFrom, xTo-xFrom, yTo-yFrom);
174             tPaint.end();
175             update();
176         }
177
178         void WhiteBoardPaintArea::drawEllipse(int lineWidth, const QColor& penColor,
179                                               int xFrom, int yFrom, int xTo, int yTo)
180         {
181             checkImageSize(xFrom, yFrom, xTo, yTo, lineWidth);
182             QPainter tPaint(m_imagePixmap);
183             tPaint.setPen(getPen(penColor, lineWidth, WhiteBoardGlobals::Ellipse));
184             tPaint.drawEllipse(xFrom, yFrom, xTo-xFrom, yTo-yFrom);
185             tPaint.end();
186             update();
187         }
188
189         void WhiteBoardPaintArea::drawFilledEllipse(int lineWidth, const QColor& penColor, const QColor& brushColor,
190                                                     int xFrom, int yFrom, int xTo, int yTo)
191         {
192             checkImageSize(xFrom, yFrom, xTo, yTo, lineWidth);
193             QPainter tPaint(m_imagePixmap);
194             tPaint.setPen(getPen(penColor, lineWidth, WhiteBoardGlobals::FilledEllipse));
195             tPaint.setBrush(brushColor);
196             tPaint.drawEllipse(xFrom, yFrom, xTo-xFrom, yTo-yFrom);
197             tPaint.end();
198             update();
199         }
200
201         void WhiteBoardPaintArea::drawArrow(int lineWidth, const QColor& penColor, int xFrom, int yFrom, int xTo, int yTo)
202         {
203             checkImageSize(xFrom, yFrom, xTo, yTo, lineWidth);
204             QPainter tPaint(m_imagePixmap);
205             tPaint.setPen(getPen(penColor, lineWidth, WhiteBoardGlobals::Arrow));
206             arrow(&tPaint, xFrom, yFrom, xTo, yTo);
207             tPaint.end();
208             update();
209         }
210
211         void WhiteBoardPaintArea::useEraser(int lineWidth, int xFrom, int yFrom, int xTo, int yTo)
212         {
213             checkImageSize(xFrom, yFrom, xTo, yTo, lineWidth);
214             QPainter tPaint(m_imagePixmap);
215             tPaint.setPen(getPen(Qt::white, lineWidth, WhiteBoardGlobals::Eraser));
216             tPaint.drawLine(xFrom, yFrom, xTo, yTo);
217             tPaint.end();
218             update();
219         }
220
221         void WhiteBoardPaintArea::useFloodFill (int x, int y, const QColor& color)
222         {
223             checkImageSize(x, y, 0, 0, 1);
224             floodfill(x, y, color);
225             update();
226         }
227
228         void WhiteBoardPaintArea::useBlt(int x1src, int y1src, int x2src, int y2src, int xdest, int ydest)
229         {
230             //TODO optimize me
231             checkImageSize(x2src, y2src, xdest, ydest, 1);
232             QPixmap copyPix(m_imagePixmap->copy(x1src, y1src, x2src-x1src, y2src-y1src));
233             QPainter tPaint(m_imagePixmap);
234             tPaint.drawPixmap(xdest, ydest, copyPix);
235             tPaint.end();
236             update();
237         }
238
239         void WhiteBoardPaintArea::useText(int x1, int y1, const QString& textString)
240         {
241             text(m_imagePixmap, QFont(), Qt::black, Qt::white, x1, y1, textString, false, WhiteBoardGlobals::Text);
242             update();
243         }
244
245         void WhiteBoardPaintArea::useTextExtended(int x1, int y1, const QFont& font, const QColor& foreGround, const QColor& backGround, const QString& textString)
246         {
247             text(m_imagePixmap, font, foreGround, backGround, x1, y1, textString, false, WhiteBoardGlobals::TextExtended);
248             update();
249         }
250
251         void WhiteBoardPaintArea::save(const QString& fileName)
252         {
253             m_imagePixmap->save(fileName);
254         }
255
256         void WhiteBoardPaintArea::paintEvent(QPaintEvent *event)
257         {
258             //kDebug();
259             QPainter tPaint;
260             tPaint.begin(this);
261
262             foreach (const QRect& rect, event->region().rects())
263             {
264                 tPaint.drawPixmap(rect, *m_imagePixmap, rect);
265
266         // Looks cool but doesn't make sense for ColoredLines
267         // Invert overlapping color
268         //        const int tBytePerLine = rect.width() * sizeof(QRgb);
269         //        const int tHeight = rect.height();
270         //        static const int colorMax = 255;
271         //        QImage tOriginal = m_imagePixmap->toImage();
272         //        QImage tOverlay = m_overlayPixmap->toImage();
273         //
274         //        for (int i = rect.y(); i < tHeight; ++i)
275         //        {
276         //            QRgb* tRgb = (QRgb*)tOriginal.scanLine(i);
277         //            QRgb* tOverlayLine = (QRgb*)tOverlay.scanLine(i);
278         //            for (int j = rect.x()*sizeof(QRgb) ; j < tBytePerLine; j += sizeof(QRgb))
279         //            {
280         //                int tAlpha = qAlpha(*tOverlayLine);
281         //                if (tAlpha > 0)
282         //                {
283         //                    *tOverlayLine = qRgb(colorMax^qRed(*tRgb),colorMax^qGreen(*tRgb),colorMax^ qBlue(*tRgb));
284         //                }
285         //                ++tRgb;
286         //                ++tOverlayLine;
287         //            }
288         //        }
289         //        tPaint.drawImage(rect, tOverlay, rect);
290                 tPaint.drawPixmap(rect, *m_overlayPixmap, rect);
291             }
292
293             tPaint.end();
294         }
295
296         void WhiteBoardPaintArea::resizeEvent(QResizeEvent *event)
297         {
298             Q_UNUSED(event);
299             // kDebug() << "newsize:" << event->size();
300             // kDebug() << "newcontent:" << contentsRect().width() << " " << contentsRect().height();
301             resizeImage(contentsRect().width(), contentsRect().height());
302         }
303
304         void WhiteBoardPaintArea::mousePressEvent(QMouseEvent *event)
305         {
306             // pencil is unabled to cancel as its always directly send
307             if (event->buttons() & Qt::RightButton && m_tool != WhiteBoardGlobals::Pencil)
308             {
309                 m_mousePressed = false;
310                 m_overlayPixmap->fill(Qt::transparent);
311                 m_writtenText.clear();
312                 makeLastPosInvalid();
313                 update();
314             }
315             else if (event->buttons() & Qt::LeftButton)
316             {
317                 m_mousePressed = true;
318                 if (m_tool == WhiteBoardGlobals::FloodFill)
319                 {
320                     makeLastPosInvalid();
321                     QCursor oldCur = cursor();
322                     setCursor(Qt::WaitCursor);
323                     floodfill(event->pos().x(), event->pos().y(), m_foregroundColor);
324                     setCursor(oldCur);
325                     emit usedFloodFill(event->pos().x(), event->pos().y(), m_foregroundColor);
326                     update();
327                 }
328                 else if (m_tool == WhiteBoardGlobals::TextExtended || m_tool == WhiteBoardGlobals::Text)
329                 {
330                     m_mousePressed = false;
331                     m_overlayPixmap->fill(Qt::transparent);
332                     if (isLastPosValid())
333                     {
334                         finishText();
335                         m_lastPos = event->pos();
336                         text(m_overlayPixmap, m_font, m_foregroundColor, m_backgroundColor, event->pos().x(), event->pos().y(), m_writtenText, true, m_tool);
337                         setFocus();
338                     }
339                     else
340                     {
341                         m_lastPos = event->pos();
342                         text(m_overlayPixmap, m_font, m_foregroundColor, m_backgroundColor, event->pos().x(), event->pos().y(), m_writtenText, true, m_tool);
343                         setFocus();
344                     }
345                     update();
346                 }
347                 else
348                 {
349                     makeLastPosInvalid();
350                     mouseMoveEvent(event);
351                 }
352             }
353         }
354
355         void WhiteBoardPaintArea::mouseReleaseEvent(QMouseEvent *event)
356         {
357             if (event->button() == Qt::LeftButton && m_mousePressed)
358             {
359                 m_mousePressed = false;
360                 switch (m_tool)
361                 {
362                 case WhiteBoardGlobals::Line:
363                     emit drawedLine(m_penWidth, m_foregroundColor, m_backgroundColor,
364                                     m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
365                     break;
366
367                 case WhiteBoardGlobals::Pencil:
368                 case WhiteBoardGlobals::Eraser:
369                 case WhiteBoardGlobals::FloodFill:
370                 case WhiteBoardGlobals::Stamp:
371                     break;
372
373                 case WhiteBoardGlobals::Rectangle:
374                     emit drawedRectangle(m_penWidth, m_foregroundColor,
375                                          m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
376                     break;
377                 case WhiteBoardGlobals::FilledRectangle:
378                     emit drawedFilledRectangle(m_penWidth, m_foregroundColor, m_backgroundColor,
379                                                m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
380                     break;
381                 case WhiteBoardGlobals::Ellipse:
382                     emit drawedEllipse(m_penWidth, m_foregroundColor,
383                                        m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
384                     break;
385                 case WhiteBoardGlobals::FilledEllipse:
386                     emit drawedFilledEllipse(m_penWidth, m_foregroundColor, m_backgroundColor,
387                                              m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
388                     break;
389                 case WhiteBoardGlobals::Arrow:
390                     emit drawedArrow(m_penWidth, m_foregroundColor,
391                                      m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
392                     break;
393                 case WhiteBoardGlobals::Text:
394                 case WhiteBoardGlobals::TextExtended:
395                     //don't finish writing here, finish it on tool change or when clicking somewhere else
396                     return;
397
398                 case WhiteBoardGlobals::Selection:
399                     kDebug() << "TODO implement whiteboard Selection";
400                     return;
401                 }
402
403                 QPainter tPainter(m_imagePixmap);
404                 tPainter.drawPixmap(0,0, *m_overlayPixmap);
405                 tPainter.end();
406                 m_overlayPixmap->fill(Qt::transparent);
407                 makeLastPosInvalid();
408                 update();
409             }
410         }
411
412         void WhiteBoardPaintArea::mouseMoveEvent(QMouseEvent *event)
413         {
414             if (m_mousePressed)
415             {
416                 if (isLastPosValid())
417                 {
418                     checkImageSize(event->pos().x(), event->pos().y(), m_lastPos.x(), m_lastPos.y(), m_penWidth);
419                 }
420                 else
421                 {
422                     checkImageSize(event->pos().x(), event->pos().y(), 0, 0, m_penWidth);
423                 }
424
425                 QPainter tPainter(m_overlayPixmap);
426                 tPainter.setPen(getPen(m_foregroundColor, m_penWidth, m_tool));
427
428                 switch (m_tool)
429                 {
430                 case WhiteBoardGlobals::Pencil:
431                     {
432                         if (isLastPosValid())
433                         {
434                             tPainter.drawLine(m_lastPos, event->pos());
435                             emit drawedPencil(m_penWidth, m_foregroundColor, m_backgroundColor,
436                                               m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
437                         }
438                         else
439                         {
440                             tPainter.drawPoint(event->pos());
441                             emit drawedLine(m_penWidth, m_foregroundColor, m_backgroundColor,
442                                             event->pos().x(), event->pos().y(), event->pos().x(), event->pos().y());
443                         }
444                     }
445                     m_lastPos = event->pos();
446                     break;
447
448                 case WhiteBoardGlobals::FilledEllipse:
449                     tPainter.setBrush(m_backgroundColor);
450                 case WhiteBoardGlobals::Ellipse:
451                     {
452                         if (isLastPosValid())
453                         {
454                             m_overlayPixmap->fill(Qt::transparent);
455                             const int xStart = m_lastPos.x();
456                             const int yStart = m_lastPos.y();
457                             const int xTo = event->pos().x() - xStart;
458                             const int yTo = event->pos().y() - yStart;
459                             tPainter.drawEllipse(xStart, yStart, xTo, yTo);
460                         }
461                         else
462                         {
463                             tPainter.drawPoint(event->pos());
464                             m_lastPos = event->pos();
465                         }
466                     }
467                     break;
468
469                 case WhiteBoardGlobals::FilledRectangle:
470                     tPainter.setBrush(m_backgroundColor);
471                 case WhiteBoardGlobals::Rectangle:
472                     {
473                         if (isLastPosValid())
474                         {
475                             m_overlayPixmap->fill(Qt::transparent);
476                             const int xStart = m_lastPos.x();
477                             const int yStart = m_lastPos.y();
478                             const int xTo = event->pos().x() - xStart;
479                             const int yTo = event->pos().y() - yStart;
480                             tPainter.drawRect(xStart, yStart, xTo, yTo);
481                         }
482                         else
483                         {
484                             tPainter.drawPoint(event->pos());
485                             m_lastPos = event->pos();
486                         }
487                     }
488                     break;
489
490                 case WhiteBoardGlobals::Line:
491                     {
492                         if (isLastPosValid())
493                         {
494                             m_overlayPixmap->fill(Qt::transparent);
495                             tPainter.drawLine(m_lastPos, event->pos());
496                         }
497                         else
498                         {
499                             tPainter.drawPoint(event->pos());
500                             m_lastPos = event->pos();
501                         }
502                     }
503                     break;
504
505                 case WhiteBoardGlobals::Eraser:
506                     {
507                         tPainter.setPen(getPen(Qt::white, m_penWidth, m_tool));
508                         if (isLastPosValid())
509                         {
510                             tPainter.drawLine(m_lastPos, event->pos());
511                             emit usedEraser(m_penWidth, m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
512                         }
513                         else
514                         {
515                             kDebug() << "draw eraser point";
516                             tPainter.drawPoint(event->pos());
517                             emit usedEraser(m_penWidth, event->pos().x(), event->pos().y(), event->pos().x(), event->pos().y());
518                         }
519                     }
520                     m_lastPos = event->pos();
521                     break;
522
523                 case WhiteBoardGlobals::FloodFill:
524                     //handle in mousePressEvent, otherwise the would always trigger a floodfill on move
525                     break;
526
527                 case WhiteBoardGlobals::Arrow:
528                     {
529                         if (isLastPosValid())
530                         {
531                             m_overlayPixmap->fill(Qt::transparent);
532                             arrow(&tPainter, m_lastPos.x(), m_lastPos.y(), event->pos().x(), event->pos().y());
533                         }
534                         else
535                         {
536                             m_lastPos = event->pos();
537                         }
538                     }
539                     break;
540
541                 case WhiteBoardGlobals::TextExtended:
542                 case WhiteBoardGlobals::Text:
543                     //handle in mousePressEvent
544                     break;
545
546                 case WhiteBoardGlobals::Stamp:
547                 case WhiteBoardGlobals::Selection:
548                     kDebug() << "TODO implement whiteboard Selection/Stamp";
549                     break;
550                 }
551                 tPainter.end();
552
553                 update();
554             }
555         }
556
557         void WhiteBoardPaintArea::keyPressEvent(QKeyEvent* event)
558         {
559             // kDebug() << event->text() << event->text().length() << int(event->text()[0].toAscii());
560
561             if ((m_tool == WhiteBoardGlobals::Text || m_tool == WhiteBoardGlobals::TextExtended) && isLastPosValid())
562             {
563                 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
564                 {
565                     finishText();
566                     update();
567                     return;
568                 }
569
570                 if (event->key() != Qt::Key_Backspace)
571                 {
572                     QString eventText = event->text();
573                     //chars before 32(space) are not printable
574                     if (eventText.length() == 1 && !eventText.at(0).isPrint())
575                     {
576                         return;
577                     }
578                     m_writtenText += eventText;
579                 }
580                 else
581                 {
582                     m_writtenText.chop(1);
583                 }
584                 m_overlayPixmap->fill(Qt::transparent);
585                 text(m_overlayPixmap, m_font, m_foregroundColor, m_backgroundColor, m_lastPos.x(), m_lastPos.y(), m_writtenText, true, m_tool);
586                 update();
587             }
588             QWidget::keyPressEvent(event);
589         }
590
591         void WhiteBoardPaintArea::makeLastPosInvalid()
592         {
593             m_lastPos.setX(InvalidLastPos);
594             m_lastPos.setY(InvalidLastPos);
595         }
596
597         bool WhiteBoardPaintArea::isLastPosValid()
598         {
599             if (m_lastPos.x() > InvalidLastPos && m_lastPos.y() > InvalidLastPos)
600             {
601                 return true;
602             }
603             return false;
604         }
605
606         void WhiteBoardPaintArea::checkImageSize(int x1, int y1, int x2, int y2, int penWidth)
607         {
608             // kDebug() << x1 << y1 << x2 << y2;
609             if (width() == WhiteBoardGlobals::MaxImageSize && height() == WhiteBoardGlobals::MaxImageSize)
610             {
611                 return;
612             }
613
614             const int maxX = qMax(qMin(qMax(x1+penWidth,x2+penWidth), WhiteBoardGlobals::MaxImageSize), width());
615             const int maxY = qMax(qMin(qMax(y1+penWidth,y2+penWidth), WhiteBoardGlobals::MaxImageSize), height());
616
617             if (maxX > width() || maxY > height())
618             {
619                 // if the image is not visible we receive no resizeEvent
620                 resizeImage(maxX, maxY);
621                 resize(maxX, maxY);
622             }
623         }
624
625         void WhiteBoardPaintArea::resizeImage(int width, int height)
626         {
627             if (m_imagePixmap->height() < height || m_imagePixmap->width() < width)
628             {
629                 QPixmap* oldImg = m_imagePixmap;
630                 m_imagePixmap = new QPixmap(width, height);
631                 m_imagePixmap->fill(Qt::white);
632                 QPainter tPaint(m_imagePixmap);
633                 tPaint.drawPixmap(0, 0, *oldImg);
634                 tPaint.end();
635                 delete oldImg;
636
637                 QPixmap* oldOverlay = m_overlayPixmap;
638                 m_overlayPixmap = new QPixmap(width, height);
639                 m_overlayPixmap->fill(Qt::transparent);
640                 QPainter tPaintOverlay(m_overlayPixmap);
641                 tPaintOverlay.drawPixmap(0, 0, *oldOverlay);
642                 tPaintOverlay.end();
643                 delete oldOverlay;
644
645                 const int maxWidth = qMin(qMax(width, minimumSize().width()), WhiteBoardGlobals::MaxImageSize);
646                 const int maxHeight = qMin(qMax(height, minimumSize().height()), WhiteBoardGlobals::MaxImageSize);
647                 setMinimumSize(maxWidth, maxHeight);
648             }
649         }
650
651         QPen WhiteBoardPaintArea::getPen(const QColor& color, int lineWidth, WhiteBoardGlobals::WhiteBoardTool tool)
652         {
653             switch (tool)
654             {
655                 case WhiteBoardGlobals::Pencil:
656                 case WhiteBoardGlobals::Line:
657                 case WhiteBoardGlobals::Eraser:
658                 case WhiteBoardGlobals::Arrow:
659                     return QPen(color, lineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
660
661                 default:
662                     return QPen(color, lineWidth, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
663             }
664         }
665
666         struct StackCoords
667         {
668             int x;
669             int y;
670         };
671
672         void WhiteBoardPaintArea::floodfill(int x, int y, const QColor& fillColor)
673         {
674             // A recursiv "fast" floodfill *can* cause a stackoverflow
675             // so we use a "slow" stack + extra data.
676             // TODO optimize me, mass QImage::pixel use is slow
677             QImage image = m_imagePixmap->toImage();
678             const int width = image.width();
679             const int height = image.height();
680             if (x < 0 || y < 0 || x >= width || y >= height)
681             {
682                 return;
683             }
684             QRgb originalColor = image.pixel(x,y);
685             QRgb color = fillColor.rgb();
686
687             if (color == originalColor)
688             {
689                 //already filled
690                 return;
691             }
692
693             QStack<StackCoords> tStack;
694             StackCoords t;
695             t.x = x;
696             t.y = y;
697
698             tStack.push(t);
699             while (!tStack.isEmpty())
700             {
701                 StackCoords co = tStack.pop();
702                 image.setPixel(co.x, co.y, color);
703
704                 StackCoords tLeft;
705                 tLeft.x = co.x-1;
706                 tLeft.y = co.y;
707                 if (tLeft.x >= 0 && image.pixel(tLeft.x, tLeft.y) == originalColor)
708                 {
709                     tStack.push(tLeft);
710                 }
711
712                 StackCoords tRight;
713                 tRight.x = co.x+1;
714                 tRight.y = co.y;
715                 if (tRight.x < width && image.pixel(tRight.x, tRight.y) == originalColor)
716                 {
717                     tStack.push(tRight);
718                 }
719
720                 StackCoords tUp;
721                 tUp.x = co.x;
722                 tUp.y = co.y-1;
723                 if (tUp.y >= 0 && image.pixel(tUp.x, tUp.y) == originalColor)
724                 {
725                     tStack.push(tUp);
726                 }
727
728                 StackCoords tDown;
729                 tDown.x = co.x;
730                 tDown.y = co.y+1;
731                 if (tDown.y < height && image.pixel(tDown.x, tDown.y) == originalColor)
732                 {
733                     tStack.push(tDown);
734                 }
735             }
736             delete m_imagePixmap;
737             m_imagePixmap = new QPixmap(QPixmap::fromImage(image));
738         }
739
740         void WhiteBoardPaintArea::arrow(QPainter* painter, int x1, int y1, int x2, int y2)
741         {
742             //eh? this got somehow out of control
743             //TODO simplify me
744             const int yDiff = y1 - y2;
745             const int xDiff = x1 - x2;
746             qreal angle;
747             if (xDiff == 0 && yDiff == 0)
748             {
749                 return;
750             }
751             else if (xDiff == 0 && yDiff > 0)
752             {
753                 angle = M_PI;
754             }
755             else if (xDiff > 0 && yDiff == 0)
756             {
757                 angle = 2.0*M_PI/4.0*3.0;
758             }
759             else if (xDiff == 0 && yDiff < 0)
760             {
761                 angle = 0;
762             }
763             else if (xDiff < 0 && yDiff == 0)
764             {
765                 angle = M_PI/2.0;
766             }
767             else
768             {
769                 angle = atan(qreal(yDiff)/qreal(xDiff));
770
771                 if (xDiff > 0.0 && yDiff > 0.0)
772                 {
773                     angle = (M_PI/2.0) - angle + M_PI;
774                 }
775                 else if (xDiff > 0.0 && yDiff < 0.0)
776                 {
777                     angle = (2.0*M_PI/4.0*3.0) - angle;
778                 }
779                 else if (xDiff < 0.0)
780                 {
781                     angle = (M_PI/2.0) - angle;
782                 }
783             }
784
785             static const int arrowLength = 9;
786
787             angle -= M_PI;
788             static const qreal radDiff = qreal(2)* M_PI / qreal(360) * 22;
789             qreal tRightAngle = angle + radDiff;
790             const int x1Arrow = sin(tRightAngle) * arrowLength + x2;
791             const int y1Arrow = cos(tRightAngle) * arrowLength + y2;
792
793             qreal tLeftAngle = angle - radDiff;
794             const int x2Arrow = sin(tLeftAngle) * arrowLength + x2;
795             const int y2Arrow = cos(tLeftAngle) * arrowLength + y2;
796
797             painter->drawLine(x1, y1, x2, y2);
798             painter->drawLine(x1Arrow, y1Arrow, x2, y2);
799             painter->drawLine(x2Arrow, y2Arrow, x2, y2);
800         }
801
802         void WhiteBoardPaintArea::text(QPaintDevice* device, const QFont& font, const QColor& foreGround,
803                                        const QColor& backGround, int x1, int y1, const QString& textString,
804                                        bool drawSelection, Konversation::DCC::WhiteBoardGlobals::WhiteBoardTool tool)
805         {
806             QFontMetrics tMetrics(font);
807             QSize tSize = tMetrics.size(Qt::TextSingleLine, textString);
808
809             //HACK if size changes device is invalid
810             bool isOverlay = (device == m_overlayPixmap);
811             checkImageSize(x1,y1,x1+tSize.width(), y1+tSize.height());
812
813             if (isOverlay)
814             {
815                 device = m_overlayPixmap;
816             }
817             else
818             {
819                 device = m_imagePixmap;
820             }
821
822             QPainter tPaint(device);
823             tPaint.setFont(font);
824             if (tool == WhiteBoardGlobals::TextExtended)
825             {
826                 tPaint.setBrush(backGround);
827
828                 //paint background, but I don't like it
829 //                 tPaint.setPen(getPen(backGround, 1, WhiteBoardGlobals::FilledRectangle));
830 //                 tPaint.drawRect(x1, y1, tSize.width(), tSize.height());
831
832                 tPaint.setPen(getPen(foreGround, 1, tool));
833             }
834
835             tPaint.drawText(x1, y1+tSize.height(), textString);
836             if (drawSelection)
837             {
838                 //draw white and blue dash to make it also visible on blue background
839                 tPaint.setBrush(Qt::transparent);
840                 QPen tPen = getPen(Qt::blue, 1, tool);
841                 tPen.setStyle(Qt::CustomDashLine);
842
843                 static const qreal dashLength = 4;
844                 QVector<qreal> dashes;
845                 dashes << dashLength << dashLength;
846                 tPen.setDashPattern(dashes);
847
848                 tPaint.setPen(tPen);
849                 tPaint.drawRect(x1-1,y1-1, tSize.width()+1, tSize.height()+1);
850
851                 tPen.setDashOffset(dashLength);
852                 tPen.setColor(Qt::white);
853
854                 tPaint.setPen(tPen);
855                 tPaint.drawRect(x1-1,y1-1, tSize.width()+1, tSize.height()+1);
856             }
857             tPaint.end();
858         }
859
860         void WhiteBoardPaintArea::finishText()
861         {
862             m_overlayPixmap->fill(Qt::transparent);
863             if (m_writtenText.isEmpty())
864             {
865                 return;
866             }
867
868             text(m_imagePixmap, m_font, m_foregroundColor, m_backgroundColor, m_lastPos.x(), m_lastPos.y(), m_writtenText, false, m_tool);
869             if (m_tool == WhiteBoardGlobals::TextExtended)
870             {
871                 emit usedTextExtended(m_lastPos.x(), m_lastPos.y(), m_font, m_foregroundColor, m_backgroundColor, m_writtenText);
872             }
873             else if (m_tool == WhiteBoardGlobals::Text)
874             {
875                 emit usedText(m_lastPos.x(), m_lastPos.y(), m_writtenText);
876             }
877             m_writtenText.clear();
878         }
879     }
880 }