Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qprintdialog_mac.mm
Go to the documentation of this file.
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 <AppKit/AppKit.h>
5
6#include "qprintdialog.h"
8
9#include <QtCore/qtemporarydir.h>
10#include <QtCore/private/qcore_mac_p.h>
11#include <QtWidgets/private/qapplication_p.h>
12#include <QtPrintSupport/qprinter.h>
13#include <QtPrintSupport/qprintengine.h>
14#include <qpa/qplatformprintdevice.h>
15
16#include <QtPrintSupport/private/qprintengine_mac_p.h>
17
19
20using namespace Qt::StringLiterals;
21
23
25{
26 Q_DECLARE_PUBLIC(QPrintDialog)
27
28public:
31
34
35 inline QPrintDialog *printDialog() { return q_func(); }
36
37 NSPrintInfo *printInfo;
38 NSPrintPanel *printPanel;
39};
40
42
44
45
46@class QT_MANGLE_NAMESPACE(QCocoaPrintPanelDelegate);
47
49@end
50
52 NSPrintInfo *printInfo;
53}
54
55- (instancetype)initWithNSPrintInfo:(NSPrintInfo *)nsPrintInfo
56{
57 if ((self = [self init])) {
58 printInfo = nsPrintInfo;
59 }
60 return self;
61}
62
63- (void)printPanelDidEnd:(NSPrintPanel *)printPanel
64 returnCode:(int)returnCode contextInfo:(void *)contextInfo
65{
66 Q_UNUSED(printPanel);
67
68 QPrintDialog *dialog = static_cast<QPrintDialog *>(contextInfo);
69 QPrinter *printer = dialog->printer();
70
71 if (returnCode == NSModalResponseOK) {
72 PMPrintSession session = static_cast<PMPrintSession>(printInfo.PMPrintSession);
73 PMPrintSettings settings = static_cast<PMPrintSettings>(printInfo.PMPrintSettings);
74
75 UInt32 frompage, topage;
76 PMGetFirstPage(settings, &frompage);
77 PMGetLastPage(settings, &topage);
78 topage = qMin(UInt32(INT_MAX), topage);
79 dialog->setFromTo(frompage, topage);
80
81 // OK, I need to map these values back let's see
82 // If from is 1 and to is INT_MAX, then print it all
83 // (Apologies to the folks with more than INT_MAX pages)
84 if (dialog->fromPage() == 1 && dialog->toPage() == INT_MAX) {
85 dialog->setPrintRange(QPrintDialog::AllPages);
86 printer->setPageRanges(QPageRanges());
87 } else {
88 dialog->setPrintRange(QPrintDialog::PageRange); // In a way a lie, but it shouldn't hurt.
89 // Carbon hands us back a very large number here even for ALL, set it to max
90 // in that case to follow the behavior of the other print dialogs.
91 if (dialog->maxPage() < dialog->toPage())
92 dialog->setFromTo(dialog->fromPage(), dialog->maxPage());
93 }
94
95 // Keep us in sync with chosen destination
96 PMDestinationType dest;
97 PMSessionGetDestinationType(session, settings, &dest);
98 if (dest == kPMDestinationFile) {
99 QCFType<CFURLRef> file;
100 PMSessionCopyDestinationLocation(session, settings, &file);
101 UInt8 localFile[2048]; // Assuming there's a POSIX file system here.
102 CFURLGetFileSystemRepresentation(file, true, localFile, sizeof(localFile));
103 auto outputFile = QFileInfo(QString::fromUtf8(reinterpret_cast<const char *>(localFile)));
104 if (outputFile.suffix() == "pdf"_L1)
105 printer->setOutputFileName(outputFile.absoluteFilePath());
106 else
107 qWarning() << "Can not print to file type" << outputFile.suffix();
108 } else if (dest == kPMDestinationPreview) {
109 static QTemporaryDir printPreviews;
110 auto documentName = printer->docName();
111 if (documentName.isEmpty())
113 auto fileName = printPreviews.filePath(QString("%1.pdf"_L1).arg(documentName));
114 printer->setOutputFileName(fileName);
115 // Ideally we would have a callback when the PDF engine is done writing
116 // to the file, and open Preview in response to that. Lacking that, we
117 // use the quick and dirty assumption that the the print operation will
118 // happen synchronously after the dialog is accepted, so we can defer
119 // the opening of the file to the next runloop pass.
120 dispatch_async(dispatch_get_main_queue(), ^{
121 [NSWorkspace.sharedWorkspace openURL:[NSURL fileURLWithPath:fileName.toNSString()]];
122 });
123 } else if (dest == kPMDestinationProcessPDF) {
124 qWarning("Printing workflows are not supported");
125 } else if (dest == kPMDestinationPrinter) {
126 PMPrinter macPrinter;
127 PMSessionGetCurrentPrinter(session, &macPrinter);
128 QString printerId = QString::fromCFString(PMPrinterGetID(macPrinter)).trimmed();
129 if (printer->printerName() != printerId)
130 printer->setPrinterName(printerId);
131 }
132 }
133
134 // Note this code should be in QCocoaPrintDevice, but that implementation is in the plugin,
135 // we need to move the dialog implementation into the plugin first to be able to access it.
136 // Need to tell QPrinter/QPageLayout if the page size or orientation has been changed
137 PMPageFormat pageFormat = static_cast<PMPageFormat>([printInfo PMPageFormat]);
138 PMPaper paper;
139 PMGetPageFormatPaper(pageFormat, &paper);
140 PMOrientation orientation;
141 PMGetOrientation(pageFormat, &orientation);
142 QPageSize pageSize;
143 CFStringRef key;
144 double width = 0;
145 double height = 0;
146 // If the PPD name is empty then is custom, for some reason PMPaperIsCustom doesn't work here
147 PMPaperGetPPDPaperName(paper, &key);
148 if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) {
149 QString ppdKey = QString::fromCFString(key);
150 if (ppdKey.isEmpty()) {
151 // Is using a custom page size as defined in the Print Dialog custom settings using mm or inches.
152 // We can't ask PMPaper what those units actually are, we can only get the point size which may return
153 // slightly wrong results due to rounding.
154 // Testing shows if using metric/mm then is rounded mm, if imperial/inch is rounded to 2 decimal places
155 // Even if we pass in our own custom size in mm with decimal places, the dialog will still round it!
156 // Suspect internal storage is in rounded mm?
157 if (QLocale().measurementSystem() == QLocale::MetricSystem) {
159 // Round to 0 decimal places
160 pageSize = QPageSize(sizef.toSize(), QPageSize::Millimeter);
161 } else {
163 const int w = qRound(width * 100 / multiplier);
164 const int h = qRound(height * 100 / multiplier);
165 pageSize = QPageSize(QSizeF(w / 100.0, h / 100.0), QPageSize::Inch);
166 }
167 } else {
169 }
170 }
171 if (pageSize.isValid() && !pageSize.isEquivalentTo(printer->pageLayout().pageSize()))
172 printer->setPageSize(pageSize);
173 printer->setPageOrientation(orientation == kPMLandscape ? QPageLayout::Landscape : QPageLayout::Portrait);
174
175 dialog->done((returnCode == NSModalResponseOK) ? QDialog::Accepted : QDialog::Rejected);
176}
177
178@end
179
181
183{
184 Q_Q(QPrintDialog);
185
186 if (printer->outputFormat() == QPrinter::NativeFormat) {
187 printInfo = static_cast<QMacPrintEngine *>(printer->printEngine())->printInfo();
188 [printInfo retain];
189 } else {
190 const QPageLayout pageLayout = printer->pageLayout();
191 // initialize the printInfo using the dictionary from the application-wide print info
192 const auto dictionary = [NSPrintInfo.sharedPrintInfo dictionary];
193 printInfo = [[NSPrintInfo alloc] initWithDictionary:dictionary];
194 printInfo.orientation = pageLayout.orientation() == QPageLayout::Landscape
195 ? NSPaperOrientationLandscape : NSPaperOrientationPortrait;
196 printInfo.paperSize = pageLayout.pageSize().size(QPageSize::Point).toCGSize();
197 }
198
199 // It seems the only way that PM lets you use all is if the minimum
200 // for the page range is 1. This _kind of_ makes sense if you think about
201 // it. However, calling PMSetFirstPage() or PMSetLastPage() always enforces
202 // the range.
203 // get print settings from the platform plugin
204 PMPrintSettings settings = static_cast<PMPrintSettings>([printInfo PMPrintSettings]);
205 PMSetPageRange(settings, q->minPage(), q->maxPage());
206 if (q->printRange() == QAbstractPrintDialog::PageRange) {
207 PMSetFirstPage(settings, q->fromPage(), false);
208 PMSetLastPage(settings, q->toPage(), false);
209 }
210 [printInfo updateFromPMPrintSettings];
211
212 QPrintDialog::PrintDialogOptions qtOptions = q->options();
213 NSPrintPanelOptions macOptions = NSPrintPanelShowsCopies;
214 if (qtOptions & QPrintDialog::PrintPageRange)
215 macOptions |= NSPrintPanelShowsPageRange;
216 if (qtOptions & QPrintDialog::PrintShowPageSize)
217 macOptions |= NSPrintPanelShowsPaperSize | NSPrintPanelShowsPageSetupAccessory
218 | NSPrintPanelShowsOrientation;
219
220 printPanel = [NSPrintPanel printPanel];
221 [printPanel retain];
222 [printPanel setOptions:macOptions];
223
224 // Call processEvents in case the event dispatcher has been interrupted, and needs to do
225 // cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
226 // close down during the cleanup (QTBUG-17913):
228
229 QT_MANGLE_NAMESPACE(QCocoaPrintPanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QCocoaPrintPanelDelegate) alloc] initWithNSPrintInfo:printInfo];
230 if (modality == Qt::ApplicationModal || !q->parentWidget()) {
231 if (modality == Qt::NonModal)
232 qWarning("QPrintDialog is required to be modal on OS X");
233
234 // Make sure we don't interrupt the runModalWithPrintInfo call.
235 (void) QMetaObject::invokeMethod(qApp->platformNativeInterface(),
236 "clearCurrentThreadCocoaEventDispatcherInterruptFlag");
237
238 int rval = [printPanel runModalWithPrintInfo:printInfo];
239 [delegate printPanelDidEnd:printPanel returnCode:rval contextInfo:q];
240 } else {
241 Q_ASSERT(q->window());
242 QWindow *parentWindow = q->window()->windowHandle();
243 NSWindow *window = static_cast<NSWindow *>(qApp->platformNativeInterface()->nativeResourceForWindow("nswindow", parentWindow));
244 [printPanel beginSheetWithPrintInfo:printInfo
245 modalForWindow:window
246 delegate:delegate
247 didEndSelector:@selector(printPanelDidEnd:returnCode:contextInfo:)
248 contextInfo:q];
249 }
250}
251
253{
254 [printInfo release];
255 printInfo = 0;
256 [printPanel release];
257 printPanel = 0;
258}
259
265
271
276
278{
279 Q_D(QPrintDialog);
280
282
284 d->openCocoaPrintPanel(Qt::ApplicationModal);
285 d->closeCocoaPrintPanel();
286
287 QDialog::setVisible(false);
288
289 return result();
290}
291
292
296void QPrintDialog::setVisible(bool visible)
297{
298 Q_D(QPrintDialog);
299
300 bool isCurrentlyVisible = (d->printPanel != 0);
301
302 if (!visible == !isCurrentlyVisible)
303 return;
304
305 if (d->printer->outputFormat() != QPrinter::NativeFormat)
306 return;
307
309
310 if (visible) {
312 if (modality == Qt::NonModal) {
313 // NSPrintPanels can only be modal, so we must pick a type
315 }
316 d->openCocoaPrintPanel(modality);
317 return;
318 } else {
319 if (d->printPanel) {
320 d->closeCocoaPrintPanel();
321 return;
322 }
323 }
324}
325
327
328#include "moc_qprintdialog.cpp"
The QAbstractPrintDialog class provides a base implementation for print dialogs used to configure pri...
The QDialog class is the base class of dialog windows.
Definition qdialog.h:19
int result() const
In general returns the modal dialog's result code, Accepted or Rejected.
Definition qdialog.cpp:475
@ Accepted
Definition qdialog.h:30
void setVisible(bool visible) override
\reimp
Definition qdialog.cpp:744
@ ExcludeSocketNotifiers
Definition qeventloop.h:28
@ ExcludeUserInputEvents
Definition qeventloop.h:27
void done(int result) override
\reimp
QString applicationDisplayName
the user-visible name of this application
@ MetricSystem
Definition qlocale.h:868
\inmodule QtGui
Definition qpagelayout.h:20
Unit
This enum type is used to specify the measurement unit for page layout and margins.
Definition qpagelayout.h:24
Orientation orientation() const
Returns the page orientation of the page layout.
QPageSize pageSize() const
Returns the page size of the page layout.
The QPageRanges class represents a collection of page ranges.
Definition qpageranges.h:21
\inmodule QtGui
Definition qpagesize.h:22
bool isValid() const
Returns true if this page size is valid.
QSizeF size(Unit units) const
Returns the size of the page in the required units.
bool isEquivalentTo(const QPageSize &other) const
Returns true if this page is equivalent to the other page, i.e.
virtual bool setPageOrientation(QPageLayout::Orientation orientation)
virtual bool setPageSize(const QPageSize &pageSize)
QPageLayout pageLayout() const
virtual void setPageRanges(const QPageRanges &ranges)
static QPageSize createPageSize(const QString &key, const QSize &size, const QString &localizedName)
QPrintDialog * printDialog()
NSPrintPanel * printPanel
void openCocoaPrintPanel(Qt::WindowModality modality)
The QPrintDialog class provides a dialog for specifying the printer's configuration.
int exec() override
\reimp
~QPrintDialog()
Destroys the print dialog.
QPrintDialog(QPrinter *printer, QWidget *parent=nullptr)
Constructs a new modal printer dialog for the given printer with the given parent.
\reentrant
Definition qprinter.h:28
QPrintEngine * printEngine() const
@ NativeFormat
Definition qprinter.h:69
QString docName() const
Returns the document name.
Definition qprinter.cpp:772
void setOutputFileName(const QString &)
Sets the name of the output file to fileName.
Definition qprinter.cpp:717
QString printerName() const
Returns the printer name.
Definition qprinter.cpp:621
OutputFormat outputFormat() const
Definition qprinter.cpp:570
void setPrinterName(const QString &)
Sets the printer name to name.
Definition qprinter.cpp:638
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString trimmed() const &
Definition qstring.h:447
The QWidget class is the base class of all user interface objects.
Definition qwidget.h:99
void setAttribute(Qt::WidgetAttribute, bool on=true)
Sets the attribute attribute on this widget if on is true; otherwise clears the attribute.
Qt::WindowModality windowModality
which windows are blocked by the modal widget
Definition qwidget.h:104
void hide()
Hides the widget.
Definition qwidget.cpp:8135
QWidget * parentWidget() const
Returns the parent of this widget, or \nullptr if it does not have any parent widget.
Definition qwidget.h:904
bool visible
whether the widget is visible
Definition qwidget.h:144
\inmodule QtGui
Definition qwindow.h:63
Combined button and popup list for selecting options.
@ WA_DontShowOnScreen
Definition qnamespace.h:383
WindowModality
@ NonModal
@ WindowModal
@ ApplicationModal
QString self
Definition language.cpp:58
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
static QString outputFile
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLint GLsizei width
GLfloat GLfloat GLfloat GLfloat h
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
qreal qt_pointMultiplier(QPageLayout::Unit unit)
NSPrintInfo * printInfo
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
#define QT_MANGLE_NAMESPACE(name)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
QFuture< QSet< QString > > dictionary
QFile file
[0]
QSettings settings("MySoft", "Star Runner")
[0]
QFileDialog dialog(this)
[1]
aWidget window() -> setWindowTitle("New Window Title")
[2]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...