qwt_plot_print.cpp 15.1 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

// vim: expandtab

#include <qpainter.h>
#if QT_VERSION < 0x040000
#include <qpaintdevicemetrics.h>
#else
#include <qpaintengine.h>
#endif
#include "qwt_painter.h"
#include "qwt_legend_item.h"
#include "qwt_plot.h"
#include "qwt_plot_canvas.h"
#include "qwt_plot_layout.h"
#include "qwt_legend.h"
#include "qwt_dyngrid_layout.h"
#include "qwt_scale_widget.h"
#include "qwt_scale_engine.h"
#include "qwt_text.h"
#include "qwt_text_label.h"
#include "qwt_math.h"

/*!
  \brief Print the plot to a \c QPaintDevice (\c QPrinter)
  This function prints the contents of a QwtPlot instance to
  \c QPaintDevice object. The size is derived from its device
  metrics.

  \param paintDev device to paint on, often a printer
  \param pfilter print filter
  \sa QwtPlot::print
  \sa QwtPlotPrintFilter
*/

void QwtPlot::print(QPaintDevice &paintDev,
44
                    const QwtPlotPrintFilter &pfilter) const
pixhawk's avatar
pixhawk committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
{
#if QT_VERSION < 0x040000
    QPaintDeviceMetrics mpr(&paintDev);
    int w = mpr.width();
    int h = mpr.height();
#else
    int w = paintDev.width();
    int h = paintDev.height();
#endif

    QRect rect(0, 0, w, h);
    double aspect = double(rect.width())/double(rect.height());
    if ((aspect < 1.0))
        rect.setHeight(int(aspect*rect.width()));

    QPainter p(&paintDev);
    print(&p, rect, pfilter);
}

/*!
  \brief Paint the plot into a given rectangle.
  Paint the contents of a QwtPlot instance into a given rectangle.

  \param painter Painter
  \param plotRect Bounding rectangle
  \param pfilter Print filter
  \sa QwtPlotPrintFilter
*/
void QwtPlot::print(QPainter *painter, const QRect &plotRect,
74
                    const QwtPlotPrintFilter &pfilter) const
pixhawk's avatar
pixhawk committed
75 76 77 78 79
{
    int axisId;

    if ( painter == 0 || !painter->isActive() ||
            !plotRect.isValid() || size().isNull() )
80
        return;
pixhawk's avatar
pixhawk committed
81 82 83 84 85 86 87 88 89 90 91 92 93 94

    painter->save();
#if 1
    /*
      PDF: In Qt4 ( <= 4.3.2 ) the scales are painted in gray instead of
      black. See http://trolltech.com/developer/task-tracker/index_html?id=184671&method=entry
      The dummy lines below work around the problem.
     */
    const QPen pen = painter->pen();
    painter->setPen(QPen(Qt::black, 1));
    painter->setPen(pen);
#endif

    // All paint operations need to be scaled according to
95
    // the paint device metrics.
pixhawk's avatar
pixhawk committed
96 97 98 99 100 101 102

    QwtPainter::setMetricsMap(this, painter->device());
    const QwtMetricsMap &metricsMap = QwtPainter::metricsMap();

    // It is almost impossible to integrate into the Qt layout
    // framework, when using different fonts for printing
    // and screen. To avoid writing different and Qt unconform
103
    // layout engines we change the widget attributes, print and
pixhawk's avatar
pixhawk committed
104 105 106 107 108 109
    // reset the widget attributes again. This way we produce a lot of
    // useless layout events ...

    pfilter.apply((QwtPlot *)this);

    int baseLineDists[QwtPlot::axisCnt];
110 111
    if ( pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales ) {
        for (axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) {
pixhawk's avatar
pixhawk committed
112
            QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(axisId);
113
            if ( scaleWidget ) {
pixhawk's avatar
pixhawk committed
114 115 116 117 118 119 120
                baseLineDists[axisId] = scaleWidget->margin();
                scaleWidget->setMargin(0);
            }
        }
    }
    // Calculate the layout for the print.

121 122
    int layoutOptions = QwtPlotLayout::IgnoreScrollbars
                        | QwtPlotLayout::IgnoreFrames;
pixhawk's avatar
pixhawk committed
123 124 125 126 127
    if ( !(pfilter.options() & QwtPlotPrintFilter::PrintMargin) )
        layoutOptions |= QwtPlotLayout::IgnoreMargin;
    if ( !(pfilter.options() & QwtPlotPrintFilter::PrintLegend) )
        layoutOptions |= QwtPlotLayout::IgnoreLegend;

128 129 130
    ((QwtPlot *)this)->plotLayout()->activate(this,
            QwtPainter::metricsMap().deviceToLayout(plotRect),
            layoutOptions);
pixhawk's avatar
pixhawk committed
131 132

    if ((pfilter.options() & QwtPlotPrintFilter::PrintTitle)
133
            && (!titleLabel()->text().isEmpty())) {
pixhawk's avatar
pixhawk committed
134 135 136 137
        printTitle(painter, plotLayout()->titleRect());
    }

    if ( (pfilter.options() & QwtPlotPrintFilter::PrintLegend)
138
            && legend() && !legend()->isEmpty() ) {
pixhawk's avatar
pixhawk committed
139 140 141
        printLegend(painter, plotLayout()->legendRect());
    }

142
    for ( axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) {
pixhawk's avatar
pixhawk committed
143
        QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(axisId);
144
        if (scaleWidget) {
pixhawk's avatar
pixhawk committed
145 146 147 148 149 150
            int baseDist = scaleWidget->margin();

            int startDist, endDist;
            scaleWidget->getBorderDistHint(startDist, endDist);

            printScale(painter, axisId, startDist, endDist,
151
                       baseDist, plotLayout()->scaleRect(axisId));
pixhawk's avatar
pixhawk committed
152 153 154 155 156
        }
    }

    QRect canvasRect = plotLayout()->canvasRect();

157
    /*
pixhawk's avatar
pixhawk committed
158
       The border of the bounding rect needs to ba scaled to
159
       layout coordinates, so that it is aligned to the axes
pixhawk's avatar
pixhawk committed
160 161
     */
    QRect boundingRect( canvasRect.left() - 1, canvasRect.top() - 1,
162
                        canvasRect.width() + 2, canvasRect.height() + 2);
pixhawk's avatar
pixhawk committed
163 164 165 166 167
    boundingRect = metricsMap.layoutToDevice(boundingRect);
    boundingRect.setWidth(boundingRect.width() - 1);
    boundingRect.setHeight(boundingRect.height() - 1);

    canvasRect = metricsMap.layoutToDevice(canvasRect);
168

pixhawk's avatar
pixhawk committed
169 170 171 172 173 174
    // When using QwtPainter all sizes where computed in pixel
    // coordinates and scaled by QwtPainter later. This limits
    // the precision to screen resolution. A better solution
    // is to scale the maps and print in unlimited resolution.

    QwtScaleMap map[axisCnt];
175
    for (axisId = 0; axisId < axisCnt; axisId++) {
pixhawk's avatar
pixhawk committed
176 177 178 179 180 181
        map[axisId].setTransformation(axisScaleEngine(axisId)->transformation());

        const QwtScaleDiv &scaleDiv = *axisScaleDiv(axisId);
        map[axisId].setScaleInterval(scaleDiv.lBound(), scaleDiv.hBound());

        double from, to;
182
        if ( axisEnabled(axisId) ) {
pixhawk's avatar
pixhawk committed
183 184 185 186
            const int sDist = axisWidget(axisId)->startBorderDist();
            const int eDist = axisWidget(axisId)->endBorderDist();
            const QRect &scaleRect = plotLayout()->scaleRect(axisId);

187
            if ( axisId == xTop || axisId == xBottom ) {
pixhawk's avatar
pixhawk committed
188 189
                from = metricsMap.layoutToDeviceX(scaleRect.left() + sDist);
                to = metricsMap.layoutToDeviceX(scaleRect.right() + 1 - eDist);
190
            } else {
pixhawk's avatar
pixhawk committed
191 192 193
                from = metricsMap.layoutToDeviceY(scaleRect.bottom() + 1 - eDist );
                to = metricsMap.layoutToDeviceY(scaleRect.top() + sDist);
            }
194
        } else {
pixhawk's avatar
pixhawk committed
195
            int margin = plotLayout()->canvasMargin(axisId);
196
            if ( axisId == yLeft || axisId == yRight ) {
pixhawk's avatar
pixhawk committed
197 198 199
                margin = metricsMap.layoutToDeviceY(margin);
                from = canvasRect.bottom() - margin;
                to = canvasRect.top() + margin;
200
            } else {
pixhawk's avatar
pixhawk committed
201 202 203 204 205 206 207 208
                margin = metricsMap.layoutToDeviceX(margin);
                from = canvasRect.left() + margin;
                to = canvasRect.right() - margin;
            }
        }
        map[axisId].setPaintXInterval(from, to);
    }

209
    // The canvas maps are already scaled.
pixhawk's avatar
pixhawk committed
210 211 212 213 214 215 216
    QwtPainter::setMetricsMap(painter->device(), painter->device());
    printCanvas(painter, boundingRect, canvasRect, map, pfilter);
    QwtPainter::resetMetricsMap();

    ((QwtPlot *)this)->plotLayout()->invalidate();

    // reset all widgets with their original attributes.
217
    if ( pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales ) {
pixhawk's avatar
pixhawk committed
218 219
        // restore the previous base line dists

220
        for (axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) {
pixhawk's avatar
pixhawk committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
            QwtScaleWidget *scaleWidget = (QwtScaleWidget *)axisWidget(axisId);
            if ( scaleWidget  )
                scaleWidget->setMargin(baseLineDists[axisId]);
        }
    }

    pfilter.reset((QwtPlot *)this);

    painter->restore();
}

/*!
  Print the title into a given rectangle.

  \param painter Painter
  \param rect Bounding rectangle
*/

void QwtPlot::printTitle(QPainter *painter, const QRect &rect) const
{
    painter->setFont(titleLabel()->font());

243
    const QColor color =
pixhawk's avatar
pixhawk committed
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
#if QT_VERSION < 0x040000
        titleLabel()->palette().color(
            QPalette::Active, QColorGroup::Text);
#else
        titleLabel()->palette().color(
            QPalette::Active, QPalette::Text);
#endif

    painter->setPen(color);
    titleLabel()->text().draw(painter, rect);
}

/*!
  Print the legend into a given rectangle.

  \param painter Painter
  \param rect Bounding rectangle
*/

void QwtPlot::printLegend(QPainter *painter, const QRect &rect) const
{
    if ( !legend() || legend()->isEmpty() )
        return;

    QLayout *l = legend()->contentsWidget()->layout();
    if ( l == 0 || !l->inherits("QwtDynGridLayout") )
        return;

    QwtDynGridLayout *legendLayout = (QwtDynGridLayout *)l;

    uint numCols = legendLayout->columnsForWidth(rect.width());
#if QT_VERSION < 0x040000
276
    QValueList<QRect> itemRects =
pixhawk's avatar
pixhawk committed
277 278
        legendLayout->layoutItems(rect, numCols);
#else
279
    QList<QRect> itemRects =
pixhawk's avatar
pixhawk committed
280 281 282 283 284 285 286
        legendLayout->layoutItems(rect, numCols);
#endif

    int index = 0;

#if QT_VERSION < 0x040000
    QLayoutIterator layoutIterator = legendLayout->iterator();
287 288
    for ( QLayoutItem *item = layoutIterator.current();
            item != 0; item = ++layoutIterator) {
pixhawk's avatar
pixhawk committed
289
#else
290
    for ( int i = 0; i < legendLayout->count(); i++ ) {
pixhawk's avatar
pixhawk committed
291 292 293
        QLayoutItem *item = legendLayout->itemAt(i);
#endif
        QWidget *w = item->widget();
294
        if ( w ) {
pixhawk's avatar
pixhawk committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
            painter->save();
            painter->setClipping(true);
            QwtPainter::setClipRect(painter, itemRects[index]);

            printLegendItem(painter, w, itemRects[index]);

            index++;
            painter->restore();
        }
    }
}

/*!
  Print the legend item into a given rectangle.

  \param painter Painter
  \param w Widget representing a legend item
  \param rect Bounding rectangle
*/

315 316
void QwtPlot::printLegendItem(QPainter *painter,
                              const QWidget *w, const QRect &rect) const
pixhawk's avatar
pixhawk committed
317
{
318
    if ( w->inherits("QwtLegendItem") ) {
pixhawk's avatar
pixhawk committed
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
        QwtLegendItem *item = (QwtLegendItem *)w;

        painter->setFont(item->font());
        item->drawItem(painter, rect);
    }
}

/*!
  \brief Paint a scale into a given rectangle.
  Paint the scale into a given rectangle.

  \param painter Painter
  \param axisId Axis
  \param startDist Start border distance
  \param endDist End border distance
  \param baseDist Base distance
  \param rect Bounding rectangle
*/

void QwtPlot::printScale(QPainter *painter,
339 340
                         int axisId, int startDist, int endDist, int baseDist,
                         const QRect &rect) const
pixhawk's avatar
pixhawk committed
341 342 343 344 345
{
    if (!axisEnabled(axisId))
        return;

    const QwtScaleWidget *scaleWidget = axisWidget(axisId);
346 347
    if ( scaleWidget->isColorBarEnabled()
            && scaleWidget->colorBarWidth() > 0) {
pixhawk's avatar
pixhawk committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
        const QwtMetricsMap map = QwtPainter::metricsMap();

        QRect r = map.layoutToScreen(rect);
        r.setWidth(r.width() - 1);
        r.setHeight(r.height() - 1);

        scaleWidget->drawColorBar(painter, scaleWidget->colorBarRect(r));

        const int off = scaleWidget->colorBarWidth() + scaleWidget->spacing();
        if ( scaleWidget->scaleDraw()->orientation() == Qt::Horizontal )
            baseDist += map.screenToLayoutY(off);
        else
            baseDist += map.screenToLayoutX(off);
    }

    QwtScaleDraw::Alignment align;
    int x, y, w;

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
    switch(axisId) {
    case yLeft: {
        x = rect.right() - baseDist;
        y = rect.y() + startDist;
        w = rect.height() - startDist - endDist;
        align = QwtScaleDraw::LeftScale;
        break;
    }
    case yRight: {
        x = rect.left() + baseDist;
        y = rect.y() + startDist;
        w = rect.height() - startDist - endDist;
        align = QwtScaleDraw::RightScale;
        break;
    }
    case xTop: {
        x = rect.left() + startDist;
        y = rect.bottom() - baseDist;
        w = rect.width() - startDist - endDist;
        align = QwtScaleDraw::TopScale;
        break;
    }
    case xBottom: {
        x = rect.left() + startDist;
        y = rect.top() + baseDist;
        w = rect.width() - startDist - endDist;
        align = QwtScaleDraw::BottomScale;
        break;
    }
    default:
        return;
pixhawk's avatar
pixhawk committed
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    }

    scaleWidget->drawTitle(painter, align, rect);

    painter->save();
    painter->setFont(scaleWidget->font());

    QPen pen = painter->pen();
    pen.setWidth(scaleWidget->penWidth());
    painter->setPen(pen);

    QwtScaleDraw *sd = (QwtScaleDraw *)scaleWidget->scaleDraw();
    const QPoint sdPos = sd->pos();
    const int sdLength = sd->length();

    sd->move(x, y);
    sd->setLength(w);

#if QT_VERSION < 0x040000
    sd->draw(painter, scaleWidget->palette().active());
#else
    QPalette palette = scaleWidget->palette();
    palette.setCurrentColorGroup(QPalette::Active);
    sd->draw(painter, palette);
#endif
    // reset previous values
423 424
    sd->move(sdPos);
    sd->setLength(sdLength);
pixhawk's avatar
pixhawk committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

    painter->restore();
}

/*!
  Print the canvas into a given rectangle.

  \param painter Painter
  \param map Maps mapping between plot and paint device coordinates
  \param boundingRect Bounding rectangle
  \param canvasRect Canvas rectangle
  \param pfilter Print filter
  \sa QwtPlotPrintFilter
*/

440 441 442
void QwtPlot::printCanvas(QPainter *painter,
                          const QRect &boundingRect, const QRect &canvasRect,
                          const QwtScaleMap map[axisCnt], const QwtPlotPrintFilter &pfilter) const
pixhawk's avatar
pixhawk committed
443
{
444
    if ( pfilter.options() & QwtPlotPrintFilter::PrintBackground ) {
pixhawk's avatar
pixhawk committed
445 446
        QBrush bgBrush;
#if QT_VERSION >= 0x040000
447
        bgBrush = canvas()->palette().brush(backgroundRole());
pixhawk's avatar
pixhawk committed
448 449 450 451 452 453
#else
        QColorGroup::ColorRole role =
            QPalette::backgroundRoleFromMode( backgroundMode() );
        bgBrush = canvas()->colorGroup().brush( role );
#endif
        QRect r = boundingRect;
454
        if ( !(pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales) ) {
pixhawk's avatar
pixhawk committed
455 456 457 458
            r = canvasRect;
#if QT_VERSION >= 0x040000
            // Unfortunately the paint engines do no always the same
            const QPaintEngine *pe = painter->paintEngine();
459 460 461 462 463 464 465 466 467
            if ( pe ) {
                switch(painter->paintEngine()->type() ) {
                case QPaintEngine::Raster:
                case QPaintEngine::X11:
                    break;
                default:
                    r.setWidth(r.width() - 1);
                    r.setHeight(r.height() - 1);
                    break;
pixhawk's avatar
pixhawk committed
468 469 470
                }
            }
#else
471
            if ( painter->device()->isExtDev() ) {
pixhawk's avatar
pixhawk committed
472
                r.setWidth(r.width() - 1);
473
                r.setHeight(r.height() - 1);
pixhawk's avatar
pixhawk committed
474 475 476 477 478 479 480
            }
#endif
        }

        QwtPainter::fillRect(painter, r, bgBrush);
    }

481
    if ( pfilter.options() & QwtPlotPrintFilter::PrintFrameWithScales ) {
pixhawk's avatar
pixhawk committed
482 483 484 485 486 487 488 489 490 491 492 493
        painter->save();
        painter->setPen(QPen(Qt::black));
        painter->setBrush(QBrush(Qt::NoBrush));
        QwtPainter::drawRect(painter, boundingRect);
        painter->restore();
    }

    painter->setClipping(true);
    QwtPainter::setClipRect(painter, canvasRect);

    drawItems(painter, canvasRect, map, pfilter);
}