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