source: ntrip/trunk/BNC/qwt/qwt_painter.cpp@ 9383

Last change on this file since 9383 was 9383, checked in by stoecker, 3 years ago

update to qwt verion 6.1.1 to fix build with newer Qt5

File size: 35.8 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_painter.h"
11#include "qwt_math.h"
12#include "qwt_clipper.h"
13#include "qwt_color_map.h"
14#include "qwt_scale_map.h"
15#include <qwindowdefs.h>
16#include <qwidget.h>
17#include <qframe.h>
18#include <qrect.h>
19#include <qpainter.h>
20#include <qpalette.h>
21#include <qpaintdevice.h>
22#include <qpainterpath.h>
23#include <qpixmap.h>
24#include <qstyle.h>
25#include <qtextdocument.h>
26#include <qabstracttextdocumentlayout.h>
27#include <qstyleoption.h>
28#include <qpaintengine.h>
29#include <qapplication.h>
30#include <qdesktopwidget.h>
31
32#if QT_VERSION >= 0x050000
33#include <qwindow.h>
34#endif
35
36#if QT_VERSION < 0x050000
37
38#ifdef Q_WS_X11
39#include <qx11info_x11.h>
40#endif
41
42#endif
43
44bool QwtPainter::d_polylineSplitting = true;
45bool QwtPainter::d_roundingAlignment = true;
46
47static inline bool qwtIsClippingNeeded(
48 const QPainter *painter, QRectF &clipRect )
49{
50 bool doClipping = false;
51 const QPaintEngine *pe = painter->paintEngine();
52 if ( pe && pe->type() == QPaintEngine::SVG )
53 {
54 // The SVG paint engine ignores any clipping,
55
56 if ( painter->hasClipping() )
57 {
58 doClipping = true;
59 clipRect = painter->clipRegion().boundingRect();
60 }
61 }
62
63 return doClipping;
64}
65
66template <class T>
67static inline void qwtDrawPolyline( QPainter *painter,
68 const T *points, int pointCount, bool polylineSplitting )
69{
70 bool doSplit = false;
71 if ( polylineSplitting )
72 {
73 const QPaintEngine *pe = painter->paintEngine();
74 if ( pe && pe->type() == QPaintEngine::Raster )
75 {
76 /*
77 The raster paint engine seems to use some algo with O(n*n).
78 ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
79 To work around this problem, we have to split the polygon into
80 smaller pieces.
81 */
82 doSplit = true;
83 }
84 }
85
86 if ( doSplit )
87 {
88 const int splitSize = 6;
89
90 for ( int i = 0; i < pointCount; i += splitSize )
91 {
92 const int n = qMin( splitSize + 1, pointCount - i );
93 painter->drawPolyline( points + i, n );
94 }
95 }
96 else
97 {
98 painter->drawPolyline( points, pointCount );
99 }
100}
101
102static inline QSize qwtScreenResolution()
103{
104 static QSize screenResolution;
105 if ( !screenResolution.isValid() )
106 {
107 QDesktopWidget *desktop = QApplication::desktop();
108 if ( desktop )
109 {
110 screenResolution.setWidth( desktop->logicalDpiX() );
111 screenResolution.setHeight( desktop->logicalDpiY() );
112 }
113 }
114
115 return screenResolution;
116}
117
118static inline void qwtUnscaleFont( QPainter *painter )
119{
120 if ( painter->font().pixelSize() >= 0 )
121 return;
122
123 const QSize screenResolution = qwtScreenResolution();
124
125 const QPaintDevice *pd = painter->device();
126 if ( pd->logicalDpiX() != screenResolution.width() ||
127 pd->logicalDpiY() != screenResolution.height() )
128 {
129 QFont pixelFont( painter->font(), QApplication::desktop() );
130 pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
131
132 painter->setFont( pixelFont );
133 }
134}
135
136/*!
137 Check is the application is running with the X11 graphics system
138 that has some special capabilities that can be used for incremental
139 painting to a widget.
140
141 \return True, when the graphics system is X11
142*/
143bool QwtPainter::isX11GraphicsSystem()
144{
145 /*
146 The X11 paint engine has been removed with Qt 5.0, but
147 reintroduced with Qt 5.10. It can be enabled with
148 "export QT_XCB_NATIVE_PAINTING=1".
149 */
150
151 static int onX11 = -1;
152 if ( onX11 < 0 )
153 {
154 QPixmap pm( 1, 1 );
155 QPainter painter( &pm );
156
157 onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0;
158 }
159
160 return onX11 == 1;
161}
162
163/*!
164 Check if the painter is using a paint engine, that aligns
165 coordinates to integers. Today these are all paint engines
166 beside QPaintEngine::Pdf and QPaintEngine::SVG.
167
168 If we have an integer based paint engine it is also
169 checked if the painter has a transformation matrix,
170 that rotates or scales.
171
172 \param painter Painter
173 \return true, when the painter is aligning
174
175 \sa setRoundingAlignment()
176*/
177bool QwtPainter::isAligning( QPainter *painter )
178{
179 if ( painter && painter->isActive() )
180 {
181 switch ( painter->paintEngine()->type() )
182 {
183 case QPaintEngine::Pdf:
184 case QPaintEngine::SVG:
185 return false;
186
187 default:;
188 }
189
190 const QTransform &tr = painter->transform();
191 if ( tr.isRotating() || tr.isScaling() )
192 {
193 // we might have to check translations too
194 return false;
195 }
196 }
197
198 return true;
199}
200
201/*!
202 Enable whether coordinates should be rounded, before they are painted
203 to a paint engine that floors to integer values. For other paint engines
204 ( PDF, SVG ) this flag has no effect.
205 QwtPainter stores this flag only, the rounding itself is done in
206 the painting code ( f.e the plot items ).
207
208 The default setting is true.
209
210 \sa roundingAlignment(), isAligning()
211*/
212void QwtPainter::setRoundingAlignment( bool enable )
213{
214 d_roundingAlignment = enable;
215}
216
217/*!
218 \brief En/Disable line splitting for the raster paint engine
219
220 In some Qt versions the raster paint engine paints polylines of many points
221 much faster when they are split in smaller chunks: f.e all supported Qt versions
222 >= Qt 5.0 when drawing an antialiased polyline with a pen width >=2.
223
224 Also the raster paint engine has a nasty bug in many versions ( Qt 4.8 - ... )
225 for short lines ( https://codereview.qt-project.org/#/c/99456 ), that is worked
226 around in this mode.
227
228 The default setting is true.
229
230 \sa polylineSplitting()
231*/
232void QwtPainter::setPolylineSplitting( bool enable )
233{
234 d_polylineSplitting = enable;
235}
236
237//! Wrapper for QPainter::drawPath()
238void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path )
239{
240 painter->drawPath( path );
241}
242
243//! Wrapper for QPainter::drawRect()
244void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
245{
246 drawRect( painter, QRectF( x, y, w, h ) );
247}
248
249//! Wrapper for QPainter::drawRect()
250void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
251{
252 const QRectF r = rect;
253
254 QRectF clipRect;
255 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
256
257 if ( deviceClipping )
258 {
259 if ( !clipRect.intersects( r ) )
260 return;
261
262 if ( !clipRect.contains( r ) )
263 {
264 fillRect( painter, r & clipRect, painter->brush() );
265
266 painter->save();
267 painter->setBrush( Qt::NoBrush );
268 drawPolyline( painter, QPolygonF( r ) );
269 painter->restore();
270
271 return;
272 }
273 }
274
275 painter->drawRect( r );
276}
277
278//! Wrapper for QPainter::fillRect()
279void QwtPainter::fillRect( QPainter *painter,
280 const QRectF &rect, const QBrush &brush )
281{
282 if ( !rect.isValid() )
283 return;
284
285 QRectF clipRect;
286 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
287
288 /*
289 Performance of Qt4 is horrible for a non trivial brush. Without
290 clipping expect minutes or hours for repainting large rectangles
291 (might result from zooming)
292 */
293
294 if ( deviceClipping )
295 clipRect &= painter->window();
296 else
297 clipRect = painter->window();
298
299 if ( painter->hasClipping() )
300 clipRect &= painter->clipRegion().boundingRect();
301
302 QRectF r = rect;
303 if ( deviceClipping )
304 r = r.intersected( clipRect );
305
306 if ( r.isValid() )
307 painter->fillRect( r, brush );
308}
309
310//! Wrapper for QPainter::drawPie()
311void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
312 int a, int alen )
313{
314 QRectF clipRect;
315 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
316 if ( deviceClipping && !clipRect.contains( rect ) )
317 return;
318
319 painter->drawPie( rect, a, alen );
320}
321
322//! Wrapper for QPainter::drawEllipse()
323void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
324{
325 QRectF clipRect;
326 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
327
328 if ( deviceClipping && !clipRect.contains( rect ) )
329 return;
330
331 painter->drawEllipse( rect );
332}
333
334//! Wrapper for QPainter::drawText()
335void QwtPainter::drawText( QPainter *painter, double x, double y,
336 const QString &text )
337{
338 drawText( painter, QPointF( x, y ), text );
339}
340
341//! Wrapper for QPainter::drawText()
342void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
343 const QString &text )
344{
345 QRectF clipRect;
346 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
347
348 if ( deviceClipping && !clipRect.contains( pos ) )
349 return;
350
351
352 painter->save();
353 qwtUnscaleFont( painter );
354 painter->drawText( pos, text );
355 painter->restore();
356}
357
358//! Wrapper for QPainter::drawText()
359void QwtPainter::drawText( QPainter *painter,
360 double x, double y, double w, double h,
361 int flags, const QString &text )
362{
363 drawText( painter, QRectF( x, y, w, h ), flags, text );
364}
365
366//! Wrapper for QPainter::drawText()
367void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
368 int flags, const QString &text )
369{
370 painter->save();
371 qwtUnscaleFont( painter );
372 painter->drawText( rect, flags, text );
373 painter->restore();
374}
375
376#ifndef QT_NO_RICHTEXT
377
378/*!
379 Draw a text document into a rectangle
380
381 \param painter Painter
382 \param rect Traget rectangle
383 \param flags Alignments/Text flags, see QPainter::drawText()
384 \param text Text document
385*/
386void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
387 int flags, const QTextDocument &text )
388{
389 QTextDocument *txt = text.clone();
390
391 painter->save();
392
393 QRectF unscaledRect = rect;
394
395 if ( painter->font().pixelSize() < 0 )
396 {
397 const QSize res = qwtScreenResolution();
398
399 const QPaintDevice *pd = painter->device();
400 if ( pd->logicalDpiX() != res.width() ||
401 pd->logicalDpiY() != res.height() )
402 {
403 QTransform transform;
404 transform.scale( res.width() / double( pd->logicalDpiX() ),
405 res.height() / double( pd->logicalDpiY() ));
406
407 painter->setWorldTransform( transform, true );
408 unscaledRect = transform.inverted().mapRect(rect);
409 }
410 }
411
412 txt->setDefaultFont( painter->font() );
413 txt->setPageSize( QSizeF( unscaledRect.width(), QWIDGETSIZE_MAX ) );
414
415 QAbstractTextDocumentLayout* layout = txt->documentLayout();
416
417 const double height = layout->documentSize().height();
418 double y = unscaledRect.y();
419 if ( flags & Qt::AlignBottom )
420 y += ( unscaledRect.height() - height );
421 else if ( flags & Qt::AlignVCenter )
422 y += ( unscaledRect.height() - height ) / 2;
423
424 QAbstractTextDocumentLayout::PaintContext context;
425 context.palette.setColor( QPalette::Text, painter->pen().color() );
426
427 painter->translate( unscaledRect.x(), y );
428 layout->draw( painter, context );
429
430 painter->restore();
431 delete txt;
432}
433
434#endif // !QT_NO_RICHTEXT
435
436
437//! Wrapper for QPainter::drawLine()
438void QwtPainter::drawLine( QPainter *painter,
439 const QPointF &p1, const QPointF &p2 )
440{
441 QRectF clipRect;
442 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
443
444 if ( deviceClipping &&
445 !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
446 {
447 QPolygonF polygon;
448 polygon += p1;
449 polygon += p2;
450 drawPolyline( painter, polygon );
451 return;
452 }
453
454 painter->drawLine( p1, p2 );
455}
456
457//! Wrapper for QPainter::drawPolygon()
458void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
459{
460 QRectF clipRect;
461 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
462
463 QPolygonF cpa = polygon;
464 if ( deviceClipping )
465 cpa = QwtClipper::clipPolygonF( clipRect, polygon, true );
466
467 painter->drawPolygon( cpa );
468}
469
470//! Wrapper for QPainter::drawPolyline()
471void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
472{
473 QRectF clipRect;
474 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
475
476 QPolygonF cpa = polygon;
477 if ( deviceClipping )
478 cpa = QwtClipper::clipPolygonF( clipRect, cpa );
479
480 qwtDrawPolyline<QPointF>( painter,
481 cpa.constData(), cpa.size(), d_polylineSplitting );
482}
483
484//! Wrapper for QPainter::drawPolyline()
485void QwtPainter::drawPolyline( QPainter *painter,
486 const QPointF *points, int pointCount )
487{
488 QRectF clipRect;
489 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
490
491 if ( deviceClipping )
492 {
493 QPolygonF polygon( pointCount );
494 ::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) );
495
496 polygon = QwtClipper::clipPolygonF( clipRect, polygon );
497 qwtDrawPolyline<QPointF>( painter,
498 polygon.constData(), polygon.size(), d_polylineSplitting );
499 }
500 else
501 {
502 qwtDrawPolyline<QPointF>( painter, points, pointCount, d_polylineSplitting );
503 }
504}
505
506//! Wrapper for QPainter::drawPolygon()
507void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon )
508{
509 QRectF clipRect;
510 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
511
512 QPolygon cpa = polygon;
513 if ( deviceClipping )
514 cpa = QwtClipper::clipPolygon( clipRect, polygon, true );
515
516 painter->drawPolygon( cpa );
517}
518
519//! Wrapper for QPainter::drawPolyline()
520void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon )
521{
522 QRectF clipRect;
523 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
524
525 QPolygon cpa = polygon;
526 if ( deviceClipping )
527 cpa = QwtClipper::clipPolygon( clipRect, cpa );
528
529 qwtDrawPolyline<QPoint>( painter,
530 cpa.constData(), cpa.size(), d_polylineSplitting );
531}
532
533//! Wrapper for QPainter::drawPolyline()
534void QwtPainter::drawPolyline( QPainter *painter,
535 const QPoint *points, int pointCount )
536{
537 QRectF clipRect;
538 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
539
540 if ( deviceClipping )
541 {
542 QPolygon polygon( pointCount );
543 ::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) );
544
545 polygon = QwtClipper::clipPolygon( clipRect, polygon );
546 qwtDrawPolyline<QPoint>( painter,
547 polygon.constData(), polygon.size(), d_polylineSplitting );
548 }
549 else
550 {
551 qwtDrawPolyline<QPoint>( painter, points, pointCount, d_polylineSplitting );
552 }
553}
554
555//! Wrapper for QPainter::drawPoint()
556void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
557{
558 QRectF clipRect;
559 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
560
561 if ( deviceClipping && !clipRect.contains( pos ) )
562 return;
563
564 painter->drawPoint( pos );
565}
566
567//! Wrapper for QPainter::drawPoint()
568void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos )
569{
570 QRectF clipRect;
571 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
572
573 if ( deviceClipping )
574 {
575 const int minX = qCeil( clipRect.left() );
576 const int maxX = qFloor( clipRect.right() );
577 const int minY = qCeil( clipRect.top() );
578 const int maxY = qFloor( clipRect.bottom() );
579
580 if ( pos.x() < minX || pos.x() > maxX
581 || pos.y() < minY || pos.y() > maxY )
582 {
583 return;
584 }
585 }
586
587 painter->drawPoint( pos );
588}
589
590//! Wrapper for QPainter::drawPoints()
591void QwtPainter::drawPoints( QPainter *painter,
592 const QPoint *points, int pointCount )
593{
594 QRectF clipRect;
595 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
596
597 if ( deviceClipping )
598 {
599 const int minX = qCeil( clipRect.left() );
600 const int maxX = qFloor( clipRect.right() );
601 const int minY = qCeil( clipRect.top() );
602 const int maxY = qFloor( clipRect.bottom() );
603
604 const QRect r( minX, minY, maxX - minX, maxY - minY );
605
606 QPolygon clippedPolygon( pointCount );
607 QPoint *clippedData = clippedPolygon.data();
608
609 int numClippedPoints = 0;
610 for ( int i = 0; i < pointCount; i++ )
611 {
612 if ( r.contains( points[i] ) )
613 clippedData[ numClippedPoints++ ] = points[i];
614 }
615 painter->drawPoints( clippedData, numClippedPoints );
616 }
617 else
618 {
619 painter->drawPoints( points, pointCount );
620 }
621}
622
623//! Wrapper for QPainter::drawPoints()
624void QwtPainter::drawPoints( QPainter *painter,
625 const QPointF *points, int pointCount )
626{
627 QRectF clipRect;
628 const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
629
630 if ( deviceClipping )
631 {
632 QPolygonF clippedPolygon( pointCount );
633 QPointF *clippedData = clippedPolygon.data();
634
635 int numClippedPoints = 0;
636 for ( int i = 0; i < pointCount; i++ )
637 {
638 if ( clipRect.contains( points[i] ) )
639 clippedData[ numClippedPoints++ ] = points[i];
640 }
641 painter->drawPoints( clippedData, numClippedPoints );
642 }
643 else
644 {
645 painter->drawPoints( points, pointCount );
646 }
647}
648
649//! Wrapper for QPainter::drawImage()
650void QwtPainter::drawImage( QPainter *painter,
651 const QRectF &rect, const QImage &image )
652{
653 const QRect alignedRect = rect.toAlignedRect();
654
655 if ( alignedRect != rect )
656 {
657 const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
658
659 painter->save();
660 painter->setClipRect( clipRect, Qt::IntersectClip );
661 painter->drawImage( alignedRect, image );
662 painter->restore();
663 }
664 else
665 {
666 painter->drawImage( alignedRect, image );
667 }
668}
669
670//! Wrapper for QPainter::drawPixmap()
671void QwtPainter::drawPixmap( QPainter *painter,
672 const QRectF &rect, const QPixmap &pixmap )
673{
674 const QRect alignedRect = rect.toAlignedRect();
675
676 if ( alignedRect != rect )
677 {
678 const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
679
680 painter->save();
681 painter->setClipRect( clipRect, Qt::IntersectClip );
682 painter->drawPixmap( alignedRect, pixmap );
683 painter->restore();
684 }
685 else
686 {
687 painter->drawPixmap( alignedRect, pixmap );
688 }
689}
690
691//! Draw a focus rectangle on a widget using its style.
692void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget )
693{
694 drawFocusRect( painter, widget, widget->rect() );
695}
696
697//! Draw a focus rectangle on a widget using its style.
698void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget,
699 const QRect &rect )
700{
701 QStyleOptionFocusRect opt;
702 opt.init( widget );
703 opt.rect = rect;
704 opt.state |= QStyle::State_HasFocus;
705 opt.backgroundColor = widget->palette().color( widget->backgroundRole() );
706
707 widget->style()->drawPrimitive(
708 QStyle::PE_FrameFocusRect, &opt, painter, widget );
709}
710
711/*!
712 Draw a round frame
713
714 \param painter Painter
715 \param rect Frame rectangle
716 \param palette QPalette::WindowText is used for plain borders
717 QPalette::Dark and QPalette::Light for raised
718 or sunken borders
719 \param lineWidth Line width
720 \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
721*/
722void QwtPainter::drawRoundFrame( QPainter *painter,
723 const QRectF &rect, const QPalette &palette,
724 int lineWidth, int frameStyle )
725{
726 enum Style
727 {
728 Plain,
729 Sunken,
730 Raised
731 };
732
733 Style style = Plain;
734 if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
735 style = Sunken;
736 else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
737 style = Raised;
738
739 const double lw2 = 0.5 * lineWidth;
740 QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
741
742 QBrush brush;
743
744 if ( style != Plain )
745 {
746 QColor c1 = palette.color( QPalette::Light );
747 QColor c2 = palette.color( QPalette::Dark );
748
749 if ( style == Sunken )
750 qSwap( c1, c2 );
751
752 QLinearGradient gradient( r.topLeft(), r.bottomRight() );
753 gradient.setColorAt( 0.0, c1 );
754#if 0
755 gradient.setColorAt( 0.3, c1 );
756 gradient.setColorAt( 0.7, c2 );
757#endif
758 gradient.setColorAt( 1.0, c2 );
759
760 brush = QBrush( gradient );
761 }
762 else // Plain
763 {
764 brush = palette.brush( QPalette::WindowText );
765 }
766
767 painter->save();
768
769 painter->setPen( QPen( brush, lineWidth ) );
770 painter->setBrush( Qt::NoBrush );
771
772 painter->drawEllipse( r );
773
774 painter->restore();
775}
776
777/*!
778 Draw a rectangular frame
779
780 \param painter Painter
781 \param rect Frame rectangle
782 \param palette Palette
783 \param foregroundRole Foreground role used for QFrame::Plain
784 \param frameWidth Frame width
785 \param midLineWidth Used for QFrame::Box
786 \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
787*/
788void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect,
789 const QPalette &palette, QPalette::ColorRole foregroundRole,
790 int frameWidth, int midLineWidth, int frameStyle )
791{
792 if ( frameWidth <= 0 || rect.isEmpty() )
793 return;
794
795 const int shadow = frameStyle & QFrame::Shadow_Mask;
796
797 painter->save();
798
799 if ( shadow == QFrame::Plain )
800 {
801 const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
802 const QRectF innerRect = outerRect.adjusted(
803 frameWidth, frameWidth, -frameWidth, -frameWidth );
804
805 QPainterPath path;
806 path.addRect( outerRect );
807 path.addRect( innerRect );
808
809 painter->setPen( Qt::NoPen );
810 painter->setBrush( palette.color( foregroundRole ) );
811
812 painter->drawPath( path );
813 }
814 else
815 {
816 const int shape = frameStyle & QFrame::Shape_Mask;
817
818 if ( shape == QFrame::Box )
819 {
820 const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
821 const QRectF midRect1 = outerRect.adjusted(
822 frameWidth, frameWidth, -frameWidth, -frameWidth );
823 const QRectF midRect2 = midRect1.adjusted(
824 midLineWidth, midLineWidth, -midLineWidth, -midLineWidth );
825
826 const QRectF innerRect = midRect2.adjusted(
827 frameWidth, frameWidth, -frameWidth, -frameWidth );
828
829 QPainterPath path1;
830 path1.moveTo( outerRect.bottomLeft() );
831 path1.lineTo( outerRect.topLeft() );
832 path1.lineTo( outerRect.topRight() );
833 path1.lineTo( midRect1.topRight() );
834 path1.lineTo( midRect1.topLeft() );
835 path1.lineTo( midRect1.bottomLeft() );
836
837 QPainterPath path2;
838 path2.moveTo( outerRect.bottomLeft() );
839 path2.lineTo( outerRect.bottomRight() );
840 path2.lineTo( outerRect.topRight() );
841 path2.lineTo( midRect1.topRight() );
842 path2.lineTo( midRect1.bottomRight() );
843 path2.lineTo( midRect1.bottomLeft() );
844
845 QPainterPath path3;
846 path3.moveTo( midRect2.bottomLeft() );
847 path3.lineTo( midRect2.topLeft() );
848 path3.lineTo( midRect2.topRight() );
849 path3.lineTo( innerRect.topRight() );
850 path3.lineTo( innerRect.topLeft() );
851 path3.lineTo( innerRect.bottomLeft() );
852
853 QPainterPath path4;
854 path4.moveTo( midRect2.bottomLeft() );
855 path4.lineTo( midRect2.bottomRight() );
856 path4.lineTo( midRect2.topRight() );
857 path4.lineTo( innerRect.topRight() );
858 path4.lineTo( innerRect.bottomRight() );
859 path4.lineTo( innerRect.bottomLeft() );
860
861 QPainterPath path5;
862 path5.addRect( midRect1 );
863 path5.addRect( midRect2 );
864
865 painter->setPen( Qt::NoPen );
866
867 QBrush brush1 = palette.dark().color();
868 QBrush brush2 = palette.light().color();
869
870 if ( shadow == QFrame::Raised )
871 qSwap( brush1, brush2 );
872
873 painter->setBrush( brush1 );
874 painter->drawPath( path1 );
875 painter->drawPath( path4 );
876
877 painter->setBrush( brush2 );
878 painter->drawPath( path2 );
879 painter->drawPath( path3 );
880
881 painter->setBrush( palette.mid() );
882 painter->drawPath( path5 );
883 }
884#if 0
885 // qDrawWinPanel doesn't result in something nice
886 // on a scalable document like PDF. Better draw a
887 // Panel.
888
889 else if ( shape == QFrame::WinPanel )
890 {
891 painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true );
892 qDrawWinPanel ( painter, rect.toRect(), palette,
893 frameStyle & QFrame::Sunken );
894 }
895 else if ( shape == QFrame::StyledPanel )
896 {
897 }
898#endif
899 else
900 {
901 const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
902 const QRectF innerRect = outerRect.adjusted(
903 frameWidth - 1.0, frameWidth - 1.0,
904 -( frameWidth - 1.0 ), -( frameWidth - 1.0 ) );
905
906 QPainterPath path1;
907 path1.moveTo( outerRect.bottomLeft() );
908 path1.lineTo( outerRect.topLeft() );
909 path1.lineTo( outerRect.topRight() );
910 path1.lineTo( innerRect.topRight() );
911 path1.lineTo( innerRect.topLeft() );
912 path1.lineTo( innerRect.bottomLeft() );
913
914
915 QPainterPath path2;
916 path2.moveTo( outerRect.bottomLeft() );
917 path2.lineTo( outerRect.bottomRight() );
918 path2.lineTo( outerRect.topRight() );
919 path2.lineTo( innerRect.topRight() );
920 path2.lineTo( innerRect.bottomRight() );
921 path2.lineTo( innerRect.bottomLeft() );
922
923 painter->setPen( Qt::NoPen );
924
925 QBrush brush1 = palette.dark().color();
926 QBrush brush2 = palette.light().color();
927
928 if ( shadow == QFrame::Raised )
929 qSwap( brush1, brush2 );
930
931 painter->setBrush( brush1 );
932 painter->drawPath( path1 );
933
934 painter->setBrush( brush2 );
935 painter->drawPath( path2 );
936 }
937
938 }
939
940 painter->restore();
941}
942
943/*!
944 Draw a rectangular frame with rounded borders
945
946 \param painter Painter
947 \param rect Frame rectangle
948 \param xRadius x-radius of the ellipses defining the corners
949 \param yRadius y-radius of the ellipses defining the corners
950 \param palette QPalette::WindowText is used for plain borders
951 QPalette::Dark and QPalette::Light for raised
952 or sunken borders
953 \param lineWidth Line width
954 \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
955*/
956
957void QwtPainter::drawRoundedFrame( QPainter *painter,
958 const QRectF &rect, double xRadius, double yRadius,
959 const QPalette &palette, int lineWidth, int frameStyle )
960{
961 painter->save();
962 painter->setRenderHint( QPainter::Antialiasing, true );
963 painter->setBrush( Qt::NoBrush );
964
965 double lw2 = lineWidth * 0.5;
966 QRectF innerRect = rect.adjusted( lw2, lw2, -lw2, -lw2 );
967
968 QPainterPath path;
969 path.addRoundedRect( innerRect, xRadius, yRadius );
970
971 enum Style
972 {
973 Plain,
974 Sunken,
975 Raised
976 };
977
978 Style style = Plain;
979 if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
980 style = Sunken;
981 else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
982 style = Raised;
983
984 if ( style != Plain && path.elementCount() == 17 )
985 {
986 // move + 4 * ( cubicTo + lineTo )
987 QPainterPath pathList[8];
988
989 for ( int i = 0; i < 4; i++ )
990 {
991 const int j = i * 4 + 1;
992
993 pathList[ 2 * i ].moveTo(
994 path.elementAt(j - 1).x, path.elementAt( j - 1 ).y
995 );
996
997 pathList[ 2 * i ].cubicTo(
998 path.elementAt(j + 0).x, path.elementAt(j + 0).y,
999 path.elementAt(j + 1).x, path.elementAt(j + 1).y,
1000 path.elementAt(j + 2).x, path.elementAt(j + 2).y );
1001
1002 pathList[ 2 * i + 1 ].moveTo(
1003 path.elementAt(j + 2).x, path.elementAt(j + 2).y
1004 );
1005 pathList[ 2 * i + 1 ].lineTo(
1006 path.elementAt(j + 3).x, path.elementAt(j + 3).y
1007 );
1008 }
1009
1010 QColor c1( palette.color( QPalette::Dark ) );
1011 QColor c2( palette.color( QPalette::Light ) );
1012
1013 if ( style == Raised )
1014 qSwap( c1, c2 );
1015
1016 for ( int i = 0; i < 4; i++ )
1017 {
1018 const QRectF r = pathList[2 * i].controlPointRect();
1019
1020 QPen arcPen;
1021 arcPen.setCapStyle( Qt::FlatCap );
1022 arcPen.setWidth( lineWidth );
1023
1024 QPen linePen;
1025 linePen.setCapStyle( Qt::FlatCap );
1026 linePen.setWidth( lineWidth );
1027
1028 switch( i )
1029 {
1030 case 0:
1031 {
1032 arcPen.setColor( c1 );
1033 linePen.setColor( c1 );
1034 break;
1035 }
1036 case 1:
1037 {
1038 QLinearGradient gradient;
1039 gradient.setStart( r.topLeft() );
1040 gradient.setFinalStop( r.bottomRight() );
1041 gradient.setColorAt( 0.0, c1 );
1042 gradient.setColorAt( 1.0, c2 );
1043
1044 arcPen.setBrush( gradient );
1045 linePen.setColor( c2 );
1046 break;
1047 }
1048 case 2:
1049 {
1050 arcPen.setColor( c2 );
1051 linePen.setColor( c2 );
1052 break;
1053 }
1054 case 3:
1055 {
1056 QLinearGradient gradient;
1057
1058 gradient.setStart( r.bottomRight() );
1059 gradient.setFinalStop( r.topLeft() );
1060 gradient.setColorAt( 0.0, c2 );
1061 gradient.setColorAt( 1.0, c1 );
1062
1063 arcPen.setBrush( gradient );
1064 linePen.setColor( c1 );
1065 break;
1066 }
1067 }
1068
1069
1070 painter->setPen( arcPen );
1071 painter->drawPath( pathList[ 2 * i] );
1072
1073 painter->setPen( linePen );
1074 painter->drawPath( pathList[ 2 * i + 1] );
1075 }
1076 }
1077 else
1078 {
1079 QPen pen( palette.color( QPalette::WindowText ), lineWidth );
1080 painter->setPen( pen );
1081 painter->drawPath( path );
1082 }
1083
1084 painter->restore();
1085}
1086
1087/*!
1088 Draw a color bar into a rectangle
1089
1090 \param painter Painter
1091 \param colorMap Color map
1092 \param interval Value range
1093 \param scaleMap Scale map
1094 \param orientation Orientation
1095 \param rect Traget rectangle
1096*/
1097void QwtPainter::drawColorBar( QPainter *painter,
1098 const QwtColorMap &colorMap, const QwtInterval &interval,
1099 const QwtScaleMap &scaleMap, Qt::Orientation orientation,
1100 const QRectF &rect )
1101{
1102 QVector<QRgb> colorTable;
1103 if ( colorMap.format() == QwtColorMap::Indexed )
1104 colorTable = colorMap.colorTable( interval );
1105
1106 QColor c;
1107
1108 const QRect devRect = rect.toAlignedRect();
1109
1110 /*
1111 We paint to a pixmap first to have something scalable for printing
1112 ( f.e. in a Pdf document )
1113 */
1114
1115 QPixmap pixmap( devRect.size() );
1116 pixmap.fill( Qt::transparent );
1117
1118 QPainter pmPainter( &pixmap );
1119 pmPainter.translate( -devRect.x(), -devRect.y() );
1120
1121 if ( orientation == Qt::Horizontal )
1122 {
1123 QwtScaleMap sMap = scaleMap;
1124 sMap.setPaintInterval( rect.left(), rect.right() );
1125
1126 for ( int x = devRect.left(); x <= devRect.right(); x++ )
1127 {
1128 const double value = sMap.invTransform( x );
1129
1130 if ( colorMap.format() == QwtColorMap::RGB )
1131 c.setRgba( colorMap.rgb( interval, value ) );
1132 else
1133 c = colorTable[colorMap.colorIndex( interval, value )];
1134
1135 pmPainter.setPen( c );
1136 pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
1137 }
1138 }
1139 else // Vertical
1140 {
1141 QwtScaleMap sMap = scaleMap;
1142 sMap.setPaintInterval( rect.bottom(), rect.top() );
1143
1144 for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
1145 {
1146 const double value = sMap.invTransform( y );
1147
1148 if ( colorMap.format() == QwtColorMap::RGB )
1149 c.setRgba( colorMap.rgb( interval, value ) );
1150 else
1151 c = colorTable[colorMap.colorIndex( interval, value )];
1152
1153 pmPainter.setPen( c );
1154 pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
1155 }
1156 }
1157 pmPainter.end();
1158
1159 drawPixmap( painter, rect, pixmap );
1160}
1161
1162static inline void qwtFillRect( const QWidget *widget, QPainter *painter,
1163 const QRect &rect, const QBrush &brush)
1164{
1165 if ( brush.style() == Qt::TexturePattern )
1166 {
1167 painter->save();
1168
1169 painter->setClipRect( rect );
1170 painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
1171
1172 painter->restore();
1173 }
1174 else if ( brush.gradient() )
1175 {
1176 painter->save();
1177
1178 painter->setClipRect( rect );
1179 painter->fillRect(0, 0, widget->width(),
1180 widget->height(), brush);
1181
1182 painter->restore();
1183 }
1184 else
1185 {
1186 painter->fillRect(rect, brush);
1187 }
1188}
1189
1190/*!
1191 Fill a pixmap with the content of a widget
1192
1193 In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy
1194 for backgrounds with gradients. Thus fillPixmap() offers
1195 an alternative implementation.
1196
1197 \param widget Widget
1198 \param pixmap Pixmap to be filled
1199 \param offset Offset
1200
1201 \sa QPixmap::fill()
1202 */
1203void QwtPainter::fillPixmap( const QWidget *widget,
1204 QPixmap &pixmap, const QPoint &offset )
1205{
1206 const QRect rect( offset, pixmap.size() );
1207
1208 QPainter painter( &pixmap );
1209 painter.translate( -offset );
1210
1211 const QBrush autoFillBrush =
1212 widget->palette().brush( widget->backgroundRole() );
1213
1214 if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) )
1215 {
1216 const QBrush bg = widget->palette().brush( QPalette::Window );
1217 qwtFillRect( widget, &painter, rect, bg);
1218 }
1219
1220 if ( widget->autoFillBackground() )
1221 qwtFillRect( widget, &painter, rect, autoFillBrush);
1222
1223 if ( widget->testAttribute(Qt::WA_StyledBackground) )
1224 {
1225 painter.setClipRegion( rect );
1226
1227 QStyleOption opt;
1228 opt.initFrom( widget );
1229 widget->style()->drawPrimitive( QStyle::PE_Widget,
1230 &opt, &painter, widget );
1231 }
1232}
1233
1234/*!
1235 Fill rect with the background of a widget
1236
1237 \param painter Painter
1238 \param rect Rectangle to be filled
1239 \param widget Widget
1240
1241 \sa QStyle::PE_Widget, QWidget::backgroundRole()
1242 */
1243void QwtPainter::drawBackgound( QPainter *painter,
1244 const QRectF &rect, const QWidget *widget )
1245{
1246 if ( widget->testAttribute( Qt::WA_StyledBackground ) )
1247 {
1248 QStyleOption opt;
1249 opt.initFrom( widget );
1250 opt.rect = rect.toAlignedRect();
1251
1252 widget->style()->drawPrimitive(
1253 QStyle::PE_Widget, &opt, painter, widget);
1254 }
1255 else
1256 {
1257 const QBrush brush =
1258 widget->palette().brush( widget->backgroundRole() );
1259
1260 painter->fillRect( rect, brush );
1261 }
1262}
1263
1264/*!
1265 \return A pixmap that can be used as backing store
1266
1267 \param widget Widget, for which the backingstore is intended
1268 \param size Size of the pixmap
1269 */
1270QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size )
1271{
1272 QPixmap pm;
1273
1274#define QWT_HIGH_DPI 1
1275
1276#if QT_VERSION >= 0x050000 && QWT_HIGH_DPI
1277 qreal pixelRatio = 1.0;
1278
1279 if ( widget && widget->windowHandle() )
1280 {
1281#if QT_VERSION < 0x050100
1282 pixelRatio = widget->windowHandle()->devicePixelRatio();
1283#else
1284 pixelRatio = widget->devicePixelRatio();
1285#endif
1286 }
1287 else
1288 {
1289 if ( qApp )
1290 pixelRatio = qApp->devicePixelRatio();
1291 }
1292
1293 pm = QPixmap( size * pixelRatio );
1294 pm.setDevicePixelRatio( pixelRatio );
1295#else
1296 Q_UNUSED( widget )
1297 pm = QPixmap( size );
1298#endif
1299
1300#if QT_VERSION < 0x050000
1301#ifdef Q_WS_X11
1302 if ( widget && isX11GraphicsSystem() )
1303 {
1304 if ( pm.x11Info().screen() != widget->x11Info().screen() )
1305 pm.x11SetScreen( widget->x11Info().screen() );
1306 }
1307#endif
1308#endif
1309
1310 return pm;
1311}
1312
Note: See TracBrowser for help on using the repository browser.