Make QRegion not need to be friends with QVector
[profile/ivi/qtbase.git] / src / gui / painting / qcssutil.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qcssutil_p.h"
43 #include "private/qcssparser_p.h"
44 #include "qpainter.h"
45 #include <qmath.h>
46
47 #ifndef QT_NO_CSSPARSER
48
49 QT_BEGIN_NAMESPACE
50
51 using namespace QCss;
52
53 static QPen qPenFromStyle(const QBrush& b, qreal width, BorderStyle s)
54 {
55     Qt::PenStyle ps = Qt::NoPen;
56
57     switch (s) {
58     case BorderStyle_Dotted:
59         ps  = Qt::DotLine;
60         break;
61     case BorderStyle_Dashed:
62         ps = width == 1 ? Qt::DotLine : Qt::DashLine;
63         break;
64     case BorderStyle_DotDash:
65         ps = Qt::DashDotLine;
66         break;
67     case BorderStyle_DotDotDash:
68         ps = Qt::DashDotDotLine;
69         break;
70     case BorderStyle_Inset:
71     case BorderStyle_Outset:
72     case BorderStyle_Solid:
73         ps = Qt::SolidLine;
74         break;
75     default:
76         break;
77     }
78
79     return QPen(b, width, ps, Qt::FlatCap);
80 }
81
82 void qDrawRoundedCorners(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2,
83                          const QSizeF& r1, const QSizeF& r2,
84                          Edge edge, BorderStyle s, QBrush c)
85 {
86     const qreal pw = (edge == TopEdge || edge == BottomEdge) ? y2-y1 : x2-x1;
87     if (s == BorderStyle_Double) {
88         qreal wby3 = pw/3;
89         switch (edge) {
90         case TopEdge:
91         case BottomEdge:
92             qDrawRoundedCorners(p, x1, y1, x2, y1+wby3, r1, r2, edge, BorderStyle_Solid, c);
93             qDrawRoundedCorners(p, x1, y2-wby3, x2, y2, r1, r2, edge, BorderStyle_Solid, c);
94             break;
95         case LeftEdge:
96             qDrawRoundedCorners(p, x1, y1+1, x1+wby3, y2, r1, r2, LeftEdge, BorderStyle_Solid, c);
97             qDrawRoundedCorners(p, x2-wby3, y1+1, x2, y2, r1, r2, LeftEdge, BorderStyle_Solid, c);
98             break;
99         case RightEdge:
100             qDrawRoundedCorners(p, x1, y1+1, x1+wby3, y2, r1, r2, RightEdge, BorderStyle_Solid, c);
101             qDrawRoundedCorners(p, x2-wby3, y1+1, x2, y2, r1, r2, RightEdge, BorderStyle_Solid, c);
102             break;
103         default:
104             break;
105         }
106         return;
107     } else if (s == BorderStyle_Ridge || s == BorderStyle_Groove) {
108         BorderStyle s1, s2;
109         if (s == BorderStyle_Groove) {
110             s1 = BorderStyle_Inset;
111             s2 = BorderStyle_Outset;
112         } else {
113             s1 = BorderStyle_Outset;
114             s2 = BorderStyle_Inset;
115         }
116         int pwby2 = qRound(pw/2);
117         switch (edge) {
118         case TopEdge:
119             qDrawRoundedCorners(p, x1, y1, x2, y1 + pwby2, r1, r2, TopEdge, s1, c);
120             qDrawRoundedCorners(p, x1, y1 + pwby2, x2, y2, r1, r2, TopEdge, s2, c);
121             break;
122         case BottomEdge:
123             qDrawRoundedCorners(p, x1, y1 + pwby2, x2, y2, r1, r2, BottomEdge, s1, c);
124             qDrawRoundedCorners(p, x1, y1, x2, y2-pwby2, r1, r2, BottomEdge, s2, c);
125             break;
126         case LeftEdge:
127             qDrawRoundedCorners(p, x1, y1, x1 + pwby2, y2, r1, r2, LeftEdge, s1, c);
128             qDrawRoundedCorners(p, x1 + pwby2, y1, x2, y2, r1, r2, LeftEdge, s2, c);
129             break;
130         case RightEdge:
131             qDrawRoundedCorners(p, x1 + pwby2, y1, x2, y2, r1, r2, RightEdge, s1, c);
132             qDrawRoundedCorners(p, x1, y1, x2 - pwby2, y2, r1, r2, RightEdge, s2, c);
133             break;
134         default:
135             break;
136         }
137     } else if ((s == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
138             || (s == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
139             c = c.color().lighter();
140
141     p->save();
142     qreal pwby2 = pw/2;
143     p->setBrush(Qt::NoBrush);
144     QPen pen = qPenFromStyle(c, pw, s);
145     pen.setCapStyle(Qt::SquareCap); // this eliminates the offby1 errors that we might hit below
146     p->setPen(pen);
147     switch (edge) {
148     case TopEdge:
149         if (!r1.isEmpty())
150             p->drawArc(QRectF(x1 - r1.width() + pwby2, y1 + pwby2,
151                               2*r1.width() - pw, 2*r1.height() - pw), 135*16, -45*16);
152         if (!r2.isEmpty())
153             p->drawArc(QRectF(x2 - r2.width() + pwby2, y1 + pwby2,
154                        2*r2.width() - pw, 2*r2.height() - pw), 45*16, 45*16);
155         break;
156     case BottomEdge:
157         if (!r1.isEmpty())
158             p->drawArc(QRectF(x1 - r1.width() + pwby2, y2 - 2*r1.height() + pwby2,
159                               2*r1.width() - pw, 2*r1.height() - pw), -90 * 16, -45 * 16);
160         if (!r2.isEmpty())
161             p->drawArc(QRectF(x2 - r2.width() + pwby2, y2 - 2*r2.height() + pwby2,
162                        2*r2.width() - pw, 2*r2.height() - pw), -90 * 16, 45 * 16);
163         break;
164     case LeftEdge:
165         if (!r1.isEmpty())
166             p->drawArc(QRectF(x1 + pwby2, y1 - r1.height() + pwby2,
167                        2*r1.width() - pw, 2*r1.height() - pw), 135*16, 45*16);
168         if (!r2.isEmpty())
169             p->drawArc(QRectF(x1 + pwby2, y2 - r2.height() + pwby2,
170                        2*r2.width() - pw, 2*r2.height() - pw), 180*16, 45*16);
171         break;
172     case RightEdge:
173         if (!r1.isEmpty())
174             p->drawArc(QRectF(x2 - 2*r1.width() + pwby2, y1 - r1.height() + pwby2,
175                        2*r1.width() - pw, 2*r1.height() - pw), 45*16, -45*16);
176         if (!r2.isEmpty())
177             p->drawArc(QRectF(x2 - 2*r2.width() + pwby2, y2 - r2.height() + pwby2,
178                        2*r2.width() - pw, 2*r2.height() - pw), 315*16, 45*16);
179         break;
180     default:
181         break;
182     }
183     p->restore();
184 }
185
186
187 void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, qreal dw2,
188                QCss::Edge edge, QCss::BorderStyle style, QBrush c)
189 {
190     p->save();
191     const qreal width = (edge == TopEdge || edge == BottomEdge) ? (y2-y1) : (x2-x1);
192
193     if (width <= 2 && style == BorderStyle_Double)
194         style = BorderStyle_Solid;
195
196     switch (style) {
197     case BorderStyle_Inset:
198     case BorderStyle_Outset:
199         if ((style == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
200             || (style == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
201             c = c.color().lighter();
202         // fall through!
203     case BorderStyle_Solid: {
204         p->setPen(Qt::NoPen);
205         p->setBrush(c);
206         if (width == 1 || (dw1 == 0 && dw2 == 0)) {
207             p->drawRect(QRectF(x1, y1, x2-x1, y2-y1));
208         } else { // draw trapezoid
209             QPolygonF quad;
210             switch (edge) {
211             case TopEdge:
212                 quad << QPointF(x1, y1) << QPointF(x1 + dw1, y2)
213                      << QPointF(x2 - dw2, y2) << QPointF(x2, y1);
214                 break;
215             case BottomEdge:
216                 quad << QPointF(x1 + dw1, y1) << QPointF(x1, y2)
217                      << QPointF(x2, y2) << QPointF(x2 - dw2, y1);
218                 break;
219             case LeftEdge:
220                 quad << QPointF(x1, y1) << QPointF(x1, y2)
221                      << QPointF(x2, y2 - dw2) << QPointF(x2, y1 + dw1);
222                 break;
223             case RightEdge:
224                 quad << QPointF(x1, y1 + dw1) << QPointF(x1, y2 - dw2)
225                      << QPointF(x2, y2) << QPointF(x2, y1);
226                 break;
227             default:
228                 break;
229             }
230             p->drawConvexPolygon(quad);
231         }
232         break;
233     }
234     case BorderStyle_Dotted:
235     case BorderStyle_Dashed:
236     case BorderStyle_DotDash:
237     case BorderStyle_DotDotDash:
238         p->setPen(qPenFromStyle(c, width, style));
239         if (width == 1)
240             p->drawLine(QLineF(x1, y1, x2 - 1, y2 - 1));
241         else if (edge == TopEdge || edge == BottomEdge)
242             p->drawLine(QLineF(x1 + width/2, (y1 + y2)/2, x2 - width/2, (y1 + y2)/2));
243         else
244             p->drawLine(QLineF((x1+x2)/2, y1 + width/2, (x1+x2)/2, y2 - width/2));
245         break;
246
247     case BorderStyle_Double: {
248         int wby3 = qRound(width/3);
249         int dw1by3 = qRound(dw1/3);
250         int dw2by3 = qRound(dw2/3);
251         switch (edge) {
252         case TopEdge:
253             qDrawEdge(p, x1, y1, x2, y1 + wby3, dw1by3, dw2by3, TopEdge, BorderStyle_Solid, c);
254             qDrawEdge(p, x1 + dw1 - dw1by3, y2 - wby3, x2 - dw2 + dw1by3, y2,
255                       dw1by3, dw2by3, TopEdge, BorderStyle_Solid, c);
256             break;
257         case LeftEdge:
258             qDrawEdge(p, x1, y1, x1 + wby3, y2, dw1by3, dw2by3, LeftEdge, BorderStyle_Solid, c);
259             qDrawEdge(p, x2 - wby3, y1 + dw1 - dw1by3, x2, y2 - dw2 + dw2by3, dw1by3, dw2by3,
260                       LeftEdge, BorderStyle_Solid, c);
261             break;
262         case BottomEdge:
263             qDrawEdge(p, x1 + dw1 - dw1by3, y1, x2 - dw2 + dw2by3, y1 + wby3, dw1by3, dw2by3,
264                       BottomEdge, BorderStyle_Solid, c);
265             qDrawEdge(p, x1, y2 - wby3, x2, y2, dw1by3, dw2by3, BottomEdge, BorderStyle_Solid, c);
266             break;
267         case RightEdge:
268             qDrawEdge(p, x2 - wby3, y1, x2, y2, dw1by3, dw2by3, RightEdge, BorderStyle_Solid, c);
269             qDrawEdge(p, x1, y1 + dw1 - dw1by3, x1 + wby3, y2 - dw2 + dw2by3, dw1by3, dw2by3,
270                       RightEdge, BorderStyle_Solid, c);
271             break;
272         default:
273             break;
274         }
275         break;
276     }
277     case BorderStyle_Ridge:
278     case BorderStyle_Groove: {
279         BorderStyle s1, s2;
280         if (style == BorderStyle_Groove) {
281             s1 = BorderStyle_Inset;
282             s2 = BorderStyle_Outset;
283         } else {
284             s1 = BorderStyle_Outset;
285             s2 = BorderStyle_Inset;
286         }
287         int dw1by2 = qFloor(dw1/2), dw2by2 = qFloor(dw2/2);
288         int wby2 = qRound(width/2);
289         switch (edge) {
290         case TopEdge:
291             qDrawEdge(p, x1, y1, x2, y1 + wby2, dw1by2, dw2by2, TopEdge, s1, c);
292             qDrawEdge(p, x1 + dw1by2, y1 + wby2, x2 - dw2by2, y2, dw1by2, dw2by2, TopEdge, s2, c);
293             break;
294         case BottomEdge:
295             qDrawEdge(p, x1, y1 + wby2, x2, y2, dw1by2, dw2by2, BottomEdge, s1, c);
296             qDrawEdge(p, x1 + dw1by2, y1, x2 - dw2by2, y1 + wby2, dw1by2, dw2by2, BottomEdge, s2, c);
297             break;
298         case LeftEdge:
299             qDrawEdge(p, x1, y1, x1 + wby2, y2, dw1by2, dw2by2, LeftEdge, s1, c);
300             qDrawEdge(p, x1 + wby2, y1 + dw1by2, x2, y2 - dw2by2, dw1by2, dw2by2, LeftEdge, s2, c);
301             break;
302         case RightEdge:
303             qDrawEdge(p, x1 + wby2, y1, x2, y2, dw1by2, dw2by2, RightEdge, s1, c);
304             qDrawEdge(p, x1, y1 + dw1by2, x1 + wby2, y2 - dw2by2, dw1by2, dw2by2, RightEdge, s2, c);
305             break;
306         default:
307             break;
308         }
309     }
310     default:
311         break;
312     }
313     p->restore();
314 }
315
316 void qNormalizeRadii(const QRect &br, const QSize *radii,
317                      QSize *tlr, QSize *trr, QSize *blr, QSize *brr)
318 {
319     *tlr = radii[0].expandedTo(QSize(0, 0));
320     *trr = radii[1].expandedTo(QSize(0, 0));
321     *blr = radii[2].expandedTo(QSize(0, 0));
322     *brr = radii[3].expandedTo(QSize(0, 0));
323     if (tlr->width() + trr->width() > br.width())
324         *tlr = *trr = QSize(0, 0);
325     if (blr->width() + brr->width() > br.width())
326         *blr = *brr = QSize(0, 0);
327     if (tlr->height() + blr->height() > br.height())
328         *tlr = *blr = QSize(0, 0);
329     if (trr->height() + brr->height() > br.height())
330         *trr = *brr = QSize(0, 0);
331 }
332
333 // Determines if Edge e1 draws over Edge e2. Depending on this trapezoids or rectanges are drawn
334 static bool paintsOver(const QCss::BorderStyle *styles, const QBrush *colors, QCss::Edge e1, QCss::Edge e2)
335 {
336     QCss::BorderStyle s1 = styles[e1];
337     QCss::BorderStyle s2 = styles[e2];
338
339     if (s2 == BorderStyle_None || colors[e2] == Qt::transparent)
340         return true;
341
342     if ((s1 == BorderStyle_Solid && s2 == BorderStyle_Solid) && (colors[e1] == colors[e2]))
343         return true;
344
345     return false;
346 }
347
348 void qDrawBorder(QPainter *p, const QRect &rect, const QCss::BorderStyle *styles,
349                  const int *borders, const QBrush *colors, const QSize *radii)
350 {
351     const QRectF br(rect);
352     QSize tlr, trr, blr, brr;
353     qNormalizeRadii(rect, radii, &tlr, &trr, &blr, &brr);
354
355     // Drawn in increasing order of precendence
356     if (styles[BottomEdge] != BorderStyle_None && borders[BottomEdge] > 0) {
357         qreal dw1 = (blr.width() || paintsOver(styles, colors, BottomEdge, LeftEdge)) ? 0 : borders[LeftEdge];
358         qreal dw2 = (brr.width() || paintsOver(styles, colors, BottomEdge, RightEdge)) ? 0 : borders[RightEdge];
359         qreal x1 = br.x() + blr.width();
360         qreal y1 = br.y() + br.height() - borders[BottomEdge];
361         qreal x2 = br.x() + br.width() - brr.width();
362         qreal y2 = br.y() + br.height() ;
363
364         qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, BottomEdge, styles[BottomEdge], colors[BottomEdge]);
365         if (blr.width() || brr.width())
366             qDrawRoundedCorners(p, x1, y1, x2, y2, blr, brr, BottomEdge, styles[BottomEdge], colors[BottomEdge]);
367     }
368     if (styles[RightEdge] != BorderStyle_None && borders[RightEdge] > 0) {
369         qreal dw1 = (trr.height() || paintsOver(styles, colors, RightEdge, TopEdge)) ? 0 : borders[TopEdge];
370         qreal dw2 = (brr.height() || paintsOver(styles, colors, RightEdge, BottomEdge)) ? 0 : borders[BottomEdge];
371         qreal x1 = br.x() + br.width() - borders[RightEdge];
372         qreal y1 = br.y() + trr.height();
373         qreal x2 = br.x() + br.width();
374         qreal y2 = br.y() + br.height() - brr.height();
375
376         qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, RightEdge, styles[RightEdge], colors[RightEdge]);
377         if (trr.height() || brr.height())
378             qDrawRoundedCorners(p, x1, y1, x2, y2, trr, brr, RightEdge, styles[RightEdge], colors[RightEdge]);
379     }
380     if (styles[LeftEdge] != BorderStyle_None && borders[LeftEdge] > 0) {
381         qreal dw1 = (tlr.height() || paintsOver(styles, colors, LeftEdge, TopEdge)) ? 0 : borders[TopEdge];
382         qreal dw2 = (blr.height() || paintsOver(styles, colors, LeftEdge, BottomEdge)) ? 0 : borders[BottomEdge];
383         qreal x1 = br.x();
384         qreal y1 = br.y() + tlr.height();
385         qreal x2 = br.x() + borders[LeftEdge];
386         qreal y2 = br.y() + br.height() - blr.height();
387
388         qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, LeftEdge, styles[LeftEdge], colors[LeftEdge]);
389         if (tlr.height() || blr.height())
390             qDrawRoundedCorners(p, x1, y1, x2, y2, tlr, blr, LeftEdge, styles[LeftEdge], colors[LeftEdge]);
391     }
392     if (styles[TopEdge] != BorderStyle_None && borders[TopEdge] > 0) {
393         qreal dw1 = (tlr.width() || paintsOver(styles, colors, TopEdge, LeftEdge)) ? 0 : borders[LeftEdge];
394         qreal dw2 = (trr.width() || paintsOver(styles, colors, TopEdge, RightEdge)) ? 0 : borders[RightEdge];
395         qreal x1 = br.x() + tlr.width();
396         qreal y1 = br.y();
397         qreal x2 = br.left() + br.width() - trr.width();
398         qreal y2 = br.y() + borders[TopEdge];
399
400         qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, TopEdge, styles[TopEdge], colors[TopEdge]);
401         if (tlr.width() || trr.width())
402             qDrawRoundedCorners(p, x1, y1, x2, y2, tlr, trr, TopEdge, styles[TopEdge], colors[TopEdge]);
403     }
404 }
405
406 #endif //QT_NO_CSSPARSER
407
408 QT_END_NAMESPACE