/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qplatformdefs.h"
#include <QtPrintSupport/private/qtprintsupportglobal_p.h>

#include "private/qabstractprintdialog_p.h"
#if QT_CONFIG(messagebox)
#include <QtWidgets/qmessagebox.h>
#endif
#include "qprintdialog.h"
#if QT_CONFIG(filedialog)
#include "qfiledialog.h"
#endif
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
#include <QtCore/qglobal.h>
#include <QtCore/qtextcodec.h>
#include <QtGui/qevent.h>
#if QT_CONFIG(filesystemmodel)
#include <QtWidgets/qfilesystemmodel.h>
#endif
#include <QtWidgets/qstyleditemdelegate.h>
#include <QtWidgets/qformlayout.h>
#include <QtPrintSupport/qprinter.h>

#include <qpa/qplatformprintplugin.h>
#include <qpa/qplatformprintersupport.h>

#include <private/qprintdevice_p.h>

#include <QtWidgets/qdialogbuttonbox.h>

#if QT_CONFIG(completer)
#include <private/qcompleter_p.h>
#endif
#include "ui_qprintpropertieswidget.h"
#include "ui_qprintsettingsoutput.h"
#include "ui_qprintwidget.h"

#if QT_CONFIG(cups)
Q_DECLARE_METATYPE(const ppd_option_t *)
#include <private/qcups_p.h>
#if QT_CONFIG(cupsjobwidget)
#include "qcupsjobwidget_p.h"
#endif
#endif

/*

Print dialog class declarations

    QPrintDialog:            The main Print Dialog, nothing really held here.

    QUnixPrintWidget:
    QUnixPrintWidgetPrivate: The real Unix Print Dialog implementation.

                             Directly includes the upper half of the Print Dialog
                             containing the Printer Selection widgets and
                             Properties button.

                             Embeds the Properties pop-up dialog from
                             QPrintPropertiesDialog

                             Embeds the lower half from separate widget class
                             QPrintDialogPrivate

                             Layout in qprintwidget.ui

    QPrintDialogPrivate:     The lower half of the Print Dialog containing the
                             Copies and Options tabs that expands when the
                             Options button is selected.

                             Layout in qprintsettingsoutput.ui

    QPrintPropertiesDialog:  Dialog displayed when clicking on Properties button to
                             allow editing of Page and Advanced tabs.

                             Layout in qprintpropertieswidget.ui
*/

static void initResources()
{
    Q_INIT_RESOURCE(qprintdialog);
}

QT_BEGIN_NAMESPACE

class QPrintPropertiesDialog : public QDialog
{
    Q_OBJECT
public:
    QPrintPropertiesDialog(QPrinter *printer, QPrintDevice *currentPrintDevice,
                           QPrinter::OutputFormat outputFormat, const QString &printerName,
                           QAbstractPrintDialog *parent);
    ~QPrintPropertiesDialog();

    void setupPrinter() const;

private slots:
    void reject() override;
    void accept() override;

private:
    void showEvent(QShowEvent *event) override;

    friend class QUnixPrintWidgetPrivate;
#if QT_CONFIG(cups)
    QPrinter *m_printer;
#endif
    Ui::QPrintPropertiesWidget widget;
    QDialogButtonBox *m_buttons;
#if QT_CONFIG(cupsjobwidget)
    QCupsJobWidget *m_jobOptions;
#endif

#if QT_CONFIG(cups)
    bool createAdvancedOptionsWidget();
    void setPrinterAdvancedCupsOptions() const;
    void revertAdvancedOptionsToSavedValues() const;
    void advancedOptionsUpdateSavedValues() const;
    bool anyPpdOptionConflict() const;
    bool anyAdvancedOptionConflict() const;

    QPrintDevice *m_currentPrintDevice;
    QTextCodec *m_cupsCodec = nullptr;
    QVector<QComboBox*> m_advancedOptionsCombos;
#endif
};

class QUnixPrintWidgetPrivate;

class QUnixPrintWidget : public QWidget
{
    Q_OBJECT

public:
    explicit QUnixPrintWidget(QPrinter *printer, QWidget *parent = nullptr);
    ~QUnixPrintWidget();
    void updatePrinter();

private:
    friend class QPrintDialog;
    friend class QPrintDialogPrivate;
    friend class QUnixPrintWidgetPrivate;
    QUnixPrintWidgetPrivate *d;
    Q_PRIVATE_SLOT(d, void _q_printerChanged(int))
    Q_PRIVATE_SLOT(d, void _q_btnBrowseClicked())
    Q_PRIVATE_SLOT(d, void _q_btnPropertiesClicked())
};

class QUnixPrintWidgetPrivate
{
public:
    QUnixPrintWidgetPrivate(QUnixPrintWidget *q, QPrinter *prn);
    ~QUnixPrintWidgetPrivate();

    bool checkFields();
    void setupPrinter();
    void setOptionsPane(QPrintDialogPrivate *pane);
    void setupPrinterProperties();
// slots
    void _q_printerChanged(int index);
    void _q_btnPropertiesClicked();
    void _q_btnBrowseClicked();

    QUnixPrintWidget * const parent;
    QPrintPropertiesDialog *propertiesDialog;
    Ui::QPrintWidget widget;
    QAbstractPrintDialog * q;
    QPrinter *printer;
    QPrintDevice m_currentPrintDevice;

    void updateWidget();

#if QT_CONFIG(cups)
    void setPpdDuplex(QPrinter::DuplexMode mode);
    ppd_option_t *m_duplexPpdOption;
#endif

private:
    QPrintDialogPrivate *optionsPane;
    bool filePrintersAdded;
};

class QPrintDialogPrivate : public QAbstractPrintDialogPrivate
{
    Q_DECLARE_PUBLIC(QPrintDialog)
    Q_DECLARE_TR_FUNCTIONS(QPrintDialog)
public:
    QPrintDialogPrivate();
    ~QPrintDialogPrivate();

    void init();

    void selectPrinter(const QPrinter::OutputFormat outputFormat);

    void _q_togglePageSetCombo(bool);
#if QT_CONFIG(messagebox)
    void _q_checkFields();
#endif
    void _q_collapseOrExpandDialog();

#if QT_CONFIG(cups)
    void updatePpdDuplexOption(QRadioButton *radio);
#endif
    void setupPrinter();
    void updateWidgets();

    virtual void setTabs(const QList<QWidget*> &tabs) override;

    Ui::QPrintSettingsOutput options;
    QUnixPrintWidget *top;
    QWidget *bottom;
    QDialogButtonBox *buttons;
    QPushButton *collapseButton;
    QPrinter::OutputFormat printerOutputFormat;
private:
    void setExplicitDuplexMode(QPrint::DuplexMode duplexMode);
    // duplex mode explicitly set by user, QPrint::DuplexAuto otherwise
    QPrint::DuplexMode explicitDuplexMode;
};

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*

    QPrintPropertiesDialog

    Dialog displayed when clicking on Properties button to allow editing of Page
    and Advanced tabs.

*/

QPrintPropertiesDialog::QPrintPropertiesDialog(QPrinter *printer, QPrintDevice *currentPrintDevice,
                                               QPrinter::OutputFormat outputFormat, const QString &printerName,
                                               QAbstractPrintDialog *parent)
    : QDialog(parent)
#if QT_CONFIG(cups)
    , m_printer(printer)
#endif
{
    setWindowTitle(tr("Printer Properties"));
    QVBoxLayout *lay = new QVBoxLayout(this);
    QWidget *content = new QWidget(this);
    widget.setupUi(content);
    m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
    lay->addWidget(content);
    lay->addWidget(m_buttons);

    connect(m_buttons->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &QPrintPropertiesDialog::accept);
    connect(m_buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &QPrintPropertiesDialog::reject);

    widget.pageSetup->setPrinter(printer, currentPrintDevice, outputFormat, printerName);

#if QT_CONFIG(cupsjobwidget)
    m_jobOptions = new QCupsJobWidget(printer, currentPrintDevice);
    widget.tabs->insertTab(1, m_jobOptions, tr("Job Options"));
#endif

    const int advancedTabIndex = widget.tabs->indexOf(widget.cupsPropertiesPage);
#if QT_CONFIG(cups)
    m_currentPrintDevice = currentPrintDevice;
    const bool anyWidgetCreated = createAdvancedOptionsWidget();

    widget.tabs->setTabEnabled(advancedTabIndex, anyWidgetCreated);

    connect(widget.pageSetup, &QPageSetupWidget::ppdOptionChanged, this, [this] {
        widget.conflictsLabel->setVisible(anyPpdOptionConflict());
    });

#else
    Q_UNUSED(currentPrintDevice)
    widget.tabs->setTabEnabled(advancedTabIndex, false);
#endif
}

QPrintPropertiesDialog::~QPrintPropertiesDialog()
{
}

void QPrintPropertiesDialog::setupPrinter() const
{
#if QT_CONFIG(cups)
    QCUPSSupport::clearCupsOptions(m_printer);
#endif

    widget.pageSetup->setupPrinter();
#if QT_CONFIG(cupsjobwidget)
    m_jobOptions->setupPrinter();
#endif

#if QT_CONFIG(cups)
    // Set Color by default, that will change if the "ColorModel" property is available
    m_printer->setColorMode(QPrinter::Color);

    setPrinterAdvancedCupsOptions();
#endif
}

void QPrintPropertiesDialog::reject()
{
    widget.pageSetup->revertToSavedValues();

#if QT_CONFIG(cupsjobwidget)
    m_jobOptions->revertToSavedValues();
#endif

#if QT_CONFIG(cups)
    revertAdvancedOptionsToSavedValues();
#endif
    QDialog::reject();
}

void QPrintPropertiesDialog::accept()
{
#if QT_CONFIG(cups)
    if (widget.pageSetup->hasPpdConflict()) {
        widget.tabs->setCurrentWidget(widget.tabPage);
        const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Page Setup Conflicts"),
                                                                        tr("There are conflicts in page setup options. Do you want to fix them?"),
                                                                        QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
        if (answer != QMessageBox::No)
            return;
    } else if (anyAdvancedOptionConflict()) {
        widget.tabs->setCurrentWidget(widget.cupsPropertiesPage);
        const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Advanced Option Conflicts"),
                                                                        tr("There are conflicts in some advanced options. Do you want to fix them?"),
                                                                        QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
        if (answer != QMessageBox::No)
            return;
    }
    advancedOptionsUpdateSavedValues();
#endif

#if QT_CONFIG(cupsjobwidget)
    m_jobOptions->updateSavedValues();
#endif

    widget.pageSetup->updateSavedValues();

    QDialog::accept();
}

void QPrintPropertiesDialog::showEvent(QShowEvent *event)
{
#if QT_CONFIG(cups)
    widget.conflictsLabel->setVisible(anyPpdOptionConflict());
#endif
    QDialog::showEvent(event);
}

#if QT_CONFIG(cups)

// Used to store the ppd_option_t for each QComboBox that represents an advanced option
static const char *ppdOptionProperty = "_q_ppd_option";

// Used to store the originally selected choice index for each QComboBox that represents an advanced option
static const char *ppdOriginallySelectedChoiceProperty = "_q_ppd_originally_selected_choice";

// Used to store the warning label pointer for each QComboBox that represents an advanced option
static const char *warningLabelProperty = "_q_warning_label";

static bool isBlacklistedGroup(const ppd_group_t *group) Q_DECL_NOTHROW
{
    return qstrcmp(group->name, "InstallableOptions") == 0;
};

static bool isBlacklistedOption(const char *keyword) Q_DECL_NOTHROW
{
    // We already let the user set these options elsewhere
    const char *cupsOptionBlacklist[] = {
        "Collate",
        "Copies",
        "OutputOrder",
        "PageRegion",
        "PageSize",
        "Duplex" // handled by the main dialog
    };
    auto equals = [](const char *keyword) {
        return [keyword](const char *candidate) {
            return qstrcmp(keyword, candidate) == 0;
        };
    };
    return std::any_of(std::begin(cupsOptionBlacklist), std::end(cupsOptionBlacklist), equals(keyword));
};

bool QPrintPropertiesDialog::createAdvancedOptionsWidget()
{
    bool anyWidgetCreated = false;

    ppd_file_t *ppd = m_currentPrintDevice->property(PDPK_PpdFile).value<ppd_file_t*>();

    if (ppd) {
        m_cupsCodec = QTextCodec::codecForName(ppd->lang_encoding);

        QWidget *holdingWidget = new QWidget();
        QVBoxLayout *layout = new QVBoxLayout(holdingWidget);

        for (int i = 0; i < ppd->num_groups; ++i) {
            const ppd_group_t *group = &ppd->groups[i];

            if (!isBlacklistedGroup(group)) {
                QFormLayout *groupLayout = new QFormLayout();

                for (int i = 0; i < group->num_options; ++i) {
                    const ppd_option_t *option = &group->options[i];

                    if (!isBlacklistedOption(option->keyword)) {
                        QComboBox *choicesCb = new QComboBox();

                        const auto setPpdOptionFromCombo = [this, choicesCb, option] {
                            // We can't use choicesCb->currentIndex() to know the index of the option in the choices[] array
                            // because some of them may not be present in the list because they conflict with the
                            // installable options so use the index passed on addItem
                            const int selectedChoiceIndex = choicesCb->currentData().toInt();
                            const auto values = QStringList{} << QString::fromLatin1(option->keyword)
                                                                << QString::fromLatin1(option->choices[selectedChoiceIndex].choice);
                            m_currentPrintDevice->setProperty(PDPK_PpdOption, values);
                            widget.conflictsLabel->setVisible(anyPpdOptionConflict());
                        };

                        bool foundMarkedChoice = false;
                        bool markedChoiceNotAvailable = false;
                        for (int i = 0; i < option->num_choices; ++i) {
                            const ppd_choice_t *choice = &option->choices[i];
                            const auto values = QStringList{} << QString::fromLatin1(option->keyword) << QString::fromLatin1(choice->choice);
                            const bool choiceIsInstallableConflict = m_currentPrintDevice->isFeatureAvailable(PDPK_PpdChoiceIsInstallableConflict, values);
                            if (choiceIsInstallableConflict && static_cast<int>(choice->marked) == 1) {
                                markedChoiceNotAvailable = true;
                            } else if (!choiceIsInstallableConflict) {
                                choicesCb->addItem(m_cupsCodec->toUnicode(choice->text), i);
                                if (static_cast<int>(choice->marked) == 1) {
                                    choicesCb->setCurrentIndex(choicesCb->count() - 1);
                                    choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, QVariant(i));
                                    foundMarkedChoice = true;
                                } else if (!foundMarkedChoice && qstrcmp(choice->choice, option->defchoice) == 0) {
                                    choicesCb->setCurrentIndex(choicesCb->count() - 1);
                                    choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, QVariant(i));
                                }
                            }
                        }

                        if (markedChoiceNotAvailable) {
                            // If the user default option is not available because of it conflicting with
                            // the installed options, we need to set the internal ppd value to the value
                            // being shown in the combo
                            setPpdOptionFromCombo();
                        }

                        if (choicesCb->count() > 1) {

                            connect(choicesCb, QOverload<int>::of(&QComboBox::currentIndexChanged), this, setPpdOptionFromCombo);

                            // We need an extra label at the end to show the conflict warning
                            QWidget *choicesCbWithLabel = new QWidget();
                            QHBoxLayout *choicesCbWithLabelLayout = new QHBoxLayout(choicesCbWithLabel);
                            choicesCbWithLabelLayout->setContentsMargins(0, 0, 0, 0);
                            QLabel *warningLabel = new QLabel();
                            choicesCbWithLabelLayout->addWidget(choicesCb);
                            choicesCbWithLabelLayout->addWidget(warningLabel);

                            QLabel *optionLabel = new QLabel(m_cupsCodec->toUnicode(option->text));
                            groupLayout->addRow(optionLabel, choicesCbWithLabel);
                            anyWidgetCreated = true;
                            choicesCb->setProperty(ppdOptionProperty, QVariant::fromValue(option));
                            choicesCb->setProperty(warningLabelProperty, QVariant::fromValue(warningLabel));
                            m_advancedOptionsCombos << choicesCb;
                        } else {
                            delete choicesCb;
                        }
                    }
                }

                if (groupLayout->rowCount() > 0) {
                    QGroupBox *groupBox = new QGroupBox(m_cupsCodec->toUnicode(group->text));
                    groupBox->setLayout(groupLayout);
                    layout->addWidget(groupBox);
                } else {
                    delete groupLayout;
                }
            }
        }

        layout->addStretch();
        widget.scrollArea->setWidget(holdingWidget);
    }

    if (!m_cupsCodec)
        m_cupsCodec = QTextCodec::codecForLocale();

    return anyWidgetCreated;
}

void QPrintPropertiesDialog::setPrinterAdvancedCupsOptions() const
{
    for (const QComboBox *choicesCb : m_advancedOptionsCombos) {
        const ppd_option_t *option = choicesCb->property(ppdOptionProperty).value<const ppd_option_t *>();

        // We can't use choicesCb->currentIndex() to know the index of the option in the choices[] array
        // because some of them may not be present in the list because they conflict with the
        // installable options so use the index passed on addItem
        const int selectedChoiceIndex = choicesCb->currentData().toInt();
        const char *selectedChoice = option->choices[selectedChoiceIndex].choice;

        if (qstrcmp(option->keyword, "ColorModel") == 0)
            m_printer->setColorMode(qstrcmp(selectedChoice, "Gray") == 0 ? QPrinter::GrayScale : QPrinter::Color);

        if (qstrcmp(option->defchoice, selectedChoice) != 0)
            QCUPSSupport::setCupsOption(m_printer, QString::fromLatin1(option->keyword), QString::fromLatin1(selectedChoice));
    }
}

void QPrintPropertiesDialog::revertAdvancedOptionsToSavedValues() const
{
    for (QComboBox *choicesCb : m_advancedOptionsCombos) {
        const int originallySelectedChoice = choicesCb->property(ppdOriginallySelectedChoiceProperty).value<int>();
        const int newComboIndexToSelect = choicesCb->findData(originallySelectedChoice);
        choicesCb->setCurrentIndex(newComboIndexToSelect);
        // The currentIndexChanged lambda takes care of resetting the ppd option
    }
    widget.conflictsLabel->setVisible(anyPpdOptionConflict());
}

void QPrintPropertiesDialog::advancedOptionsUpdateSavedValues() const
{
    for (QComboBox *choicesCb : m_advancedOptionsCombos)
        choicesCb->setProperty(ppdOriginallySelectedChoiceProperty, choicesCb->currentData());
}

bool QPrintPropertiesDialog::anyPpdOptionConflict() const
{
    // we need to execute both since besides returning true/false they update the warning icons
    const bool pageSetupConflicts = widget.pageSetup->hasPpdConflict();
    const bool advancedOptionConflicts = anyAdvancedOptionConflict();
    return pageSetupConflicts || advancedOptionConflicts;
}

bool QPrintPropertiesDialog::anyAdvancedOptionConflict() const
{
    const QIcon warning = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, nullptr);

    bool anyConflicted = false;

    for (const QComboBox *choicesCb : m_advancedOptionsCombos) {
        const ppd_option_t *option = choicesCb->property(ppdOptionProperty).value<const ppd_option_t *>();
        QLabel *warningLabel = choicesCb->property(warningLabelProperty).value<QLabel *>();
        if (option->conflicted) {
            anyConflicted = true;
            const int pixmap_size = choicesCb->sizeHint().height() * .75;
            warningLabel->setPixmap(warning.pixmap(pixmap_size, pixmap_size));
        } else {
            warningLabel->setPixmap(QPixmap());
        }
    }

    return anyConflicted;
}

#endif


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*

    QPrintDialogPrivate

    The lower half of the Print Dialog containing the Copies and Options
    tabs that expands when the Options button is selected.

*/
QPrintDialogPrivate::QPrintDialogPrivate()
    : top(nullptr), bottom(nullptr), buttons(nullptr), collapseButton(nullptr),
      explicitDuplexMode(QPrint::DuplexAuto)
{
    initResources();
}

QPrintDialogPrivate::~QPrintDialogPrivate()
{
}

void QPrintDialogPrivate::init()
{
    Q_Q(QPrintDialog);

    top = new QUnixPrintWidget(q->printer(), q);
    bottom = new QWidget(q);
    options.setupUi(bottom);
    options.color->setIconSize(QSize(32, 32));
    options.color->setIcon(QIcon(QLatin1String(":/qt-project.org/dialogs/qprintdialog/images/status-color.png")));
    options.grayscale->setIconSize(QSize(32, 32));
    options.grayscale->setIcon(QIcon(QLatin1String(":/qt-project.org/dialogs/qprintdialog/images/status-gray-scale.png")));

#if QT_CONFIG(cups)
    // Add Page Set widget if CUPS is available
    options.pageSetCombo->addItem(tr("All Pages"), QVariant::fromValue(QCUPSSupport::AllPages));
    options.pageSetCombo->addItem(tr("Odd Pages"), QVariant::fromValue(QCUPSSupport::OddPages));
    options.pageSetCombo->addItem(tr("Even Pages"), QVariant::fromValue(QCUPSSupport::EvenPages));
#else
    for (int i = options.pagesLayout->count() - 1; i >= 0; --i)
        delete options.pagesLayout->itemAt(i)->widget();
#endif

    top->d->setOptionsPane(this);

    buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, q);
    collapseButton = new QPushButton(QPrintDialog::tr("&Options >>"), buttons);
    buttons->addButton(collapseButton, QDialogButtonBox::ResetRole);
    bottom->setVisible(false);

    QPushButton *printButton = buttons->button(QDialogButtonBox::Ok);
    printButton->setText(QPrintDialog::tr("&Print"));
    printButton->setDefault(true);

    QVBoxLayout *lay = new QVBoxLayout(q);
    lay->addWidget(top);
    lay->addWidget(bottom);
    lay->addWidget(buttons);

#if !QT_CONFIG(messagebox)
    QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(accept()));
#else
    QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(_q_checkFields()));
#endif
    QObject::connect(buttons, SIGNAL(rejected()), q, SLOT(reject()));

    QObject::connect(options.printSelection, SIGNAL(toggled(bool)),
                     q, SLOT(_q_togglePageSetCombo(bool)));

    QObject::connect(options.printCurrentPage, SIGNAL(toggled(bool)),
                     q, SLOT(_q_togglePageSetCombo(bool)));

    QObject::connect(collapseButton, SIGNAL(released()), q, SLOT(_q_collapseOrExpandDialog()));

    QObject::connect(options.noDuplex, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexNone); });
    QObject::connect(options.duplexLong, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexLongSide); });
    QObject::connect(options.duplexShort, &QAbstractButton::clicked, q, [this] { setExplicitDuplexMode(QPrint::DuplexShortSide); });

#if QT_CONFIG(cups)
    QObject::connect(options.noDuplex, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.noDuplex); });
    QObject::connect(options.duplexLong, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.duplexLong); });
    QObject::connect(options.duplexShort, &QAbstractButton::toggled, q, [this] { updatePpdDuplexOption(options.duplexShort); });
#endif
}

// initialize printer options
void QPrintDialogPrivate::selectPrinter(const QPrinter::OutputFormat outputFormat)
{
        Q_Q(QPrintDialog);
        QPrinter *p = q->printer();
        printerOutputFormat = outputFormat;

        // printer supports duplex mode?
        const auto supportedDuplexMode = top->d->m_currentPrintDevice.supportedDuplexModes();
        options.duplexLong->setEnabled(supportedDuplexMode.contains(QPrint::DuplexLongSide));
        options.duplexShort->setEnabled(supportedDuplexMode.contains(QPrint::DuplexShortSide));

        if (p->colorMode() == QPrinter::Color)
            options.color->setChecked(true);
        else
            options.grayscale->setChecked(true);

        // keep duplex value explicitly set by user, if any, and selected printer supports it;
        // use device default otherwise
        QPrint::DuplexMode duplex;
        if (explicitDuplexMode != QPrint::DuplexAuto && supportedDuplexMode.contains(explicitDuplexMode))
            duplex = explicitDuplexMode;
        else
            duplex = top->d->m_currentPrintDevice.defaultDuplexMode();
        switch (duplex) {
        case QPrint::DuplexNone:
            options.noDuplex->setChecked(true); break;
        case QPrint::DuplexLongSide:
        case QPrint::DuplexAuto:
            options.duplexLong->setChecked(true); break;
        case QPrint::DuplexShortSide:
            options.duplexShort->setChecked(true); break;
        }
        options.copies->setValue(p->copyCount());
        options.collate->setChecked(p->collateCopies());
        options.reverse->setChecked(p->pageOrder() == QPrinter::LastPageFirst);

        if (outputFormat == QPrinter::PdfFormat || options.printSelection->isChecked()
            || options.printCurrentPage->isChecked())

            options.pageSetCombo->setEnabled(false);
        else
            options.pageSetCombo->setEnabled(true);

#if QT_CONFIG(cups)
        // Disable color options on main dialog if not printing to file, it will be handled by CUPS advanced dialog
        options.colorMode->setVisible(outputFormat == QPrinter::PdfFormat);
#endif
}

#if QT_CONFIG(cups)
static std::vector<std::pair<int, int>> pageRangesFromString(const QString &pagesString) Q_DECL_NOTHROW
{
    std::vector<std::pair<int, int>> result;
    const QStringList items = pagesString.split(',');
    for (const QString item : items) {
        if (item.isEmpty())
            return {};

        if (item.contains(QLatin1Char('-'))) {
            const QStringList rangeItems = item.split('-');
            if (rangeItems.count() != 2)
                return {};

            bool ok;
            const int number1 = rangeItems[0].toInt(&ok);
            if (!ok)
                return {};

            const int number2 = rangeItems[1].toInt(&ok);
            if (!ok)
                return {};

            if (number1 < 1 || number2 < 1 || number2 < number1)
                return {};

            result.push_back(std::make_pair(number1, number2));

        } else {
            bool ok;
            const int number = item.toInt(&ok);
            if (!ok)
                return {};

            if (number < 1)
                return {};

            result.push_back(std::make_pair(number, number));
        }
    }

    // check no range intersects with the next
    std::sort(result.begin(), result.end(),
              [](const std::pair<int, int> &it1, const std::pair<int, int> &it2) { return it1.first < it2.first; });
    int previousSecond = -1;
    for (auto pair : result) {
        if (pair.first <= previousSecond)
            return {};

        previousSecond = pair.second;
    }

    return result;
}

static QString stringFromPageRanges(const std::vector<std::pair<int, int>> &pageRanges) Q_DECL_NOTHROW
{
    QString result;

    for (auto pair : pageRanges) {
        if (!result.isEmpty())
            result += QLatin1Char(',');

        if (pair.first == pair.second)
            result += QString::number(pair.first);
        else
            result += QStringLiteral("%1-%2").arg(pair.first).arg(pair.second);
    }

    return result;
}

static bool isValidPagesString(const QString &pagesString) Q_DECL_NOTHROW
{
    if (pagesString.isEmpty())
        return false;

    auto pagesRanges = pageRangesFromString(pagesString);
    return !pagesRanges.empty();
}

void QPrintDialogPrivate::updatePpdDuplexOption(QRadioButton *radio)
{
    const bool checked = radio->isChecked();
    if (checked) {
        if (radio == options.noDuplex) top->d->setPpdDuplex(QPrinter::DuplexNone);
        else if (radio == options.duplexLong) top->d->setPpdDuplex(QPrinter::DuplexLongSide);
        else if (radio == options.duplexShort) top->d->setPpdDuplex(QPrinter::DuplexShortSide);
    }
    const bool conflict = checked && top->d->m_duplexPpdOption && top->d->m_duplexPpdOption->conflicted;
    radio->setIcon(conflict ? QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, nullptr) : QIcon());
}

#endif

void QPrintDialogPrivate::setExplicitDuplexMode(const QPrint::DuplexMode duplexMode)
{
    explicitDuplexMode = duplexMode;
}

void QPrintDialogPrivate::setupPrinter()
{
    // First setup the requested OutputFormat, Printer and Page Size first
    top->d->setupPrinter();

    // Then setup Print Job options
    Q_Q(QPrintDialog);
    QPrinter* p = q->printer();

    if (options.duplex->isEnabled()) {
        if (options.noDuplex->isChecked())
            p->setDuplex(QPrinter::DuplexNone);
        else if (options.duplexLong->isChecked())
            p->setDuplex(QPrinter::DuplexLongSide);
        else
            p->setDuplex(QPrinter::DuplexShortSide);
    }

#if QT_CONFIG(cups)
    // When printing to a device the colorMode will be set by the advanced panel
    if (p->outputFormat() == QPrinter::PdfFormat)
#endif
        p->setColorMode(options.color->isChecked() ? QPrinter::Color : QPrinter::GrayScale);

    p->setPageOrder(options.reverse->isChecked() ? QPrinter::LastPageFirst : QPrinter::FirstPageFirst);

    // print range
    if (options.printAll->isChecked()) {
        p->setPrintRange(QPrinter::AllPages);
        p->setFromTo(0,0);
    } else if (options.printSelection->isChecked()) {
        p->setPrintRange(QPrinter::Selection);
        p->setFromTo(0,0);
    } else if (options.printCurrentPage->isChecked()) {
        p->setPrintRange(QPrinter::CurrentPage);
        p->setFromTo(0,0);
    } else if (options.printRange->isChecked()) {
        if (q->isOptionEnabled(QPrintDialog::PrintPageRange)) {
            p->setPrintRange(QPrinter::PageRange);
            p->setFromTo(options.from->value(), qMax(options.from->value(), options.to->value()));
        } else {
            // This case happens when CUPS server-side page range is enabled
            // Setting the range to the printer occurs below
            p->setPrintRange(QPrinter::AllPages);
            p->setFromTo(0,0);
        }
    }

#if QT_CONFIG(cups)
    if (options.pagesRadioButton->isChecked()) {
        auto pageRanges = pageRangesFromString(options.pagesLineEdit->text());

        p->setPrintRange(QPrinter::AllPages);
        p->setFromTo(0, 0);

        // server-side page filtering
        QCUPSSupport::setPageRange(p, stringFromPageRanges(pageRanges));
    }

    // page set
    if (p->printRange() == QPrinter::AllPages || p->printRange() == QPrinter::PageRange) {
        //If the application is selecting pages and the first page number is even then need to adjust the odd-even accordingly
        QCUPSSupport::PageSet pageSet = options.pageSetCombo->itemData(options.pageSetCombo->currentIndex()).value<QCUPSSupport::PageSet>();
        if (q->isOptionEnabled(QPrintDialog::PrintPageRange)
            && p->printRange() == QPrinter::PageRange
            && (q->fromPage() % 2 == 0)) {

            switch (pageSet) {
            case QCUPSSupport::AllPages:
                break;
            case QCUPSSupport::OddPages:
                QCUPSSupport::setPageSet(p, QCUPSSupport::EvenPages);
                break;
            case QCUPSSupport::EvenPages:
                QCUPSSupport::setPageSet(p, QCUPSSupport::OddPages);
                break;
            }
        } else if (pageSet != QCUPSSupport::AllPages) {
            QCUPSSupport::setPageSet(p, pageSet);
        }

        // server-side page range, since we set the page range on the printer to 0-0/AllPages above,
        // we need to take the values directly from the widget as q->fromPage() will return 0
        if (!q->isOptionEnabled(QPrintDialog::PrintPageRange) && options.printRange->isChecked())
            QCUPSSupport::setPageRange(p, options.from->value(), qMax(options.from->value(), options.to->value()));
    }
#endif

    // copies
    p->setCopyCount(options.copies->value());
    p->setCollateCopies(options.collate->isChecked());
}

void QPrintDialogPrivate::_q_togglePageSetCombo(bool checked)
{
    if (printerOutputFormat == QPrinter::PdfFormat)
        return;

    options.pageSetCombo->setDisabled(checked);
}

void QPrintDialogPrivate::_q_collapseOrExpandDialog()
{
    int collapseHeight = 0;
    Q_Q(QPrintDialog);
    QWidget *widgetToHide = bottom;
    if (widgetToHide->isVisible()) {
        collapseButton->setText(QPrintDialog::tr("&Options >>"));
        collapseHeight = widgetToHide->y() + widgetToHide->height() - (top->y() + top->height());
    }
    else
        collapseButton->setText(QPrintDialog::tr("&Options <<"));
    widgetToHide->setVisible(! widgetToHide->isVisible());
    if (! widgetToHide->isVisible()) { // make it shrink
        q->layout()->activate();
        q->resize( QSize(q->width(), q->height() - collapseHeight) );
    }
}

#if QT_CONFIG(messagebox)
void QPrintDialogPrivate::_q_checkFields()
{
    Q_Q(QPrintDialog);
    if (top->d->checkFields())
        q->accept();
}
#endif // QT_CONFIG(messagebox)


void QPrintDialogPrivate::updateWidgets()
{
    Q_Q(QPrintDialog);
    options.gbPrintRange->setVisible(q->isOptionEnabled(QPrintDialog::PrintPageRange) ||
                                     q->isOptionEnabled(QPrintDialog::PrintSelection) ||
                                     q->isOptionEnabled(QPrintDialog::PrintCurrentPage));

    options.printRange->setEnabled(q->isOptionEnabled(QPrintDialog::PrintPageRange));
    options.printSelection->setVisible(q->isOptionEnabled(QPrintDialog::PrintSelection));
    options.printCurrentPage->setVisible(q->isOptionEnabled(QPrintDialog::PrintCurrentPage));
    options.collate->setVisible(q->isOptionEnabled(QPrintDialog::PrintCollateCopies));

#if QT_CONFIG(cups)
    // Don't display Page Set if only Selection or Current Page are enabled
    if (!q->isOptionEnabled(QPrintDialog::PrintPageRange)
        && (q->isOptionEnabled(QPrintDialog::PrintSelection) || q->isOptionEnabled(QPrintDialog::PrintCurrentPage))) {
        options.pageSetCombo->setVisible(false);
        options.pageSetLabel->setVisible(false);
    } else {
        options.pageSetCombo->setVisible(true);
        options.pageSetLabel->setVisible(true);
    }

    if (!q->isOptionEnabled(QPrintDialog::PrintPageRange)) {
        // If we can do CUPS server side pages selection,
        // display the page range widgets
        options.gbPrintRange->setVisible(true);
        options.printRange->setEnabled(true);
    }
#endif

    switch (q->printRange()) {
    case QPrintDialog::AllPages:
        options.printAll->setChecked(true);
        options.pageSetCombo->setEnabled(true);
        break;
    case QPrintDialog::Selection:
        options.printSelection->setChecked(true);
        options.pageSetCombo->setEnabled(false);
        break;
    case QPrintDialog::PageRange:
        options.printRange->setChecked(true);
        options.pageSetCombo->setEnabled(true);
        break;
    case QPrintDialog::CurrentPage:
        if (q->isOptionEnabled(QPrintDialog::PrintCurrentPage)) {
            options.printCurrentPage->setChecked(true);
            options.pageSetCombo->setEnabled(false);
        }
        break;
    default:
        break;
    }
    const int minPage = qMax(1, qMin(q->minPage() , q->maxPage()));
    const int maxPage = qMax(1, q->maxPage() == INT_MAX ? 9999 : q->maxPage());

    options.from->setMinimum(minPage);
    options.to->setMinimum(minPage);
    options.from->setMaximum(maxPage);
    options.to->setMaximum(maxPage);

    options.from->setValue(q->fromPage());
    options.to->setValue(q->toPage());
    top->d->updateWidget();
}

void QPrintDialogPrivate::setTabs(const QList<QWidget*> &tabWidgets)
{
    QList<QWidget*>::ConstIterator iter = tabWidgets.begin();
    while(iter != tabWidgets.constEnd()) {
        QWidget *tab = *iter;
        options.tabs->addTab(tab, tab->windowTitle());
        ++iter;
    }
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*

    QPrintDialog

    The main Print Dialog.

*/

QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent)
    : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent)
{
    Q_D(QPrintDialog);
    d->init();
}

/*!
    Constructs a print dialog with the given \a parent.
*/
QPrintDialog::QPrintDialog(QWidget *parent)
    : QAbstractPrintDialog(*(new QPrintDialogPrivate), nullptr, parent)
{
    Q_D(QPrintDialog);
    d->init();
}

QPrintDialog::~QPrintDialog()
{
}

void QPrintDialog::setVisible(bool visible)
{
    Q_D(QPrintDialog);

    if (visible)
        d->updateWidgets();

    QAbstractPrintDialog::setVisible(visible);
}

int QPrintDialog::exec()
{
    return QDialog::exec();
}

void QPrintDialog::accept()
{
    Q_D(QPrintDialog);
#if QT_CONFIG(cups)
    if (d->options.pagesRadioButton->isChecked() && !isValidPagesString(d->options.pagesLineEdit->text())) {
        QMessageBox::critical(this, tr("Invalid Pages Definition"),
                              tr("%1 does not follow the correct syntax. Please use ',' to separate "
                              "ranges and pages, '-' to define ranges and make sure ranges do "
                              "not intersect with each other.").arg(d->options.pagesLineEdit->text()),
                              QMessageBox::Ok, QMessageBox::Ok);
        return;
    }
    if (d->top->d->m_duplexPpdOption && d->top->d->m_duplexPpdOption->conflicted) {
        const QMessageBox::StandardButton answer = QMessageBox::warning(this, tr("Duplex Settings Conflicts"),
                                                                        tr("There are conflicts in duplex settings. Do you want to fix them?"),
                                                                        QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
        if (answer != QMessageBox::No)
            return;
    }
#endif
    d->setupPrinter();
    QDialog::accept();
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*

    QUnixPrintWidget && QUnixPrintWidgetPrivate

    The upper half of the Print Dialog containing the Printer Selection widgets

*/

#if defined (Q_OS_UNIX)

/*! \internal
*/
QUnixPrintWidgetPrivate::QUnixPrintWidgetPrivate(QUnixPrintWidget *p, QPrinter *prn)
    : parent(p), propertiesDialog(nullptr), printer(prn),
#if QT_CONFIG(cups)
      m_duplexPpdOption(nullptr),
#endif
      optionsPane(nullptr), filePrintersAdded(false)
{
    q = nullptr;
    if (parent)
        q = qobject_cast<QAbstractPrintDialog*> (parent->parent());

    widget.setupUi(parent);

    int currentPrinterIndex = 0;
    QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
    if (ps) {
        const QStringList printers = ps->availablePrintDeviceIds();
        const QString defaultPrinter = ps->defaultPrintDeviceId();

        widget.printers->addItems(printers);

        const QString selectedPrinter = prn && !prn->printerName().isEmpty() ? prn->printerName() : defaultPrinter;
        const int idx = printers.indexOf(selectedPrinter);

        if (idx >= 0)
            currentPrinterIndex = idx;
    }
    widget.properties->setEnabled(true);

#if QT_CONFIG(filesystemmodel) && QT_CONFIG(completer)
    QFileSystemModel *fsm = new QFileSystemModel(widget.filename);
    fsm->setRootPath(QDir::homePath());
    widget.filename->setCompleter(new QCompleter(fsm, widget.filename));
#endif
    _q_printerChanged(currentPrinterIndex);

    QObject::connect(widget.printers, SIGNAL(currentIndexChanged(int)),
                     parent, SLOT(_q_printerChanged(int)));
    QObject::connect(widget.fileBrowser, SIGNAL(clicked()), parent, SLOT(_q_btnBrowseClicked()));
    QObject::connect(widget.properties, SIGNAL(clicked()), parent, SLOT(_q_btnPropertiesClicked()));

    // disable features that QPrinter does not yet support.
    widget.preview->setVisible(false);
}

void QUnixPrintWidgetPrivate::updateWidget()
{
    const bool printToFile = q == 0 || q->isOptionEnabled(QPrintDialog::PrintToFile);
    if (printToFile && !filePrintersAdded) {
        if (widget.printers->count())
            widget.printers->insertSeparator(widget.printers->count());
        widget.printers->addItem(QPrintDialog::tr("Print to File (PDF)"));
        filePrintersAdded = true;
    }
    if (!printToFile && filePrintersAdded) {
        widget.printers->removeItem(widget.printers->count()-1);
        widget.printers->removeItem(widget.printers->count()-1);
        if (widget.printers->count())
            widget.printers->removeItem(widget.printers->count()-1); // remove separator
        filePrintersAdded = false;
    }
    if (printer && filePrintersAdded && (printer->outputFormat() != QPrinter::NativeFormat
                                         || printer->printerName().isEmpty()))
    {
        if (printer->outputFormat() == QPrinter::PdfFormat)
            widget.printers->setCurrentIndex(widget.printers->count() - 1);
        widget.filename->setEnabled(true);
        widget.lOutput->setEnabled(true);
    }

    widget.filename->setVisible(printToFile);
    widget.lOutput->setVisible(printToFile);
    widget.fileBrowser->setVisible(printToFile);

    widget.properties->setVisible(q->isOptionEnabled(QAbstractPrintDialog::PrintShowPageSize));
}

QUnixPrintWidgetPrivate::~QUnixPrintWidgetPrivate()
{
}

void QUnixPrintWidgetPrivate::_q_printerChanged(int index)
{
    if (index < 0)
        return;
    const int printerCount = widget.printers->count();
    widget.filename->setEnabled(false);
    widget.lOutput->setEnabled(false);

    // Reset properties dialog when printer is changed
    if (propertiesDialog){
        delete propertiesDialog;
        propertiesDialog = nullptr;
    }

#if QT_CONFIG(cups)
    m_duplexPpdOption = nullptr;
#endif

    if (filePrintersAdded) {
        Q_ASSERT(index != printerCount - 2); // separator
        if (index == printerCount - 1) { // PDF
            widget.location->setText(QPrintDialog::tr("Local file"));
            widget.type->setText(QPrintDialog::tr("Write PDF file"));
            widget.properties->setEnabled(true);
            widget.filename->setEnabled(true);
            QString filename = widget.filename->text();
            widget.filename->setText(filename);
            widget.lOutput->setEnabled(true);
            if (optionsPane)
                optionsPane->selectPrinter(QPrinter::PdfFormat);
            printer->setOutputFormat(QPrinter::PdfFormat);
            m_currentPrintDevice = QPrintDevice();
            return;
        }
    }

    if (printer) {
        printer->setOutputFormat(QPrinter::NativeFormat);

        QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
        if (ps)
            m_currentPrintDevice = ps->createPrintDevice(widget.printers->itemText(index));
        else
            m_currentPrintDevice = QPrintDevice();

        printer->setPrinterName(m_currentPrintDevice.id());

        widget.location->setText(m_currentPrintDevice.location());
        widget.type->setText(m_currentPrintDevice.makeAndModel());
        if (optionsPane)
            optionsPane->selectPrinter(QPrinter::NativeFormat);
    }

#if QT_CONFIG(cups)
    m_duplexPpdOption = QCUPSSupport::findPpdOption("Duplex", &m_currentPrintDevice);
#endif
}

void QUnixPrintWidgetPrivate::setOptionsPane(QPrintDialogPrivate *pane)
{
    optionsPane = pane;
    if (optionsPane)
        optionsPane->selectPrinter(QPrinter::NativeFormat);
}

void QUnixPrintWidgetPrivate::_q_btnBrowseClicked()
{
    QString filename = widget.filename->text();
#if QT_CONFIG(filedialog)
    filename = QFileDialog::getSaveFileName(parent, QPrintDialog::tr("Print To File ..."), filename,
                                            QString(), nullptr, QFileDialog::DontConfirmOverwrite);
#else
    filename.clear();
#endif
    if (!filename.isEmpty()) {
        widget.filename->setText(filename);
        widget.printers->setCurrentIndex(widget.printers->count() - 1); // the pdf one
    }
}

#if QT_CONFIG(messagebox)
bool QUnixPrintWidgetPrivate::checkFields()
{
    if (widget.filename->isEnabled()) {
        QString file = widget.filename->text();
        QFile f(file);
        QFileInfo fi(f);
        bool exists = fi.exists();
        bool opened = false;
        if (exists && fi.isDir()) {
            QMessageBox::warning(q, q->windowTitle(),
                            QPrintDialog::tr("%1 is a directory.\nPlease choose a different file name.").arg(file));
            return false;
        } else if ((exists && !fi.isWritable()) || !(opened = f.open(QFile::Append))) {
            QMessageBox::warning(q, q->windowTitle(),
                            QPrintDialog::tr("File %1 is not writable.\nPlease choose a different file name.").arg(file));
            return false;
        } else if (exists) {
            int ret = QMessageBox::question(q, q->windowTitle(),
                                            QPrintDialog::tr("%1 already exists.\nDo you want to overwrite it?").arg(file),
                                            QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
            if (ret == QMessageBox::No)
                return false;
        }
        if (opened) {
            f.close();
            if (!exists)
                f.remove();
        }
    }

#if QT_CONFIG(cups)
    if (propertiesDialog) {
        QCUPSSupport::PagesPerSheet pagesPerSheet = propertiesDialog->widget.pageSetup->m_ui.pagesPerSheetCombo
                                                                    ->currentData().value<QCUPSSupport::PagesPerSheet>();

        QCUPSSupport::PageSet pageSet = optionsPane->options.pageSetCombo->currentData().value<QCUPSSupport::PageSet>();


        if (pagesPerSheet != QCUPSSupport::OnePagePerSheet
            && pageSet != QCUPSSupport::AllPages) {
            QMessageBox::warning(q, q->windowTitle(),
                                 QPrintDialog::tr("Options 'Pages Per Sheet' and 'Page Set' cannot be used together.\nPlease turn one of those options off."));
            return false;
        }
    }
#endif

    // Every test passed. Accept the dialog.
    return true;
}
#endif // QT_CONFIG(messagebox)

void QUnixPrintWidgetPrivate::setupPrinterProperties()
{
    delete propertiesDialog;

    QPrinter::OutputFormat outputFormat;
    QString printerName;

    if (q->isOptionEnabled(QPrintDialog::PrintToFile)
        && (widget.printers->currentIndex() == widget.printers->count() - 1)) {// PDF
        outputFormat = QPrinter::PdfFormat;
    } else {
        outputFormat = QPrinter::NativeFormat;
        printerName = widget.printers->currentText();
    }

    propertiesDialog = new QPrintPropertiesDialog(q->printer(), &m_currentPrintDevice, outputFormat, printerName, q);
}

#if QT_CONFIG(cups)
void QUnixPrintWidgetPrivate::setPpdDuplex(QPrinter::DuplexMode mode)
{
    auto values = QStringList{} << QStringLiteral("Duplex");
    if (mode == QPrinter::DuplexNone) values << QStringLiteral("None");
    else if (mode == QPrinter::DuplexLongSide) values << QStringLiteral("DuplexNoTumble");
    else if (mode == QPrinter::DuplexShortSide) values << QStringLiteral("DuplexTumble");

    m_currentPrintDevice.setProperty(PDPK_PpdOption, values);
}
#endif

void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked()
{
    if (!propertiesDialog)
        setupPrinterProperties();
    propertiesDialog->exec();

#if QT_CONFIG(cups)
    // update the warning icon on the duplex options if needed
    optionsPane->updatePpdDuplexOption(optionsPane->options.noDuplex);
    optionsPane->updatePpdDuplexOption(optionsPane->options.duplexLong);
    optionsPane->updatePpdDuplexOption(optionsPane->options.duplexShort);
#endif
}

void QUnixPrintWidgetPrivate::setupPrinter()
{
    const int printerCount = widget.printers->count();
    const int index = widget.printers->currentIndex();

    if (filePrintersAdded && index == printerCount - 1) { // PDF
        printer->setPrinterName(QString());
        Q_ASSERT(index != printerCount - 2); // separator
        printer->setOutputFormat(QPrinter::PdfFormat);
        QString path = widget.filename->text();
        if (QDir::isRelativePath(path))
            path = QDir::homePath() + QDir::separator() + path;
        printer->setOutputFileName(path);
    }
    else {
        printer->setPrinterName(widget.printers->currentText());
        printer->setOutputFileName(QString());
    }

    if (!propertiesDialog)
        setupPrinterProperties();

    propertiesDialog->setupPrinter();
}

/*! \internal
*/
QUnixPrintWidget::QUnixPrintWidget(QPrinter *printer, QWidget *parent)
    : QWidget(parent), d(new QUnixPrintWidgetPrivate(this, printer))
{
    if (printer == nullptr)
        return;
    if (printer->outputFileName().isEmpty()) {
        QString home = QDir::homePath();
        QString cur = QDir::currentPath();
        if (!home.endsWith(QLatin1Char('/')))
            home += QLatin1Char('/');
        if (!cur.startsWith(home))
            cur = home;
        else if (!cur.endsWith(QLatin1Char('/')))
            cur += QLatin1Char('/');
        if (QGuiApplication::platformName() == QStringLiteral("xcb")) {
            if (printer->docName().isEmpty()) {
                cur += QStringLiteral("print.pdf");
            } else {
                const QRegExp re(QStringLiteral("(.*)\\.\\S+"));
                if (re.exactMatch(printer->docName()))
                    cur += re.cap(1);
                else
                    cur += printer->docName();
                cur += QStringLiteral(".pdf");
            }
        } // xcb

        d->widget.filename->setText(cur);
    }
    else
        d->widget.filename->setText(printer->outputFileName());
    const QString printerName = printer->printerName();
    if (!printerName.isEmpty()) {
        const int i = d->widget.printers->findText(printerName);
        if (i >= 0)
            d->widget.printers->setCurrentIndex(i);
    }
    // PDF printer not added to the dialog yet, we'll handle those cases in QUnixPrintWidgetPrivate::updateWidget
}

/*! \internal
*/
QUnixPrintWidget::~QUnixPrintWidget()
{
    delete d;
}

/*! \internal

    Updates the printer with the states held in the QUnixPrintWidget.
*/
void QUnixPrintWidget::updatePrinter()
{
    d->setupPrinter();
}

#if QT_CONFIG(cups)

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#endif // QT_CONFIG(cups)
#endif // defined (Q_OS_UNIX)

QT_END_NAMESPACE

#include "moc_qprintdialog.cpp"
#include "qprintdialog_unix.moc"
