1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qprintpreviewdialog.h"
41 #include "qprintpreviewwidget.h"
42 #include <private/qprinter_p.h>
43 #include "qprintdialog.h"
44 
45 #include <QtWidgets/qaction.h>
46 #include <QtWidgets/qboxlayout.h>
47 #include <QtWidgets/qcombobox.h>
48 #include <QtWidgets/qlineedit.h>
49 #include <QtPrintSupport/qpagesetupdialog.h>
50 #include <QtPrintSupport/qprinter.h>
51 #include <QtWidgets/qstyle.h>
52 #include <QtWidgets/qtoolbutton.h>
53 #include <QtGui/qvalidator.h>
54 #if QT_CONFIG(filedialog)
55 #include <QtWidgets/qfiledialog.h>
56 #endif
57 #include <QtWidgets/qmainwindow.h>
58 #include <QtWidgets/qtoolbar.h>
59 #include <QtCore/QCoreApplication>
60 
61 #include "private/qdialog_p.h"
62 
63 #include <QtWidgets/qformlayout.h>
64 #include <QtWidgets/qlabel.h>
65 
initResources()66 static void initResources()
67 {
68     static bool resourcesInitialized = false;
69     if (!resourcesInitialized) {
70         Q_INIT_RESOURCE(qprintdialog);
71         resourcesInitialized = true;
72     }
73 }
74 
75 QT_BEGIN_NAMESPACE
76 
77 namespace {
78 class QPrintPreviewMainWindow : public QMainWindow
79 {
80 public:
QPrintPreviewMainWindow(QWidget * parent)81     QPrintPreviewMainWindow(QWidget *parent) : QMainWindow(parent) {}
createPopupMenu()82     QMenu *createPopupMenu() override { return nullptr; }
83 };
84 
85 class ZoomFactorValidator : public QDoubleValidator
86 {
87 public:
ZoomFactorValidator(QObject * parent)88     ZoomFactorValidator(QObject* parent)
89         : QDoubleValidator(parent) {}
ZoomFactorValidator(qreal bottom,qreal top,int decimals,QObject * parent)90     ZoomFactorValidator(qreal bottom, qreal top, int decimals, QObject *parent)
91         : QDoubleValidator(bottom, top, decimals, parent) {}
92 
validate(QString & input,int & pos) const93     State validate(QString &input, int &pos) const override
94     {
95         bool replacePercent = false;
96         if (input.endsWith(QLatin1Char('%'))) {
97             input = input.left(input.length() - 1);
98             replacePercent = true;
99         }
100         State state = QDoubleValidator::validate(input, pos);
101         if (replacePercent)
102             input += QLatin1Char('%');
103         const int num_size = 4;
104         if (state == Intermediate) {
105             int i = input.indexOf(QLocale::system().decimalPoint());
106             if ((i == -1 && input.size() > num_size)
107                 || (i != -1 && i > num_size))
108                 return Invalid;
109         }
110         return state;
111     }
112 };
113 
114 class LineEdit : public QLineEdit
115 {
116     Q_OBJECT
117 public:
LineEdit(QWidget * parent=nullptr)118     LineEdit(QWidget* parent = nullptr)
119         : QLineEdit(parent)
120     {
121         setContextMenuPolicy(Qt::NoContextMenu);
122         connect(this, &LineEdit::returnPressed, this, &LineEdit::handleReturnPressed);
123     }
124 
125 protected:
focusInEvent(QFocusEvent * e)126     void focusInEvent(QFocusEvent *e) override
127     {
128         origText = text();
129         QLineEdit::focusInEvent(e);
130     }
131 
focusOutEvent(QFocusEvent * e)132     void focusOutEvent(QFocusEvent *e) override
133     {
134         if (isModified() && !hasAcceptableInput())
135             setText(origText);
136         QLineEdit::focusOutEvent(e);
137     }
138 
139 private slots:
handleReturnPressed()140     void handleReturnPressed()
141     {
142         origText = text();
143     }
144 
145 private:
146     QString origText;
147 };
148 } // anonymous namespace
149 
150 class QPrintPreviewDialogPrivate : public QDialogPrivate
151 {
152     Q_DECLARE_PUBLIC(QPrintPreviewDialog)
153 public:
QPrintPreviewDialogPrivate()154     QPrintPreviewDialogPrivate()
155         : printDialog(nullptr), pageSetupDialog(nullptr),
156           ownPrinter(false), initialized(false)  {}
157 
158     // private slots
159     void _q_fit(QAction *action);
160     void _q_zoomIn();
161     void _q_zoomOut();
162     void _q_navigate(QAction *action);
163     void _q_setMode(QAction *action);
164     void _q_pageNumEdited();
165     void _q_print();
166     void _q_pageSetup();
167     void _q_previewChanged();
168     void _q_zoomFactorChanged();
169 
170     void init(QPrinter *printer = nullptr);
171     void populateScene();
172     void layoutPages();
173     void setupActions();
174     void updateNavActions();
175     void setFitting(bool on);
176     bool isFitting();
177     void updatePageNumLabel();
178     void updateZoomFactor();
179 
180     QPrintDialog *printDialog;
181     QPageSetupDialog *pageSetupDialog;
182     QPrintPreviewWidget *preview;
183     QPrinter *printer;
184     bool ownPrinter;
185     bool initialized;
186 
187     // widgets:
188     QLineEdit *pageNumEdit;
189     QLabel *pageNumLabel;
190     QComboBox *zoomFactor;
191 
192     // actions:
193     QActionGroup* navGroup;
194     QAction *nextPageAction;
195     QAction *prevPageAction;
196     QAction *firstPageAction;
197     QAction *lastPageAction;
198 
199     QActionGroup* fitGroup;
200     QAction *fitWidthAction;
201     QAction *fitPageAction;
202 
203     QActionGroup* zoomGroup;
204     QAction *zoomInAction;
205     QAction *zoomOutAction;
206 
207     QActionGroup* orientationGroup;
208     QAction *portraitAction;
209     QAction *landscapeAction;
210 
211     QActionGroup* modeGroup;
212     QAction *singleModeAction;
213     QAction *facingModeAction;
214     QAction *overviewModeAction;
215 
216     QActionGroup *printerGroup;
217     QAction *printAction;
218     QAction *pageSetupAction;
219 
220     QPointer<QObject> receiverToDisconnectOnClose;
221     QByteArray memberToDisconnectOnClose;
222 };
223 
init(QPrinter * _printer)224 void QPrintPreviewDialogPrivate::init(QPrinter *_printer)
225 {
226     Q_Q(QPrintPreviewDialog);
227 
228     initResources();
229 
230     if (_printer) {
231         preview = new QPrintPreviewWidget(_printer, q);
232         printer = _printer;
233     } else {
234         ownPrinter = true;
235         printer = new QPrinter;
236         preview = new QPrintPreviewWidget(printer, q);
237     }
238     QObject::connect(preview, SIGNAL(paintRequested(QPrinter*)), q, SIGNAL(paintRequested(QPrinter*)));
239     QObject::connect(preview, SIGNAL(previewChanged()), q, SLOT(_q_previewChanged()));
240     setupActions();
241 
242     pageNumEdit = new LineEdit;
243     pageNumEdit->setAlignment(Qt::AlignRight);
244     pageNumEdit->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
245     pageNumLabel = new QLabel;
246     QObject::connect(pageNumEdit, SIGNAL(editingFinished()), q, SLOT(_q_pageNumEdited()));
247 
248     zoomFactor = new QComboBox;
249     zoomFactor->setEditable(true);
250     zoomFactor->setMinimumContentsLength(7);
251     zoomFactor->setInsertPolicy(QComboBox::NoInsert);
252     LineEdit *zoomEditor = new LineEdit;
253     zoomEditor->setValidator(new ZoomFactorValidator(1, 1000, 1, zoomEditor));
254     zoomFactor->setLineEdit(zoomEditor);
255     static const short factorsX2[] = { 25, 50, 100, 200, 250, 300, 400, 800, 1600 };
256     for (auto factorX2 : factorsX2)
257         zoomFactor->addItem(QPrintPreviewDialog::tr("%1%").arg(factorX2 / 2.0));
258     QObject::connect(zoomFactor->lineEdit(), SIGNAL(editingFinished()),
259                      q, SLOT(_q_zoomFactorChanged()));
260     QObject::connect(zoomFactor, SIGNAL(currentIndexChanged(int)),
261                      q, SLOT(_q_zoomFactorChanged()));
262 
263     QPrintPreviewMainWindow *mw = new QPrintPreviewMainWindow(q);
264     QToolBar *toolbar = new QToolBar(mw);
265     toolbar->addAction(fitWidthAction);
266     toolbar->addAction(fitPageAction);
267     toolbar->addSeparator();
268     toolbar->addWidget(zoomFactor);
269     toolbar->addAction(zoomOutAction);
270     toolbar->addAction(zoomInAction);
271     toolbar->addSeparator();
272     toolbar->addAction(portraitAction);
273     toolbar->addAction(landscapeAction);
274     toolbar->addSeparator();
275     toolbar->addAction(firstPageAction);
276     toolbar->addAction(prevPageAction);
277 
278     // this is to ensure the label text and the editor text are
279     // aligned in all styles - the extra QVBoxLayout is a workaround
280     // for bug in QFormLayout
281     QWidget *pageEdit = new QWidget(toolbar);
282     QVBoxLayout *vboxLayout = new QVBoxLayout;
283     vboxLayout->setContentsMargins(0, 0, 0, 0);
284 #ifdef Q_OS_MAC
285     // We query the widgets about their size and then we fix the size.
286     // This should do the trick for the laying out part...
287     QSize pageNumEditSize, pageNumLabelSize;
288     pageNumEditSize = pageNumEdit->minimumSizeHint();
289     pageNumLabelSize = pageNumLabel->minimumSizeHint();
290     pageNumEdit->resize(pageNumEditSize);
291     pageNumLabel->resize(pageNumLabelSize);
292 #endif
293     QFormLayout *formLayout = new QFormLayout;
294 #ifdef Q_OS_MAC
295     // We have to change the growth policy in Mac.
296     formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
297 #endif
298     formLayout->setWidget(0, QFormLayout::LabelRole, pageNumEdit);
299     formLayout->setWidget(0, QFormLayout::FieldRole, pageNumLabel);
300     vboxLayout->addLayout(formLayout);
301     vboxLayout->setAlignment(Qt::AlignVCenter);
302     pageEdit->setLayout(vboxLayout);
303     toolbar->addWidget(pageEdit);
304 
305     toolbar->addAction(nextPageAction);
306     toolbar->addAction(lastPageAction);
307     toolbar->addSeparator();
308     toolbar->addAction(singleModeAction);
309     toolbar->addAction(facingModeAction);
310     toolbar->addAction(overviewModeAction);
311     toolbar->addSeparator();
312     toolbar->addAction(pageSetupAction);
313     toolbar->addAction(printAction);
314 
315     // Cannot use the actions' triggered signal here, since it doesn't autorepeat
316     QToolButton *zoomInButton = static_cast<QToolButton *>(toolbar->widgetForAction(zoomInAction));
317     QToolButton *zoomOutButton = static_cast<QToolButton *>(toolbar->widgetForAction(zoomOutAction));
318     zoomInButton->setAutoRepeat(true);
319     zoomInButton->setAutoRepeatInterval(200);
320     zoomInButton->setAutoRepeatDelay(200);
321     zoomOutButton->setAutoRepeat(true);
322     zoomOutButton->setAutoRepeatInterval(200);
323     zoomOutButton->setAutoRepeatDelay(200);
324     QObject::connect(zoomInButton, SIGNAL(clicked()), q, SLOT(_q_zoomIn()));
325     QObject::connect(zoomOutButton, SIGNAL(clicked()), q, SLOT(_q_zoomOut()));
326 
327     mw->addToolBar(toolbar);
328     mw->setCentralWidget(preview);
329     // QMainWindows are always created as top levels, force it to be a
330     // plain widget
331     mw->setParent(q, Qt::Widget);
332 
333     QVBoxLayout *topLayout = new QVBoxLayout;
334     topLayout->addWidget(mw);
335     topLayout->setContentsMargins(0, 0, 0, 0);
336     q->setLayout(topLayout);
337 
338     QString caption = QCoreApplication::translate("QPrintPreviewDialog", "Print Preview");
339     if (!printer->docName().isEmpty())
340         caption += QLatin1String(": ") + printer->docName();
341     q->setWindowTitle(caption);
342 
343     if (!printer->isValid()
344 #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
345         || printer->outputFormat() != QPrinter::NativeFormat
346 #endif
347         )
348         pageSetupAction->setEnabled(false);
349     preview->setFocus();
350 }
351 
qt_setupActionIcon(QAction * action,QLatin1String name)352 static inline void qt_setupActionIcon(QAction *action, QLatin1String name)
353 {
354     QLatin1String imagePrefix(":/qt-project.org/dialogs/qprintpreviewdialog/images/");
355     QIcon icon = QIcon::fromTheme(name);
356     icon.addFile(imagePrefix + name + QLatin1String("-24.png"), QSize(24, 24));
357     icon.addFile(imagePrefix + name + QLatin1String("-32.png"), QSize(32, 32));
358     action->setIcon(icon);
359 }
360 
setupActions()361 void QPrintPreviewDialogPrivate::setupActions()
362 {
363     Q_Q(QPrintPreviewDialog);
364 
365     // Navigation
366     navGroup = new QActionGroup(q);
367     navGroup->setExclusive(false);
368     nextPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Next page"));
369     prevPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Previous page"));
370     firstPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "First page"));
371     lastPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Last page"));
372     qt_setupActionIcon(nextPageAction, QLatin1String("go-next"));
373     qt_setupActionIcon(prevPageAction, QLatin1String("go-previous"));
374     qt_setupActionIcon(firstPageAction, QLatin1String("go-first"));
375     qt_setupActionIcon(lastPageAction, QLatin1String("go-last"));
376     QObject::connect(navGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_navigate(QAction*)));
377 
378 
379     fitGroup = new QActionGroup(q);
380     fitWidthAction = fitGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Fit width"));
381     fitPageAction = fitGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Fit page"));
382     fitWidthAction->setObjectName(QLatin1String("fitWidthAction"));
383     fitPageAction->setObjectName(QLatin1String("fitPageAction"));
384     fitWidthAction->setCheckable(true);
385     fitPageAction->setCheckable(true);
386     qt_setupActionIcon(fitWidthAction, QLatin1String("zoom-fit-width"));
387     qt_setupActionIcon(fitPageAction, QLatin1String("zoom-fit-page"));
388     QObject::connect(fitGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_fit(QAction*)));
389 
390     // Zoom
391     zoomGroup = new QActionGroup(q);
392     zoomInAction = zoomGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Zoom in"));
393     zoomOutAction = zoomGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Zoom out"));
394     qt_setupActionIcon(zoomInAction, QLatin1String("zoom-in"));
395     qt_setupActionIcon(zoomOutAction, QLatin1String("zoom-out"));
396 
397     // Portrait/Landscape
398     orientationGroup = new QActionGroup(q);
399     portraitAction = orientationGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Portrait"));
400     landscapeAction = orientationGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Landscape"));
401     portraitAction->setCheckable(true);
402     landscapeAction->setCheckable(true);
403     qt_setupActionIcon(portraitAction, QLatin1String("layout-portrait"));
404     qt_setupActionIcon(landscapeAction, QLatin1String("layout-landscape"));
405     QObject::connect(portraitAction, SIGNAL(triggered(bool)), preview, SLOT(setPortraitOrientation()));
406     QObject::connect(landscapeAction, SIGNAL(triggered(bool)), preview, SLOT(setLandscapeOrientation()));
407 
408     // Display mode
409     modeGroup = new QActionGroup(q);
410     singleModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show single page"));
411     facingModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show facing pages"));
412     overviewModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show overview of all pages"));
413     qt_setupActionIcon(singleModeAction, QLatin1String("view-pages-single"));
414     qt_setupActionIcon(facingModeAction, QLatin1String("view-pages-facing"));
415     qt_setupActionIcon(overviewModeAction, QLatin1String("view-pages-overview"));
416     singleModeAction->setObjectName(QLatin1String("singleModeAction"));
417     facingModeAction->setObjectName(QLatin1String("facingModeAction"));
418     overviewModeAction->setObjectName(QLatin1String("overviewModeAction"));
419 
420     singleModeAction->setCheckable(true);
421     facingModeAction->setCheckable(true);
422     overviewModeAction->setCheckable(true);
423     QObject::connect(modeGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_setMode(QAction*)));
424 
425     // Print
426     printerGroup = new QActionGroup(q);
427     printAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Print"));
428     pageSetupAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Page setup"));
429     qt_setupActionIcon(printAction, QLatin1String("printer"));
430     qt_setupActionIcon(pageSetupAction, QLatin1String("page-setup"));
431     QObject::connect(printAction, SIGNAL(triggered(bool)), q, SLOT(_q_print()));
432     QObject::connect(pageSetupAction, SIGNAL(triggered(bool)), q, SLOT(_q_pageSetup()));
433 
434     // Initial state:
435     fitPageAction->setChecked(true);
436     singleModeAction->setChecked(true);
437     if (preview->orientation() == QPrinter::Portrait)
438         portraitAction->setChecked(true);
439     else
440         landscapeAction->setChecked(true);
441 }
442 
443 
isFitting()444 bool QPrintPreviewDialogPrivate::isFitting()
445 {
446     return (fitGroup->isExclusive()
447             && (fitWidthAction->isChecked() || fitPageAction->isChecked()));
448 }
449 
450 
setFitting(bool on)451 void QPrintPreviewDialogPrivate::setFitting(bool on)
452 {
453     if (isFitting() == on)
454         return;
455     fitGroup->setExclusive(on);
456     if (on) {
457         QAction* action = fitWidthAction->isChecked() ? fitWidthAction : fitPageAction;
458         action->setChecked(true);
459         if (fitGroup->checkedAction() != action) {
460             // work around exclusitivity problem
461             fitGroup->removeAction(action);
462             fitGroup->addAction(action);
463         }
464     } else {
465         fitWidthAction->setChecked(false);
466         fitPageAction->setChecked(false);
467     }
468 }
469 
updateNavActions()470 void QPrintPreviewDialogPrivate::updateNavActions()
471 {
472     int curPage = preview->currentPage();
473     int numPages = preview->pageCount();
474     nextPageAction->setEnabled(curPage < numPages);
475     prevPageAction->setEnabled(curPage > 1);
476     firstPageAction->setEnabled(curPage > 1);
477     lastPageAction->setEnabled(curPage < numPages);
478     pageNumEdit->setText(QString::number(curPage));
479 }
480 
updatePageNumLabel()481 void QPrintPreviewDialogPrivate::updatePageNumLabel()
482 {
483     Q_Q(QPrintPreviewDialog);
484 
485     int numPages = preview->pageCount();
486     int maxChars = QString::number(numPages).length();
487     pageNumLabel->setText(QString::fromLatin1("/ %1").arg(numPages));
488     int cyphersWidth = q->fontMetrics().horizontalAdvance(QString().fill(QLatin1Char('8'), maxChars));
489     int maxWidth = pageNumEdit->minimumSizeHint().width() + cyphersWidth;
490     pageNumEdit->setMinimumWidth(maxWidth);
491     pageNumEdit->setMaximumWidth(maxWidth);
492     pageNumEdit->setValidator(new QIntValidator(1, numPages, pageNumEdit));
493     // any old one will be deleted later along with its parent pageNumEdit
494 }
495 
updateZoomFactor()496 void QPrintPreviewDialogPrivate::updateZoomFactor()
497 {
498     zoomFactor->lineEdit()->setText(QString::asprintf("%.1f%%", preview->zoomFactor()*100));
499 }
500 
_q_fit(QAction * action)501 void QPrintPreviewDialogPrivate::_q_fit(QAction* action)
502 {
503     setFitting(true);
504     if (action == fitPageAction)
505         preview->fitInView();
506     else
507         preview->fitToWidth();
508 }
509 
_q_zoomIn()510 void QPrintPreviewDialogPrivate::_q_zoomIn()
511 {
512     setFitting(false);
513     preview->zoomIn();
514     updateZoomFactor();
515 }
516 
_q_zoomOut()517 void QPrintPreviewDialogPrivate::_q_zoomOut()
518 {
519     setFitting(false);
520     preview->zoomOut();
521     updateZoomFactor();
522 }
523 
_q_pageNumEdited()524 void QPrintPreviewDialogPrivate::_q_pageNumEdited()
525 {
526     bool ok = false;
527     int res = pageNumEdit->text().toInt(&ok);
528     if (ok)
529         preview->setCurrentPage(res);
530 }
531 
_q_navigate(QAction * action)532 void QPrintPreviewDialogPrivate::_q_navigate(QAction* action)
533 {
534     int curPage = preview->currentPage();
535     if (action == prevPageAction)
536         preview->setCurrentPage(curPage - 1);
537     else if (action == nextPageAction)
538         preview->setCurrentPage(curPage + 1);
539     else if (action == firstPageAction)
540         preview->setCurrentPage(1);
541     else if (action == lastPageAction)
542         preview->setCurrentPage(preview->pageCount());
543     updateNavActions();
544 }
545 
_q_setMode(QAction * action)546 void QPrintPreviewDialogPrivate::_q_setMode(QAction* action)
547 {
548     if (action == overviewModeAction) {
549         preview->setViewMode(QPrintPreviewWidget::AllPagesView);
550         setFitting(false);
551         fitGroup->setEnabled(false);
552         navGroup->setEnabled(false);
553         pageNumEdit->setEnabled(false);
554         pageNumLabel->setEnabled(false);
555     } else if (action == facingModeAction) {
556         preview->setViewMode(QPrintPreviewWidget::FacingPagesView);
557     } else {
558         preview->setViewMode(QPrintPreviewWidget::SinglePageView);
559     }
560     if (action == facingModeAction || action == singleModeAction) {
561         fitGroup->setEnabled(true);
562         navGroup->setEnabled(true);
563         pageNumEdit->setEnabled(true);
564         pageNumLabel->setEnabled(true);
565         setFitting(true);
566     }
567 }
568 
_q_print()569 void QPrintPreviewDialogPrivate::_q_print()
570 {
571     Q_Q(QPrintPreviewDialog);
572 
573 #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
574     if (printer->outputFormat() != QPrinter::NativeFormat) {
575         QString title = QCoreApplication::translate("QPrintPreviewDialog", "Export to PDF");
576         QString suffix = QLatin1String(".pdf");
577         QString fileName;
578 #if QT_CONFIG(filedialog)
579         fileName = QFileDialog::getSaveFileName(q, title, printer->outputFileName(),
580                                                         QLatin1Char('*') + suffix);
581 #endif
582         if (!fileName.isEmpty()) {
583             if (QFileInfo(fileName).suffix().isEmpty())
584                 fileName.append(suffix);
585             printer->setOutputFileName(fileName);
586         }
587         if (!printer->outputFileName().isEmpty())
588             preview->print();
589         q->accept();
590         return;
591     }
592 #endif
593 
594     if (!printDialog)
595         printDialog = new QPrintDialog(printer, q);
596     if (printDialog->exec() == QDialog::Accepted) {
597         preview->print();
598         q->accept();
599     }
600 }
601 
_q_pageSetup()602 void QPrintPreviewDialogPrivate::_q_pageSetup()
603 {
604     Q_Q(QPrintPreviewDialog);
605 
606     if (!pageSetupDialog)
607         pageSetupDialog = new QPageSetupDialog(printer, q);
608 
609     if (pageSetupDialog->exec() == QDialog::Accepted) {
610         // update possible orientation changes
611         if (preview->orientation() == QPrinter::Portrait) {
612             portraitAction->setChecked(true);
613             preview->setPortraitOrientation();
614         }else {
615             landscapeAction->setChecked(true);
616             preview->setLandscapeOrientation();
617         }
618     }
619 }
620 
_q_previewChanged()621 void QPrintPreviewDialogPrivate::_q_previewChanged()
622 {
623     updateNavActions();
624     updatePageNumLabel();
625     updateZoomFactor();
626 }
627 
_q_zoomFactorChanged()628 void QPrintPreviewDialogPrivate::_q_zoomFactorChanged()
629 {
630     QString text = zoomFactor->lineEdit()->text();
631     bool ok;
632     qreal factor = text.remove(QLatin1Char('%')).toFloat(&ok);
633     factor = qMax(qreal(1.0), qMin(qreal(1000.0), factor));
634     if (ok) {
635         preview->setZoomFactor(factor/100.0);
636         zoomFactor->setEditText(QString::fromLatin1("%1%").arg(factor));
637         setFitting(false);
638     }
639 }
640 
641 ///////////////////////////////////////////////////////////////////////////
642 
643 /*!
644     \class QPrintPreviewDialog
645     \since 4.4
646 
647     \brief The QPrintPreviewDialog class provides a dialog for
648     previewing and configuring page layouts for printer output.
649 
650     \ingroup standard-dialogs
651     \ingroup printing
652     \inmodule QtPrintSupport
653 
654     Using QPrintPreviewDialog in your existing application is
655     straightforward:
656 
657     \list 1
658     \li Create the QPrintPreviewDialog.
659 
660     You can construct a QPrintPreviewDialog with an existing QPrinter
661     object, or you can have QPrintPreviewDialog create one for you,
662     which will be the system default printer.
663 
664     \li Connect the paintRequested() signal to a slot.
665 
666     When the dialog needs to generate a set of preview pages, the
667     paintRequested() signal will be emitted. You can use the exact
668     same code for the actual printing as for having the preview
669     generated, including calling QPrinter::newPage() to start a new
670     page in the preview. Connect a slot to the paintRequested()
671     signal, where you draw onto the QPrinter object that is passed
672     into the slot.
673 
674     \li Call exec().
675 
676     Call QPrintPreviewDialog::exec() to show the preview dialog.
677     \endlist
678 
679     \sa QPrinter, QPrintDialog, QPageSetupDialog, QPrintPreviewWidget
680 */
681 
682 /*!
683     Constructs a QPrintPreviewDialog based on \a printer and with \a
684     parent as the parent widget. The widget flags \a flags are passed on
685     to the QWidget constructor.
686 
687     \sa QWidget::setWindowFlags()
688 */
QPrintPreviewDialog(QPrinter * printer,QWidget * parent,Qt::WindowFlags flags)689 QPrintPreviewDialog::QPrintPreviewDialog(QPrinter* printer, QWidget *parent, Qt::WindowFlags flags)
690     : QDialog(*new QPrintPreviewDialogPrivate, parent, flags)
691 {
692     Q_D(QPrintPreviewDialog);
693     d->init(printer);
694 }
695 
696 /*!
697     \overload
698     \fn QPrintPreviewDialog::QPrintPreviewDialog(QWidget *parent, Qt::WindowFlags flags)
699 
700     This will create an internal QPrinter object, which will use the
701     system default printer.
702 */
QPrintPreviewDialog(QWidget * parent,Qt::WindowFlags f)703 QPrintPreviewDialog::QPrintPreviewDialog(QWidget *parent, Qt::WindowFlags f)
704     : QDialog(*new QPrintPreviewDialogPrivate, parent, f)
705 {
706     Q_D(QPrintPreviewDialog);
707     d->init();
708 }
709 
710 /*!
711     Destroys the QPrintPreviewDialog.
712 */
~QPrintPreviewDialog()713 QPrintPreviewDialog::~QPrintPreviewDialog()
714 {
715     Q_D(QPrintPreviewDialog);
716     if (d->ownPrinter)
717         delete d->printer;
718     delete d->printDialog;
719     delete d->pageSetupDialog;
720 }
721 
722 /*!
723     \reimp
724 */
setVisible(bool visible)725 void QPrintPreviewDialog::setVisible(bool visible)
726 {
727     Q_D(QPrintPreviewDialog);
728     // this will make the dialog get a decent default size
729     if (visible && !d->initialized) {
730         d->preview->updatePreview();
731         d->initialized = true;
732     }
733     QDialog::setVisible(visible);
734 }
735 
736 /*!
737     \reimp
738 */
done(int result)739 void QPrintPreviewDialog::done(int result)
740 {
741     Q_D(QPrintPreviewDialog);
742     QDialog::done(result);
743     if (d->receiverToDisconnectOnClose) {
744         disconnect(this, SIGNAL(finished(int)),
745                    d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
746         d->receiverToDisconnectOnClose = nullptr;
747     }
748     d->memberToDisconnectOnClose.clear();
749 }
750 
751 /*!
752     \overload
753     \since 4.5
754 
755     Opens the dialog and connects its finished(int) signal to the slot specified
756     by \a receiver and \a member.
757 
758     The signal will be disconnected from the slot when the dialog is closed.
759 */
open(QObject * receiver,const char * member)760 void QPrintPreviewDialog::open(QObject *receiver, const char *member)
761 {
762     Q_D(QPrintPreviewDialog);
763     // the int parameter isn't very useful here; we could just as well connect
764     // to reject(), but this feels less robust somehow
765     connect(this, SIGNAL(finished(int)), receiver, member);
766     d->receiverToDisconnectOnClose = receiver;
767     d->memberToDisconnectOnClose = member;
768     QDialog::open();
769 }
770 
771 /*!
772     Returns a pointer to the QPrinter object this dialog is currently
773     operating on.
774 */
printer()775 QPrinter *QPrintPreviewDialog::printer()
776 {
777     Q_D(QPrintPreviewDialog);
778     return d->printer;
779 }
780 
781 /*!
782     \fn void QPrintPreviewDialog::paintRequested(QPrinter *printer)
783 
784     This signal is emitted when the QPrintPreviewDialog needs to generate
785     a set of preview pages.
786 
787     The \a printer instance supplied is the paint device onto which you should
788     paint the contents of each page, using the QPrinter instance in the same way
789     as you would when printing directly.
790 */
791 
792 
793 QT_END_NAMESPACE
794 
795 #include "moc_qprintpreviewdialog.cpp"
796 #include "qprintpreviewdialog.moc"
797