Qt 6.x
The Qt SDK
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:
30 {}
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) {
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->parentWidget());
242 QWindow *parentWindow = q->parentWidget()->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
262{
264}
265
268{
270}
271
273{
274}
275
277{
278 Q_D(QPrintDialog);
279
281
283 d->openCocoaPrintPanel(Qt::ApplicationModal);
284 d->closeCocoaPrintPanel();
285
286 QDialog::setVisible(false);
287
288 return result();
289}
290
291
295void QPrintDialog::setVisible(bool visible)
296{
297 Q_D(QPrintDialog);
298
299 bool isCurrentlyVisible = (d->printPanel != 0);
300
301 if (!visible == !isCurrentlyVisible)
302 return;
303
304 if (d->printer->outputFormat() != QPrinter::NativeFormat)
305 return;
306
308
309 if (visible) {
311 if (modality == Qt::NonModal) {
312 // NSPrintPanels can only be modal, so we must pick a type
314 }
315 d->openCocoaPrintPanel(modality);
316 return;
317 } else {
318 if (d->printPanel) {
319 d->closeCocoaPrintPanel();
320 return;
321 }
322 }
323}
324
326
327#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:471
@ Accepted
Definition qdialog.h:30
@ ExcludeSocketNotifiers
Definition qeventloop.h:28
@ ExcludeUserInputEvents
Definition qeventloop.h:27
void done(int result) override
\reimp
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString applicationDisplayName
the user-visible name of this application
@ MetricSystem
Definition qlocale.h:858
\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:767
void setOutputFileName(const QString &)
Sets the name of the output file to fileName.
Definition qprinter.cpp:712
QString printerName() const
Returns the printer name.
Definition qprinter.cpp:616
OutputFormat outputFormat() const
Definition qprinter.cpp:565
void setPrinterName(const QString &)
Sets the printer name to name.
Definition qprinter.cpp:633
\inmodule QtCore
Definition qsize.h:207
constexpr QSize toSize() const noexcept
Returns an integer based copy of this size.
Definition qsize.h:390
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString trimmed() const &
Definition qstring.h:380
\inmodule QtCore \reentrant
QString filePath(const QString &fileName) const
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
virtual void setVisible(bool visible)
Definition qwidget.cpp:8329
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:382
WindowModality
@ NonModal
@ WindowModal
@ ApplicationModal
QString self
Definition language.cpp:57
#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:281
#define qWarning
Definition qlogging.h:162
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:92
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...
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent