diff options
Diffstat (limited to 'src/gui/dialogs')
98 files changed, 35061 insertions, 0 deletions
diff --git a/src/gui/dialogs/dialogs.pri b/src/gui/dialogs/dialogs.pri new file mode 100644 index 0000000000..f1ec8588bd --- /dev/null +++ b/src/gui/dialogs/dialogs.pri @@ -0,0 +1,97 @@ +# Qt dialogs module + +HEADERS += \ + dialogs/qabstractprintdialog.h \ + dialogs/qabstractprintdialog_p.h \ + dialogs/qabstractpagesetupdialog.h \ + dialogs/qabstractpagesetupdialog_p.h \ + dialogs/qcolordialog.h \ + dialogs/qcolordialog_p.h \ + dialogs/qdialog.h \ + dialogs/qdialog_p.h \ + dialogs/qerrormessage.h \ + dialogs/qfiledialog.h \ + dialogs/qfiledialog_p.h \ + dialogs/qfontdialog.h \ + dialogs/qfontdialog_p.h \ + dialogs/qinputdialog.h \ + dialogs/qmessagebox.h \ + dialogs/qpagesetupdialog.h \ + dialogs/qprintdialog.h \ + dialogs/qprogressdialog.h \ + dialogs/qsidebar_p.h \ + dialogs/qfilesystemmodel.h \ + dialogs/qfilesystemmodel_p.h \ + dialogs/qfileinfogatherer_p.h \ + dialogs/qwizard.h \ + dialogs/qprintpreviewdialog.h + +!embedded:mac { + OBJECTIVE_SOURCES += dialogs/qcolordialog_mac.mm \ + dialogs/qfiledialog_mac.mm \ + dialogs/qfontdialog_mac.mm \ + dialogs/qnspanelproxy_mac.mm \ + dialogs/qpagesetupdialog_mac.mm \ + dialogs/qprintdialog_mac.mm +} +win32 { + HEADERS += dialogs/qwizard_win_p.h + SOURCES += dialogs/qdialogsbinarycompat_win.cpp \ + dialogs/qfiledialog_win.cpp \ + dialogs/qpagesetupdialog_win.cpp \ + dialogs/qprintdialog_win.cpp \ + dialogs/qwizard_win.cpp + + !win32-borland:!wince*: LIBS += -lshell32 # the filedialog needs this library +} + +!mac:!embedded:unix { + HEADERS += dialogs/qpagesetupdialog_unix_p.h + SOURCES += dialogs/qprintdialog_unix.cpp \ + dialogs/qpagesetupdialog_unix.cpp + FORMS += dialogs/qprintsettingsoutput.ui \ + dialogs/qprintwidget.ui \ + dialogs/qprintpropertieswidget.ui +} + +embedded { + contains(QT_CONFIG,qtopia) { + HEADERS += dialogs/qpagesetupdialog_unix_p.h + DEFINES += QTOPIA_PRINTDIALOG + SOURCES += dialogs/qprintdialog_qws.cpp \ + dialogs/qpagesetupdialog_unix.cpp + } else { + HEADERS += dialogs/qpagesetupdialog_unix_p.h + SOURCES += dialogs/qprintdialog_unix.cpp \ + dialogs/qpagesetupdialog_unix.cpp + FORMS += dialogs/qprintsettingsoutput.ui \ + dialogs/qprintwidget.ui \ + dialogs/qprintpropertieswidget.ui + } +} + +wince*: FORMS += dialogs/qfiledialog_wince.ui +else: FORMS += dialogs/qfiledialog.ui + +INCLUDEPATH += $$PWD +SOURCES += \ + dialogs/qabstractprintdialog.cpp \ + dialogs/qabstractpagesetupdialog.cpp \ + dialogs/qcolordialog.cpp \ + dialogs/qdialog.cpp \ + dialogs/qerrormessage.cpp \ + dialogs/qfiledialog.cpp \ + dialogs/qfontdialog.cpp \ + dialogs/qinputdialog.cpp \ + dialogs/qmessagebox.cpp \ + dialogs/qprogressdialog.cpp \ + dialogs/qsidebar.cpp \ + dialogs/qfilesystemmodel.cpp \ + dialogs/qfileinfogatherer.cpp \ + dialogs/qpagesetupdialog.cpp \ + dialogs/qwizard.cpp \ + dialogs/qprintpreviewdialog.cpp + +FORMS += dialogs/qpagesetupwidget.ui +RESOURCES += dialogs/qprintdialog.qrc +RESOURCES += dialogs/qmessagebox.qrc
\ No newline at end of file diff --git a/src/gui/dialogs/images/fit-page-24.png b/src/gui/dialogs/images/fit-page-24.png Binary files differnew file mode 100644 index 0000000000..c7b39d8853 --- /dev/null +++ b/src/gui/dialogs/images/fit-page-24.png diff --git a/src/gui/dialogs/images/fit-page-32.png b/src/gui/dialogs/images/fit-page-32.png Binary files differnew file mode 100644 index 0000000000..98bc12d3ed --- /dev/null +++ b/src/gui/dialogs/images/fit-page-32.png diff --git a/src/gui/dialogs/images/fit-width-24.png b/src/gui/dialogs/images/fit-width-24.png Binary files differnew file mode 100644 index 0000000000..a729ffda54 --- /dev/null +++ b/src/gui/dialogs/images/fit-width-24.png diff --git a/src/gui/dialogs/images/fit-width-32.png b/src/gui/dialogs/images/fit-width-32.png Binary files differnew file mode 100644 index 0000000000..470a8b45d0 --- /dev/null +++ b/src/gui/dialogs/images/fit-width-32.png diff --git a/src/gui/dialogs/images/go-first-24.png b/src/gui/dialogs/images/go-first-24.png Binary files differnew file mode 100644 index 0000000000..55315ffa38 --- /dev/null +++ b/src/gui/dialogs/images/go-first-24.png diff --git a/src/gui/dialogs/images/go-first-32.png b/src/gui/dialogs/images/go-first-32.png Binary files differnew file mode 100644 index 0000000000..0fe6f94b77 --- /dev/null +++ b/src/gui/dialogs/images/go-first-32.png diff --git a/src/gui/dialogs/images/go-last-24.png b/src/gui/dialogs/images/go-last-24.png Binary files differnew file mode 100644 index 0000000000..81061b80f2 --- /dev/null +++ b/src/gui/dialogs/images/go-last-24.png diff --git a/src/gui/dialogs/images/go-last-32.png b/src/gui/dialogs/images/go-last-32.png Binary files differnew file mode 100644 index 0000000000..887506107e --- /dev/null +++ b/src/gui/dialogs/images/go-last-32.png diff --git a/src/gui/dialogs/images/go-next-24.png b/src/gui/dialogs/images/go-next-24.png Binary files differnew file mode 100644 index 0000000000..9a55ef3d86 --- /dev/null +++ b/src/gui/dialogs/images/go-next-24.png diff --git a/src/gui/dialogs/images/go-next-32.png b/src/gui/dialogs/images/go-next-32.png Binary files differnew file mode 100644 index 0000000000..6d98f50f4f --- /dev/null +++ b/src/gui/dialogs/images/go-next-32.png diff --git a/src/gui/dialogs/images/go-previous-24.png b/src/gui/dialogs/images/go-previous-24.png Binary files differnew file mode 100644 index 0000000000..2ea769eb8d --- /dev/null +++ b/src/gui/dialogs/images/go-previous-24.png diff --git a/src/gui/dialogs/images/go-previous-32.png b/src/gui/dialogs/images/go-previous-32.png Binary files differnew file mode 100644 index 0000000000..37ba0c4e8d --- /dev/null +++ b/src/gui/dialogs/images/go-previous-32.png diff --git a/src/gui/dialogs/images/layout-landscape-24.png b/src/gui/dialogs/images/layout-landscape-24.png Binary files differnew file mode 100644 index 0000000000..6f89a31cb6 --- /dev/null +++ b/src/gui/dialogs/images/layout-landscape-24.png diff --git a/src/gui/dialogs/images/layout-landscape-32.png b/src/gui/dialogs/images/layout-landscape-32.png Binary files differnew file mode 100644 index 0000000000..6a94946c36 --- /dev/null +++ b/src/gui/dialogs/images/layout-landscape-32.png diff --git a/src/gui/dialogs/images/layout-portrait-24.png b/src/gui/dialogs/images/layout-portrait-24.png Binary files differnew file mode 100644 index 0000000000..e0dbabc83b --- /dev/null +++ b/src/gui/dialogs/images/layout-portrait-24.png diff --git a/src/gui/dialogs/images/layout-portrait-32.png b/src/gui/dialogs/images/layout-portrait-32.png Binary files differnew file mode 100644 index 0000000000..d17468c0a4 --- /dev/null +++ b/src/gui/dialogs/images/layout-portrait-32.png diff --git a/src/gui/dialogs/images/page-setup-24.png b/src/gui/dialogs/images/page-setup-24.png Binary files differnew file mode 100644 index 0000000000..4bfafdace0 --- /dev/null +++ b/src/gui/dialogs/images/page-setup-24.png diff --git a/src/gui/dialogs/images/page-setup-32.png b/src/gui/dialogs/images/page-setup-32.png Binary files differnew file mode 100644 index 0000000000..2313b8fe3b --- /dev/null +++ b/src/gui/dialogs/images/page-setup-32.png diff --git a/src/gui/dialogs/images/print-24.png b/src/gui/dialogs/images/print-24.png Binary files differnew file mode 100644 index 0000000000..c6bf3e8672 --- /dev/null +++ b/src/gui/dialogs/images/print-24.png diff --git a/src/gui/dialogs/images/print-32.png b/src/gui/dialogs/images/print-32.png Binary files differnew file mode 100644 index 0000000000..5830888653 --- /dev/null +++ b/src/gui/dialogs/images/print-32.png diff --git a/src/gui/dialogs/images/qtlogo-64.png b/src/gui/dialogs/images/qtlogo-64.png Binary files differnew file mode 100644 index 0000000000..4f68e162de --- /dev/null +++ b/src/gui/dialogs/images/qtlogo-64.png diff --git a/src/gui/dialogs/images/status-color.png b/src/gui/dialogs/images/status-color.png Binary files differnew file mode 100644 index 0000000000..af3cbfa31c --- /dev/null +++ b/src/gui/dialogs/images/status-color.png diff --git a/src/gui/dialogs/images/status-gray-scale.png b/src/gui/dialogs/images/status-gray-scale.png Binary files differnew file mode 100644 index 0000000000..4462588809 --- /dev/null +++ b/src/gui/dialogs/images/status-gray-scale.png diff --git a/src/gui/dialogs/images/view-page-multi-24.png b/src/gui/dialogs/images/view-page-multi-24.png Binary files differnew file mode 100644 index 0000000000..87241472ae --- /dev/null +++ b/src/gui/dialogs/images/view-page-multi-24.png diff --git a/src/gui/dialogs/images/view-page-multi-32.png b/src/gui/dialogs/images/view-page-multi-32.png Binary files differnew file mode 100644 index 0000000000..130885a041 --- /dev/null +++ b/src/gui/dialogs/images/view-page-multi-32.png diff --git a/src/gui/dialogs/images/view-page-one-24.png b/src/gui/dialogs/images/view-page-one-24.png Binary files differnew file mode 100644 index 0000000000..4c6457b892 --- /dev/null +++ b/src/gui/dialogs/images/view-page-one-24.png diff --git a/src/gui/dialogs/images/view-page-one-32.png b/src/gui/dialogs/images/view-page-one-32.png Binary files differnew file mode 100644 index 0000000000..537193984e --- /dev/null +++ b/src/gui/dialogs/images/view-page-one-32.png diff --git a/src/gui/dialogs/images/view-page-sided-24.png b/src/gui/dialogs/images/view-page-sided-24.png Binary files differnew file mode 100644 index 0000000000..2131305c41 --- /dev/null +++ b/src/gui/dialogs/images/view-page-sided-24.png diff --git a/src/gui/dialogs/images/view-page-sided-32.png b/src/gui/dialogs/images/view-page-sided-32.png Binary files differnew file mode 100644 index 0000000000..e4d63f9992 --- /dev/null +++ b/src/gui/dialogs/images/view-page-sided-32.png diff --git a/src/gui/dialogs/images/zoom-in-24.png b/src/gui/dialogs/images/zoom-in-24.png Binary files differnew file mode 100644 index 0000000000..d29b142b6c --- /dev/null +++ b/src/gui/dialogs/images/zoom-in-24.png diff --git a/src/gui/dialogs/images/zoom-in-32.png b/src/gui/dialogs/images/zoom-in-32.png Binary files differnew file mode 100644 index 0000000000..34d70af37b --- /dev/null +++ b/src/gui/dialogs/images/zoom-in-32.png diff --git a/src/gui/dialogs/images/zoom-out-24.png b/src/gui/dialogs/images/zoom-out-24.png Binary files differnew file mode 100644 index 0000000000..19703474f8 --- /dev/null +++ b/src/gui/dialogs/images/zoom-out-24.png diff --git a/src/gui/dialogs/images/zoom-out-32.png b/src/gui/dialogs/images/zoom-out-32.png Binary files differnew file mode 100644 index 0000000000..b832206612 --- /dev/null +++ b/src/gui/dialogs/images/zoom-out-32.png diff --git a/src/gui/dialogs/qabstractpagesetupdialog.cpp b/src/gui/dialogs/qabstractpagesetupdialog.cpp new file mode 100644 index 0000000000..a07cb0e29d --- /dev/null +++ b/src/gui/dialogs/qabstractpagesetupdialog.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstractpagesetupdialog.h" +#include "qabstractpagesetupdialog_p.h" + +#ifndef QT_NO_PRINTDIALOG + +#include <QtCore/qcoreapplication.h> +#include <QtGui/qprinter.h> + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QAbstractPageSetupDialog + + \brief The QAbstractPageSetupDialog class provides a base for + implementations of page setup dialogs. +*/ + +/*! + Constructs the page setup dialog for the printer \a printer with + \a parent as parent widget. +*/ +QAbstractPageSetupDialog::QAbstractPageSetupDialog(QPrinter *printer, QWidget *parent) + : QDialog(*(new QAbstractPageSetupDialogPrivate), parent) +{ + Q_D(QAbstractPageSetupDialog); + setWindowTitle(QCoreApplication::translate("QPrintPreviewDialog", "Page Setup")); + d->setPrinter(printer); +} + +/*! + \internal +*/ +QAbstractPageSetupDialog::QAbstractPageSetupDialog(QAbstractPageSetupDialogPrivate &ptr, + QPrinter *printer, QWidget *parent) + : QDialog(ptr, parent) +{ + Q_D(QAbstractPageSetupDialog); + setWindowTitle(QCoreApplication::translate("QPrintPreviewDialog", "Page Setup")); + d->setPrinter(printer); +} + +QAbstractPageSetupDialog::~QAbstractPageSetupDialog() +{ + Q_D(QAbstractPageSetupDialog); + if (d->opts & QPageSetupDialog::OwnsPrinter) + delete d->printer; +} + +/*! + Returns the printer that this page setup dialog is operating on. +*/ +QPrinter *QAbstractPageSetupDialog::printer() +{ + Q_D(QAbstractPageSetupDialog); + return d->printer; +} + +void QAbstractPageSetupDialogPrivate::setPrinter(QPrinter *newPrinter) +{ + if (newPrinter) { + printer = newPrinter; + } else { + printer = new QPrinter; + opts |= QPageSetupDialog::OwnsPrinter; + } +#ifndef Q_WS_X11 + if (printer->outputFormat() != QPrinter::NativeFormat) + qWarning("QPageSetupDialog: Cannot be used on non-native printers"); +#endif +} + +/*! + \fn int QAbstractPageSetupDialog::exec() + + This virtual function is called to pop up the dialog. It must be + reimplemented in subclasses. +*/ + +/*! + \reimp +*/ +void QAbstractPageSetupDialog::done(int result) +{ + Q_D(QAbstractPageSetupDialog); + QDialog::done(result); + if (d->receiverToDisconnectOnClose) { + disconnect(this, SIGNAL(accepted()), + d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); + d->receiverToDisconnectOnClose = 0; + } + d->memberToDisconnectOnClose.clear(); + +} + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTDIALOG diff --git a/src/gui/dialogs/qabstractpagesetupdialog.h b/src/gui/dialogs/qabstractpagesetupdialog.h new file mode 100644 index 0000000000..3169984dcf --- /dev/null +++ b/src/gui/dialogs/qabstractpagesetupdialog.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTPAGESETUPDIALOG_H +#define QABSTRACTPAGESETUPDIALOG_H + +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_PRINTDIALOG + +class QAbstractPageSetupDialogPrivate; +class QPrinter; + +// ### Qt 5: Remove this class +class Q_GUI_EXPORT QAbstractPageSetupDialog : public QDialog +{ + Q_DECLARE_PRIVATE(QAbstractPageSetupDialog) + Q_OBJECT + +public: + explicit QAbstractPageSetupDialog(QPrinter *printer, QWidget *parent = 0); + QAbstractPageSetupDialog(QAbstractPageSetupDialogPrivate &ptr, + QPrinter *printer, QWidget *parent = 0); + ~QAbstractPageSetupDialog(); + + virtual int exec() = 0; + void done(int result); + + QPrinter *printer(); +}; + +#endif // QT_NO_PRINTDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QABSTRACTPAGESETUPDIALOG_H diff --git a/src/gui/dialogs/qabstractpagesetupdialog_p.h b/src/gui/dialogs/qabstractpagesetupdialog_p.h new file mode 100644 index 0000000000..5d09342b6a --- /dev/null +++ b/src/gui/dialogs/qabstractpagesetupdialog_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTPAGESETUPDIALOG_P_H +#define QABSTRACTPAGESETUPDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// to version without notice, or even be removed. +// +// We mean it. +// +// + +#include "private/qdialog_p.h" + +#ifndef QT_NO_PRINTDIALOG + +#include "qbytearray.h" +#include "qpagesetupdialog.h" +#include "qpointer.h" + +QT_BEGIN_NAMESPACE + +class QPrinter; + +class QAbstractPageSetupDialogPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QAbstractPageSetupDialog) + +public: + QAbstractPageSetupDialogPrivate() : printer(0) {} + + void setPrinter(QPrinter *newPrinter); + + QPrinter *printer; + QPageSetupDialog::PageSetupDialogOptions opts; + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTDIALOG + +#endif // QABSTRACTPAGESETUPDIALOG_P_H diff --git a/src/gui/dialogs/qabstractprintdialog.cpp b/src/gui/dialogs/qabstractprintdialog.cpp new file mode 100644 index 0000000000..0dc16c90b3 --- /dev/null +++ b/src/gui/dialogs/qabstractprintdialog.cpp @@ -0,0 +1,500 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstractprintdialog_p.h" +#include "qcoreapplication.h" +#include "qprintdialog.h" +#include "qprinter.h" +#include "private/qprinter_p.h" + +#ifndef QT_NO_PRINTDIALOG + +QT_BEGIN_NAMESPACE + +// hack +class QPrintDialogPrivate : public QAbstractPrintDialogPrivate +{ +}; + +/*! + \class QAbstractPrintDialog + \brief The QAbstractPrintDialog class provides a base implementation for + print dialogs used to configure printers. + + This class implements getter and setter functions that are used to + customize settings shown in print dialogs, but it is not used directly. + Use QPrintDialog to display a print dialog in your application. + + \sa QPrintDialog, QPrinter, {Printing with Qt} +*/ + +/*! + \enum QAbstractPrintDialog::PrintRange + + Used to specify the print range selection option. + + \value AllPages All pages should be printed. + \value Selection Only the selection should be printed. + \value PageRange The specified page range should be printed. + + \sa QPrinter::PrintRange +*/ + +/*! + \enum QAbstractPrintDialog::PrintDialogOption + + Used to specify which parts of the print dialog should be visible. + + \value None None of the options are enabled. + \value PrintToFile The print to file option is enabled. + \value PrintSelection The print selection option is enabled. + \value PrintPageRange The page range selection option is enabled. + \value PrintCollateCopies + + This value is obsolete and does nothing since Qt 4.5: + + \value DontUseSheet In previous versions of Qt, exec() the print dialog + would create a sheet by default the dialog was given a parent. + This is no longer supported in Qt 4.5. If you want to use sheets, use + QPrintDialog::open() instead. + + \value PrintShowPageSize Show the page size + margins page only if this is enabled. +*/ + +/*! + Constructs an abstract print dialog for \a printer with \a parent + as parent widget. +*/ +QAbstractPrintDialog::QAbstractPrintDialog(QPrinter *printer, QWidget *parent) + : QDialog(*(new QAbstractPrintDialogPrivate), parent) +{ + Q_D(QAbstractPrintDialog); + setWindowTitle(QCoreApplication::translate("QPrintDialog", "Print")); + d->setPrinter(printer); +} + +/*! + \internal +*/ +QAbstractPrintDialog::QAbstractPrintDialog(QAbstractPrintDialogPrivate &ptr, + QPrinter *printer, + QWidget *parent) + : QDialog(ptr, parent) +{ + Q_D(QAbstractPrintDialog); + setWindowTitle(QCoreApplication::translate("QPrintDialog", "Print")); + d->setPrinter(printer); +} + +/*! + \internal +*/ +QAbstractPrintDialog::~QAbstractPrintDialog() +{ + Q_D(QAbstractPrintDialog); + if (d->ownsPrinter) + delete d->printer; +} + +/*! + Sets the given \a option to be enabled if \a on is true; + otherwise, clears the given \a option. + + \sa options, testOption() +*/ +void QPrintDialog::setOption(PrintDialogOption option, bool on) +{ + Q_D(QPrintDialog); + if (!(d->pd->options & option) != !on) + setOptions(d->pd->options ^ option); +} + +/*! + Returns true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption() +*/ +bool QPrintDialog::testOption(PrintDialogOption option) const +{ + Q_D(const QPrintDialog); + return (d->pd->options & option) != 0; +} + +/*! + \property QPrintDialog::options + \brief the various options that affect the look and feel of the dialog + \since 4.5 + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the + dialog is visible is not guaranteed to have an immediate effect on the + dialog (depending on the option and on the platform). + + \sa setOption(), testOption() +*/ +void QPrintDialog::setOptions(PrintDialogOptions options) +{ + Q_D(QPrintDialog); + + PrintDialogOptions changed = (options ^ d->pd->options); + if (!changed) + return; + + d->pd->options = options; +} + +QPrintDialog::PrintDialogOptions QPrintDialog::options() const +{ + Q_D(const QPrintDialog); + return d->pd->options; +} + +/*! + \obsolete + + Use QPrintDialog::setOptions() instead. +*/ +void QAbstractPrintDialog::setEnabledOptions(PrintDialogOptions options) +{ + Q_D(QAbstractPrintDialog); + d->pd->options = options; +} + +/*! + \obsolete + + Use QPrintDialog::setOption(\a option, true) instead. +*/ +void QAbstractPrintDialog::addEnabledOption(PrintDialogOption option) +{ + Q_D(QAbstractPrintDialog); + d->pd->options |= option; +} + +/*! + \obsolete + + Use QPrintDialog::options() instead. +*/ +QAbstractPrintDialog::PrintDialogOptions QAbstractPrintDialog::enabledOptions() const +{ + Q_D(const QAbstractPrintDialog); + return d->pd->options; +} + +/*! + \obsolete + + Use QPrintDialog::testOption(\a option) instead. +*/ +bool QAbstractPrintDialog::isOptionEnabled(PrintDialogOption option) const +{ + Q_D(const QAbstractPrintDialog); + return d->pd->options & option; +} + +/*! + Sets the print range option in to be \a range. + */ +void QAbstractPrintDialog::setPrintRange(PrintRange range) +{ + Q_D(QAbstractPrintDialog); + d->pd->printRange = range; +} + +/*! + Returns the print range. +*/ +QAbstractPrintDialog::PrintRange QAbstractPrintDialog::printRange() const +{ + Q_D(const QAbstractPrintDialog); + return d->pd->printRange; +} + +/*! + Sets the page range in this dialog to be from \a min to \a max. This also + enables the PrintPageRange option. +*/ +void QAbstractPrintDialog::setMinMax(int min, int max) +{ + Q_D(QAbstractPrintDialog); + Q_ASSERT_X(min <= max, "QAbstractPrintDialog::setMinMax", + "'min' must be less than or equal to 'max'"); + d->pd->minPage = min; + d->pd->maxPage = max; + d->pd->options |= PrintPageRange; +} + +/*! + Returns the minimum page in the page range. + By default, this value is set to 1. +*/ +int QAbstractPrintDialog::minPage() const +{ + Q_D(const QAbstractPrintDialog); + return d->pd->minPage; +} + +/*! + Returns the maximum page in the page range. As of Qt 4.4, this + function returns INT_MAX by default. Previous versions returned 1 + by default. +*/ +int QAbstractPrintDialog::maxPage() const +{ + Q_D(const QAbstractPrintDialog); + return d->pd->maxPage; +} + +/*! + Sets the range in the print dialog to be from \a from to \a to. +*/ +void QAbstractPrintDialog::setFromTo(int from, int to) +{ + Q_D(QAbstractPrintDialog); + Q_ASSERT_X(from <= to, "QAbstractPrintDialog::setFromTo", + "'from' must be less than or equal to 'to'"); + d->pd->fromPage = from; + d->pd->toPage = to; + + if (d->pd->minPage == 0 && d->pd->maxPage == 0) + setMinMax(1, to); +} + +/*! + Returns the first page to be printed + By default, this value is set to 0. +*/ +int QAbstractPrintDialog::fromPage() const +{ + Q_D(const QAbstractPrintDialog); + return d->pd->fromPage; +} + +/*! + Returns the last page to be printed. + By default, this value is set to 0. +*/ +int QAbstractPrintDialog::toPage() const +{ + Q_D(const QAbstractPrintDialog); + return d->pd->toPage; +} + + +/*! + Returns the printer that this printer dialog operates + on. +*/ +QPrinter *QAbstractPrintDialog::printer() const +{ + Q_D(const QAbstractPrintDialog); + return d->printer; +} + +void QAbstractPrintDialogPrivate::setPrinter(QPrinter *newPrinter) +{ + if (newPrinter) { + printer = newPrinter; + ownsPrinter = false; + } else { + printer = new QPrinter; + ownsPrinter = true; + } + pd = printer->d_func(); +} + +/*! + \fn int QAbstractPrintDialog::exec() + + This virtual function is called to pop up the dialog. It must be + reimplemented in subclasses. +*/ + +/*! + \class QPrintDialog + + \brief The QPrintDialog class provides a dialog for specifying + the printer's configuration. + + \ingroup dialogs + + The dialog allows users to change document-related settings, such + as the paper size and orientation, type of print (color or + grayscale), range of pages, and number of copies to print. + + Controls are also provided to enable users to choose from the + printers available, including any configured network printers. + + Typically, QPrintDialog objects are constructed with a QPrinter + object, and executed using the exec() function. + + \snippet doc/src/snippets/code/src_gui_dialogs_qabstractprintdialog.cpp 0 + + If the dialog is accepted by the user, the QPrinter object is + correctly configured for printing. + + \raw HTML + <table align="center"> + <tr><td> + \endraw + \inlineimage plastique-printdialog.png + \raw HTML + </td><td> + \endraw + \inlineimage plastique-printdialog-properties.png + \raw HTML + </td></tr> + </table> + \endraw + + The printer dialog (shown above in Plastique style) enables access to common + printing properties. On X11 platforms that use the CUPS printing system, the + settings for each available printer can be modified via the dialog's + \gui{Properties} push button. + + On Windows and Mac OS X, the native print dialog is used, which means that + some QWidget and QDialog properties set on the dialog won't be respected. + The native print dialog on + Mac OS X does not support setting printer options, i.e. + QAbstractPrintDialog::setEnabledOptions() and + QAbstractPrintDialog::addEnabledOption() have no effect. + + In Qt 4.4, it was possible to use the satic functions to show a sheet on + Mac OS X. This is no longer supported in Qt 4.5. If you want this + functionality, use QPrintDialog::open(). + + \sa QPageSetupDialog, QPrinter, {Pixelator Example}, {Order Form Example}, + {Image Viewer Example}, {Scribble Example} +*/ + +/*! + \fn QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent) + + Constructs a new modal printer dialog for the given \a printer + with the given \a parent. +*/ + +/*! + \fn QPrintDialog::~QPrintDialog() + + Destroys the print dialog. +*/ + +/*! + \fn int QPrintDialog::exec() + \reimp +*/ + +/*! + \since 4.4 + + Set a list of widgets as \a tabs to be shown on the print dialog, if supported. + + Currently this option is only supported on X11. + + Setting the option tabs will transfer their ownership to the print dialog. +*/ +void QAbstractPrintDialog::setOptionTabs(const QList<QWidget*> &tabs) +{ + Q_D(QAbstractPrintDialog); + d->setTabs(tabs); +} + +/*! + + \fn void QPrintDialog::accepted(QPrinter *printer) + + This signal is emitted when the user accepts the values set in the print dialog. + The \a printer parameter includes the printer that the settings were applied to. +*/ + +/*! + \fn QPrinter *QPrintDialog::printer() + + Returns the printer that this printer dialog operates + on. This can be useful when using the QPrintDialog::open() method. +*/ + +/*! + Closes the dialog and sets its result code to \a result. If this dialog + is shown with exec(), done() causes the local event loop to finish, + and exec() to return \a result. + + \sa QDialog::done() +*/ +void QPrintDialog::done(int result) +{ + Q_D(QPrintDialog); + QDialog::done(result); + if (result == Accepted) + emit accepted(printer()); + if (d->receiverToDisconnectOnClose) { + disconnect(this, SIGNAL(accepted(QPrinter*)), + d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); + d->receiverToDisconnectOnClose = 0; + } + d->memberToDisconnectOnClose.clear(); +} + +/*! + \since 4.5 + \overload + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QPrintDialog::open(QObject *receiver, const char *member) +{ + Q_D(QPrintDialog); + connect(this, SIGNAL(accepted(QPrinter*)), receiver, member); + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTDIALOG diff --git a/src/gui/dialogs/qabstractprintdialog.h b/src/gui/dialogs/qabstractprintdialog.h new file mode 100644 index 0000000000..fc1750cb16 --- /dev/null +++ b/src/gui/dialogs/qabstractprintdialog.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTPRINTDIALOG_H +#define QABSTRACTPRINTDIALOG_H + +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_PRINTER + +class QAbstractPrintDialogPrivate; +class QPrinter; + +// ### Qt 5: remove this class +class Q_GUI_EXPORT QAbstractPrintDialog : public QDialog +{ + Q_DECLARE_PRIVATE(QAbstractPrintDialog) + Q_OBJECT + +public: + enum PrintRange { + AllPages, + Selection, + PageRange + }; + + enum PrintDialogOption { + None = 0x0000, // obsolete + PrintToFile = 0x0001, + PrintSelection = 0x0002, + PrintPageRange = 0x0004, + PrintShowPageSize = 0x0008, + PrintCollateCopies = 0x0010, + DontUseSheet = 0x0020 + }; + + Q_DECLARE_FLAGS(PrintDialogOptions, PrintDialogOption) + +#ifndef QT_NO_PRINTDIALOG + explicit QAbstractPrintDialog(QPrinter *printer, QWidget *parent = 0); + ~QAbstractPrintDialog(); + + virtual int exec() = 0; + + // obsolete + void addEnabledOption(PrintDialogOption option); + void setEnabledOptions(PrintDialogOptions options); + PrintDialogOptions enabledOptions() const; + bool isOptionEnabled(PrintDialogOption option) const; + + void setOptionTabs(const QList<QWidget*> &tabs); + + void setPrintRange(PrintRange range); + PrintRange printRange() const; + + void setMinMax(int min, int max); + int minPage() const; + int maxPage() const; + + void setFromTo(int fromPage, int toPage); + int fromPage() const; + int toPage() const; + + QPrinter *printer() const; + +protected: + QAbstractPrintDialog(QAbstractPrintDialogPrivate &ptr, QPrinter *printer, QWidget *parent = 0); + +private: + Q_DISABLE_COPY(QAbstractPrintDialog) + +#endif // QT_NO_PRINTDIALOG +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractPrintDialog::PrintDialogOptions) + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QABSTRACTPRINTDIALOG_H diff --git a/src/gui/dialogs/qabstractprintdialog_p.h b/src/gui/dialogs/qabstractprintdialog_p.h new file mode 100644 index 0000000000..6bbe5a631b --- /dev/null +++ b/src/gui/dialogs/qabstractprintdialog_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTPRINTDIALOG_P_H +#define QABSTRACTPRINTDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qdialog_p.h" + +#ifndef QT_NO_PRINTDIALOG + +#include "QtGui/qabstractprintdialog.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PRINTER + +class QPrinter; +class QPrinterPrivate; + +class QAbstractPrintDialogPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QAbstractPrintDialog) + +public: + QAbstractPrintDialogPrivate() + : printer(0), pd(0), ownsPrinter(false) + { + } + + QPrinter *printer; + QPrinterPrivate *pd; + bool ownsPrinter; + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; + + virtual void setTabs(const QList<QWidget *> &) {} + void setPrinter(QPrinter *newPrinter); +}; + +#endif //QT_NO_PRINTER + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTDIALOG + +#endif // QABSTRACTPRINTDIALOG_P_H diff --git a/src/gui/dialogs/qcolordialog.cpp b/src/gui/dialogs/qcolordialog.cpp new file mode 100644 index 0000000000..b744dca1c8 --- /dev/null +++ b/src/gui/dialogs/qcolordialog.cpp @@ -0,0 +1,1903 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcolordialog_p.h" + +#ifndef QT_NO_COLORDIALOG + +#include "qapplication.h" +#include "qdesktopwidget.h" +#include "qdrawutil.h" +#include "qevent.h" +#include "qimage.h" +#include "qlabel.h" +#include "qlayout.h" +#include "qlineedit.h" +#include "qmenu.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qpushbutton.h" +#include "qsettings.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qvalidator.h" +#include "qmime.h" +#include "qspinbox.h" +#include "qdialogbuttonbox.h" + +QT_BEGIN_NAMESPACE + +//////////// QWellArray BEGIN + +struct QWellArrayData; + +class QWellArray : public QWidget +{ + Q_OBJECT + Q_PROPERTY(int selectedColumn READ selectedColumn) + Q_PROPERTY(int selectedRow READ selectedRow) + +public: + QWellArray(int rows, int cols, QWidget* parent=0); + ~QWellArray() {} + QString cellContent(int row, int col) const; + + int selectedColumn() const { return selCol; } + int selectedRow() const { return selRow; } + + virtual void setCurrent(int row, int col); + virtual void setSelected(int row, int col); + + QSize sizeHint() const; + + virtual void setCellBrush(int row, int col, const QBrush &); + QBrush cellBrush(int row, int col); + + inline int cellWidth() const + { return cellw; } + + inline int cellHeight() const + { return cellh; } + + inline int rowAt(int y) const + { return y / cellh; } + + inline int columnAt(int x) const + { if (isRightToLeft()) return ncols - (x / cellw) - 1; return x / cellw; } + + inline int rowY(int row) const + { return cellh * row; } + + inline int columnX(int column) const + { if (isRightToLeft()) return cellw * (ncols - column - 1); return cellw * column; } + + inline int numRows() const + { return nrows; } + + inline int numCols() const + {return ncols; } + + inline QRect cellRect() const + { return QRect(0, 0, cellw, cellh); } + + inline QSize gridSize() const + { return QSize(ncols * cellw, nrows * cellh); } + + QRect cellGeometry(int row, int column) + { + QRect r; + if (row >= 0 && row < nrows && column >= 0 && column < ncols) + r.setRect(columnX(column), rowY(row), cellw, cellh); + return r; + } + + inline void updateCell(int row, int column) { update(cellGeometry(row, column)); } + +signals: + void selected(int row, int col); + +protected: + virtual void paintCell(QPainter *, int row, int col, const QRect&); + virtual void paintCellContents(QPainter *, int row, int col, const QRect&); + + void mousePressEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void keyPressEvent(QKeyEvent*); + void focusInEvent(QFocusEvent*); + void focusOutEvent(QFocusEvent*); + void paintEvent(QPaintEvent *); + +private: + Q_DISABLE_COPY(QWellArray) + + int nrows; + int ncols; + int cellw; + int cellh; + int curRow; + int curCol; + int selRow; + int selCol; + QWellArrayData *d; +}; + +void QWellArray::paintEvent(QPaintEvent *e) +{ + QRect r = e->rect(); + int cx = r.x(); + int cy = r.y(); + int ch = r.height(); + int cw = r.width(); + int colfirst = columnAt(cx); + int collast = columnAt(cx + cw); + int rowfirst = rowAt(cy); + int rowlast = rowAt(cy + ch); + + if (isRightToLeft()) { + int t = colfirst; + colfirst = collast; + collast = t; + } + + QPainter painter(this); + QPainter *p = &painter; + QRect rect(0, 0, cellWidth(), cellHeight()); + + + if (collast < 0 || collast >= ncols) + collast = ncols-1; + if (rowlast < 0 || rowlast >= nrows) + rowlast = nrows-1; + + // Go through the rows + for (int r = rowfirst; r <= rowlast; ++r) { + // get row position and height + int rowp = rowY(r); + + // Go through the columns in the row r + // if we know from where to where, go through [colfirst, collast], + // else go through all of them + for (int c = colfirst; c <= collast; ++c) { + // get position and width of column c + int colp = columnX(c); + // Translate painter and draw the cell + rect.translate(colp, rowp); + paintCell(p, r, c, rect); + rect.translate(-colp, -rowp); + } + } +} + +struct QWellArrayData { + QBrush *brush; +}; + +QWellArray::QWellArray(int rows, int cols, QWidget *parent) + : QWidget(parent) + ,nrows(rows), ncols(cols) +{ + d = 0; + setFocusPolicy(Qt::StrongFocus); + cellw = 28; + cellh = 24; + curCol = 0; + curRow = 0; + selCol = -1; + selRow = -1; +} + +QSize QWellArray::sizeHint() const +{ + ensurePolished(); + return gridSize().boundedTo(QSize(640, 480)); +} + + +void QWellArray::paintCell(QPainter* p, int row, int col, const QRect &rect) +{ + int b = 3; //margin + + const QPalette & g = palette(); + QStyleOptionFrame opt; + int dfw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + opt.lineWidth = dfw; + opt.midLineWidth = 1; + opt.rect = rect.adjusted(b, b, -b, -b); + opt.palette = g; + opt.state = QStyle::State_Enabled | QStyle::State_Sunken; + style()->drawPrimitive(QStyle::PE_Frame, &opt, p, this); + b += dfw; + + if ((row == curRow) && (col == curCol)) { + if (hasFocus()) { + QStyleOptionFocusRect opt; + opt.palette = g; + opt.rect = rect; + opt.state = QStyle::State_None | QStyle::State_KeyboardFocusChange; + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this); + } + } + paintCellContents(p, row, col, opt.rect.adjusted(dfw, dfw, -dfw, -dfw)); +} + +/*! + Reimplement this function to change the contents of the well array. + */ +void QWellArray::paintCellContents(QPainter *p, int row, int col, const QRect &r) +{ + if (d) { + p->fillRect(r, d->brush[row*numCols()+col]); + } else { + p->fillRect(r, Qt::white); + p->setPen(Qt::black); + p->drawLine(r.topLeft(), r.bottomRight()); + p->drawLine(r.topRight(), r.bottomLeft()); + } +} + +void QWellArray::mousePressEvent(QMouseEvent *e) +{ + // The current cell marker is set to the cell the mouse is pressed in + QPoint pos = e->pos(); + setCurrent(rowAt(pos.y()), columnAt(pos.x())); +} + +void QWellArray::mouseReleaseEvent(QMouseEvent * /* event */) +{ + // The current cell marker is set to the cell the mouse is clicked in + setSelected(curRow, curCol); +} + + +/* + Sets the cell currently having the focus. This is not necessarily + the same as the currently selected cell. +*/ + +void QWellArray::setCurrent(int row, int col) +{ + if ((curRow == row) && (curCol == col)) + return; + + if (row < 0 || col < 0) + row = col = -1; + + int oldRow = curRow; + int oldCol = curCol; + + curRow = row; + curCol = col; + + updateCell(oldRow, oldCol); + updateCell(curRow, curCol); +} + +/* + Sets the currently selected cell to \a row, \a column. If \a row or + \a column are less than zero, the current cell is unselected. + + Does not set the position of the focus indicator. +*/ +void QWellArray::setSelected(int row, int col) +{ + int oldRow = selRow; + int oldCol = selCol; + + if (row < 0 || col < 0) + row = col = -1; + + selCol = col; + selRow = row; + + updateCell(oldRow, oldCol); + updateCell(selRow, selCol); + if (row >= 0) + emit selected(row, col); + +#ifndef QT_NO_MENU + if (isVisible() && qobject_cast<QMenu*>(parentWidget())) + parentWidget()->close(); +#endif +} + +void QWellArray::focusInEvent(QFocusEvent*) +{ + updateCell(curRow, curCol); +} + +void QWellArray::setCellBrush(int row, int col, const QBrush &b) +{ + if (!d) { + d = new QWellArrayData; + int i = numRows()*numCols(); + d->brush = new QBrush[i]; + } + if (row >= 0 && row < numRows() && col >= 0 && col < numCols()) + d->brush[row*numCols()+col] = b; +} + +/* + Returns the brush set for the cell at \a row, \a column. If no brush is + set, Qt::NoBrush is returned. +*/ + +QBrush QWellArray::cellBrush(int row, int col) +{ + if (d && row >= 0 && row < numRows() && col >= 0 && col < numCols()) + return d->brush[row*numCols()+col]; + return Qt::NoBrush; +} + + + +/*!\reimp +*/ + +void QWellArray::focusOutEvent(QFocusEvent*) +{ + updateCell(curRow, curCol); +} + +/*\reimp +*/ +void QWellArray::keyPressEvent(QKeyEvent* e) +{ + switch(e->key()) { // Look at the key code + case Qt::Key_Left: // If 'left arrow'-key, + if(curCol > 0) // and cr't not in leftmost col + setCurrent(curRow, curCol - 1); // set cr't to next left column + break; + case Qt::Key_Right: // Correspondingly... + if(curCol < numCols()-1) + setCurrent(curRow, curCol + 1); + break; + case Qt::Key_Up: + if(curRow > 0) + setCurrent(curRow - 1, curCol); + break; + case Qt::Key_Down: + if(curRow < numRows()-1) + setCurrent(curRow + 1, curCol); + break; +#if 0 + // bad idea that shouldn't have been implemented; very counterintuitive + case Qt::Key_Return: + case Qt::Key_Enter: + /* + ignore the key, so that the dialog get it, but still select + the current row/col + */ + e->ignore(); + // fallthrough intended +#endif + case Qt::Key_Space: + setSelected(curRow, curCol); + break; + default: // If not an interesting key, + e->ignore(); // we don't accept the event + return; + } + +} + +//////////// QWellArray END + +static bool initrgb = false; +static QRgb stdrgb[6*8]; +static QRgb cusrgb[2*8]; +static bool customSet = false; + + +static void initRGB() +{ + if (initrgb) + return; + initrgb = true; + int i = 0; + for (int g = 0; g < 4; g++) + for (int r = 0; r < 4; r++) + for (int b = 0; b < 3; b++) + stdrgb[i++] = qRgb(r * 255 / 3, g * 255 / 3, b * 255 / 2); + + for (i = 0; i < 2*8; i++) + cusrgb[i] = 0xffffffff; +} + +/*! + Returns the number of custom colors supported by QColorDialog. All + color dialogs share the same custom colors. +*/ +int QColorDialog::customCount() +{ + return 2 * 8; +} + +/*! + \since 4.5 + + Returns the custom color at the given \a index as a QRgb value. +*/ +QRgb QColorDialog::customColor(int index) +{ + if (uint(index) >= uint(customCount())) + return qRgb(255, 255, 255); + initRGB(); + return cusrgb[index]; +} + +/*! + Sets the custom color at \a index to the QRgb \a color value. + + \note This function does not apply to the Native Color Dialog on the Mac + OS X platform. If you still require this function, use the + QColorDialog::DontUseNativeDialog option. +*/ +void QColorDialog::setCustomColor(int index, QRgb color) +{ + if (uint(index) >= uint(customCount())) + return; + initRGB(); + customSet = true; + cusrgb[index] = color; +} + +/*! + Sets the standard color at \a index to the QRgb \a color value. + + \note This function does not apply to the Native Color Dialog on the Mac + OS X platform. If you still require this function, use the + QColorDialog::DontUseNativeDialog option. +*/ + +void QColorDialog::setStandardColor(int index, QRgb color) +{ + if (uint(index) >= uint(6 * 8)) + return; + initRGB(); + stdrgb[index] = color; +} + +static inline void rgb2hsv(QRgb rgb, int &h, int &s, int &v) +{ + QColor c; + c.setRgb(rgb); + c.getHsv(&h, &s, &v); +} + +class QColorWell : public QWellArray +{ +public: + QColorWell(QWidget *parent, int r, int c, QRgb *vals) + :QWellArray(r, c, parent), values(vals), mousePressed(false), oldCurrent(-1, -1) + { setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); } + +protected: + void paintCellContents(QPainter *, int row, int col, const QRect&); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *e); + void dragLeaveEvent(QDragLeaveEvent *e); + void dragMoveEvent(QDragMoveEvent *e); + void dropEvent(QDropEvent *e); +#endif + +private: + QRgb *values; + bool mousePressed; + QPoint pressPos; + QPoint oldCurrent; + +}; + +void QColorWell::paintCellContents(QPainter *p, int row, int col, const QRect &r) +{ + int i = row + col*numRows(); + p->fillRect(r, QColor(values[i])); +} + +void QColorWell::mousePressEvent(QMouseEvent *e) +{ + oldCurrent = QPoint(selectedRow(), selectedColumn()); + QWellArray::mousePressEvent(e); + mousePressed = true; + pressPos = e->pos(); +} + +void QColorWell::mouseMoveEvent(QMouseEvent *e) +{ + QWellArray::mouseMoveEvent(e); +#ifndef QT_NO_DRAGANDDROP + if (!mousePressed) + return; + if ((pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) { + setCurrent(oldCurrent.x(), oldCurrent.y()); + int i = rowAt(pressPos.y()) + columnAt(pressPos.x()) * numRows(); + QColor col(values[i]); + QMimeData *mime = new QMimeData; + mime->setColorData(col); + QPixmap pix(cellWidth(), cellHeight()); + pix.fill(col); + QPainter p(&pix); + p.drawRect(0, 0, pix.width() - 1, pix.height() - 1); + p.end(); + QDrag *drg = new QDrag(this); + drg->setMimeData(mime); + drg->setPixmap(pix); + mousePressed = false; + drg->start(); + } +#endif +} + +#ifndef QT_NO_DRAGANDDROP +void QColorWell::dragEnterEvent(QDragEnterEvent *e) +{ + if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid()) + e->accept(); + else + e->ignore(); +} + +void QColorWell::dragLeaveEvent(QDragLeaveEvent *) +{ + if (hasFocus()) + parentWidget()->setFocus(); +} + +void QColorWell::dragMoveEvent(QDragMoveEvent *e) +{ + if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid()) { + setCurrent(rowAt(e->pos().y()), columnAt(e->pos().x())); + e->accept(); + } else { + e->ignore(); + } +} + +void QColorWell::dropEvent(QDropEvent *e) +{ + QColor col = qvariant_cast<QColor>(e->mimeData()->colorData()); + if (col.isValid()) { + int i = rowAt(e->pos().y()) + columnAt(e->pos().x()) * numRows(); + values[i] = col.rgb(); + update(); + e->accept(); + } else { + e->ignore(); + } +} + +#endif // QT_NO_DRAGANDDROP + +void QColorWell::mouseReleaseEvent(QMouseEvent *e) +{ + if (!mousePressed) + return; + QWellArray::mouseReleaseEvent(e); + mousePressed = false; +} + +class QColorPicker : public QFrame +{ + Q_OBJECT +public: + QColorPicker(QWidget* parent); + ~QColorPicker(); + +public slots: + void setCol(int h, int s); + +signals: + void newCol(int h, int s); + +protected: + QSize sizeHint() const; + void paintEvent(QPaintEvent*); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + +private: + int hue; + int sat; + + QPoint colPt(); + int huePt(const QPoint &pt); + int satPt(const QPoint &pt); + void setCol(const QPoint &pt); + + QPixmap *pix; +}; + +static int pWidth = 220; +static int pHeight = 200; + +class QColorLuminancePicker : public QWidget +{ + Q_OBJECT +public: + QColorLuminancePicker(QWidget* parent=0); + ~QColorLuminancePicker(); + +public slots: + void setCol(int h, int s, int v); + void setCol(int h, int s); + +signals: + void newHsv(int h, int s, int v); + +protected: + void paintEvent(QPaintEvent*); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + +private: + enum { foff = 3, coff = 4 }; //frame and contents offset + int val; + int hue; + int sat; + + int y2val(int y); + int val2y(int val); + void setVal(int v); + + QPixmap *pix; +}; + + +int QColorLuminancePicker::y2val(int y) +{ + int d = height() - 2*coff - 1; + return 255 - (y - coff)*255/d; +} + +int QColorLuminancePicker::val2y(int v) +{ + int d = height() - 2*coff - 1; + return coff + (255-v)*d/255; +} + +QColorLuminancePicker::QColorLuminancePicker(QWidget* parent) + :QWidget(parent) +{ + hue = 100; val = 100; sat = 100; + pix = 0; + // setAttribute(WA_NoErase, true); +} + +QColorLuminancePicker::~QColorLuminancePicker() +{ + delete pix; +} + +void QColorLuminancePicker::mouseMoveEvent(QMouseEvent *m) +{ + setVal(y2val(m->y())); +} +void QColorLuminancePicker::mousePressEvent(QMouseEvent *m) +{ + setVal(y2val(m->y())); +} + +void QColorLuminancePicker::setVal(int v) +{ + if (val == v) + return; + val = qMax(0, qMin(v,255)); + delete pix; pix=0; + repaint(); + emit newHsv(hue, sat, val); +} + +//receives from a hue,sat chooser and relays. +void QColorLuminancePicker::setCol(int h, int s) +{ + setCol(h, s, val); + emit newHsv(h, s, val); +} + +void QColorLuminancePicker::paintEvent(QPaintEvent *) +{ + int w = width() - 5; + + QRect r(0, foff, w, height() - 2*foff); + int wi = r.width() - 2; + int hi = r.height() - 2; + if (!pix || pix->height() != hi || pix->width() != wi) { + delete pix; + QImage img(wi, hi, QImage::Format_RGB32); + int y; + uint *pixel = (uint *) img.scanLine(0); + for (y = 0; y < hi; y++) { + const uint *end = pixel + wi; + while (pixel < end) { + QColor c; + c.setHsv(hue, sat, y2val(y+coff)); + *pixel = c.rgb(); + ++pixel; + } + } + pix = new QPixmap(QPixmap::fromImage(img)); + } + QPainter p(this); + p.drawPixmap(1, coff, *pix); + const QPalette &g = palette(); + qDrawShadePanel(&p, r, g, true); + p.setPen(g.foreground().color()); + p.setBrush(g.foreground()); + QPolygon a; + int y = val2y(val); + a.setPoints(3, w, y, w+5, y+5, w+5, y-5); + p.eraseRect(w, 0, 5, height()); + p.drawPolygon(a); +} + +void QColorLuminancePicker::setCol(int h, int s , int v) +{ + val = v; + hue = h; + sat = s; + delete pix; pix=0; + repaint(); +} + +QPoint QColorPicker::colPt() +{ return QPoint((360-hue)*(pWidth-1)/360, (255-sat)*(pHeight-1)/255); } +int QColorPicker::huePt(const QPoint &pt) +{ return 360 - pt.x()*360/(pWidth-1); } +int QColorPicker::satPt(const QPoint &pt) +{ return 255 - pt.y()*255/(pHeight-1) ; } +void QColorPicker::setCol(const QPoint &pt) +{ setCol(huePt(pt), satPt(pt)); } + +QColorPicker::QColorPicker(QWidget* parent) + : QFrame(parent) +{ + hue = 0; sat = 0; + setCol(150, 255); + + QImage img(pWidth, pHeight, QImage::Format_RGB32); + int x, y; + uint *pixel = (uint *) img.scanLine(0); + for (y = 0; y < pHeight; y++) { + const uint *end = pixel + pWidth; + x = 0; + while (pixel < end) { + QPoint p(x, y); + QColor c; + c.setHsv(huePt(p), satPt(p), 200); + *pixel = c.rgb(); + ++pixel; + ++x; + } + } + pix = new QPixmap(QPixmap::fromImage(img)); + setAttribute(Qt::WA_NoSystemBackground); + setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) ); +} + +QColorPicker::~QColorPicker() +{ + delete pix; +} + +QSize QColorPicker::sizeHint() const +{ + return QSize(pWidth + 2*frameWidth(), pHeight + 2*frameWidth()); +} + +void QColorPicker::setCol(int h, int s) +{ + int nhue = qMin(qMax(0,h), 359); + int nsat = qMin(qMax(0,s), 255); + if (nhue == hue && nsat == sat) + return; + + QRect r(colPt(), QSize(20,20)); + hue = nhue; sat = nsat; + r = r.united(QRect(colPt(), QSize(20,20))); + r.translate(contentsRect().x()-9, contentsRect().y()-9); + // update(r); + repaint(r); +} + +void QColorPicker::mouseMoveEvent(QMouseEvent *m) +{ + QPoint p = m->pos() - contentsRect().topLeft(); + setCol(p); + emit newCol(hue, sat); +} + +void QColorPicker::mousePressEvent(QMouseEvent *m) +{ + QPoint p = m->pos() - contentsRect().topLeft(); + setCol(p); + emit newCol(hue, sat); +} + +void QColorPicker::paintEvent(QPaintEvent* ) +{ + QPainter p(this); + drawFrame(&p); + QRect r = contentsRect(); + + p.drawPixmap(r.topLeft(), *pix); + QPoint pt = colPt() + r.topLeft(); + p.setPen(Qt::black); + + p.fillRect(pt.x()-9, pt.y(), 20, 2, Qt::black); + p.fillRect(pt.x(), pt.y()-9, 2, 20, Qt::black); + +} + +class QColSpinBox : public QSpinBox +{ +public: + QColSpinBox(QWidget *parent) + : QSpinBox(parent) { setRange(0, 255); } + void setValue(int i) { + bool block = signalsBlocked(); + blockSignals(true); + QSpinBox::setValue(i); + blockSignals(block); + } +}; + +class QColorShowLabel; + +class QColorShower : public QWidget +{ + Q_OBJECT +public: + QColorShower(QColorDialog *parent); + + //things that don't emit signals + void setHsv(int h, int s, int v); + + int currentAlpha() const + { return (colorDialog->options() & QColorDialog::ShowAlphaChannel) ? alphaEd->value() : 255; } + void setCurrentAlpha(int a) { alphaEd->setValue(a); rgbEd(); } + void showAlpha(bool b); + bool isAlphaVisible() const; + + QRgb currentColor() const { return curCol; } + QColor currentQColor() const { return curQColor; } + void retranslateStrings(); + void updateQColor(); + +public slots: + void setRgb(QRgb rgb); + +signals: + void newCol(QRgb rgb); + void currentColorChanged(const QColor &color); + +private slots: + void rgbEd(); + void hsvEd(); +private: + void showCurrentColor(); + int hue, sat, val; + QRgb curCol; + QColor curQColor; + QLabel *lblHue; + QLabel *lblSat; + QLabel *lblVal; + QLabel *lblRed; + QLabel *lblGreen; + QLabel *lblBlue; + QColSpinBox *hEd; + QColSpinBox *sEd; + QColSpinBox *vEd; + QColSpinBox *rEd; + QColSpinBox *gEd; + QColSpinBox *bEd; + QColSpinBox *alphaEd; + QLabel *alphaLab; + QColorShowLabel *lab; + bool rgbOriginal; + QColorDialog *colorDialog; + + friend class QColorDialog; + friend class QColorDialogPrivate; +}; + +class QColorShowLabel : public QFrame +{ + Q_OBJECT + +public: + QColorShowLabel(QWidget *parent) : QFrame(parent) { + setFrameStyle(QFrame::Panel|QFrame::Sunken); + setAcceptDrops(true); + mousePressed = false; + } + void setColor(QColor c) { col = c; } + +signals: + void colorDropped(QRgb); + +protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *e); + void dragLeaveEvent(QDragLeaveEvent *e); + void dropEvent(QDropEvent *e); +#endif + +private: + QColor col; + bool mousePressed; + QPoint pressPos; +}; + +void QColorShowLabel::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + drawFrame(&p); + p.fillRect(contentsRect()&e->rect(), col); +} + +void QColorShower::showAlpha(bool b) +{ + alphaLab->setVisible(b); + alphaEd->setVisible(b); +} + +inline bool QColorShower::isAlphaVisible() const +{ + return alphaLab->isVisible(); +} + +void QColorShowLabel::mousePressEvent(QMouseEvent *e) +{ + mousePressed = true; + pressPos = e->pos(); +} + +void QColorShowLabel::mouseMoveEvent(QMouseEvent *e) +{ +#ifdef QT_NO_DRAGANDDROP + Q_UNUSED(e); +#else + if (!mousePressed) + return; + if ((pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) { + QMimeData *mime = new QMimeData; + mime->setColorData(col); + QPixmap pix(30, 20); + pix.fill(col); + QPainter p(&pix); + p.drawRect(0, 0, pix.width() - 1, pix.height() - 1); + p.end(); + QDrag *drg = new QDrag(this); + drg->setMimeData(mime); + drg->setPixmap(pix); + mousePressed = false; + drg->start(); + } +#endif +} + +#ifndef QT_NO_DRAGANDDROP +void QColorShowLabel::dragEnterEvent(QDragEnterEvent *e) +{ + if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid()) + e->accept(); + else + e->ignore(); +} + +void QColorShowLabel::dragLeaveEvent(QDragLeaveEvent *) +{ +} + +void QColorShowLabel::dropEvent(QDropEvent *e) +{ + QColor color = qvariant_cast<QColor>(e->mimeData()->colorData()); + if (color.isValid()) { + col = color; + repaint(); + emit colorDropped(col.rgb()); + e->accept(); + } else { + e->ignore(); + } +} +#endif // QT_NO_DRAGANDDROP + +void QColorShowLabel::mouseReleaseEvent(QMouseEvent *) +{ + if (!mousePressed) + return; + mousePressed = false; +} + +QColorShower::QColorShower(QColorDialog *parent) + : QWidget(parent) +{ + colorDialog = parent; + + curCol = qRgb(255, 255, 255); + curQColor = Qt::white; + + QGridLayout *gl = new QGridLayout(this); + gl->setMargin(gl->spacing()); + lab = new QColorShowLabel(this); +#ifndef Q_OS_WINCE + lab->setMinimumWidth(60); +#else + lab->setMinimumWidth(20); +#endif + gl->addWidget(lab, 0, 0, -1, 1); + connect(lab, SIGNAL(colorDropped(QRgb)), this, SIGNAL(newCol(QRgb))); + connect(lab, SIGNAL(colorDropped(QRgb)), this, SLOT(setRgb(QRgb))); + + hEd = new QColSpinBox(this); + hEd->setRange(0, 359); + lblHue = new QLabel(this); +#ifndef QT_NO_SHORTCUT + lblHue->setBuddy(hEd); +#endif + lblHue->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + gl->addWidget(lblHue, 0, 1); + gl->addWidget(hEd, 0, 2); + + sEd = new QColSpinBox(this); + lblSat = new QLabel(this); +#ifndef QT_NO_SHORTCUT + lblSat->setBuddy(sEd); +#endif + lblSat->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + gl->addWidget(lblSat, 1, 1); + gl->addWidget(sEd, 1, 2); + + vEd = new QColSpinBox(this); + lblVal = new QLabel(this); +#ifndef QT_NO_SHORTCUT + lblVal->setBuddy(vEd); +#endif + lblVal->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + gl->addWidget(lblVal, 2, 1); + gl->addWidget(vEd, 2, 2); + + rEd = new QColSpinBox(this); + lblRed = new QLabel(this); +#ifndef QT_NO_SHORTCUT + lblRed->setBuddy(rEd); +#endif + lblRed->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + gl->addWidget(lblRed, 0, 3); + gl->addWidget(rEd, 0, 4); + + gEd = new QColSpinBox(this); + lblGreen = new QLabel(this); +#ifndef QT_NO_SHORTCUT + lblGreen->setBuddy(gEd); +#endif + lblGreen->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + gl->addWidget(lblGreen, 1, 3); + gl->addWidget(gEd, 1, 4); + + bEd = new QColSpinBox(this); + lblBlue = new QLabel(this); +#ifndef QT_NO_SHORTCUT + lblBlue->setBuddy(bEd); +#endif + lblBlue->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + gl->addWidget(lblBlue, 2, 3); + gl->addWidget(bEd, 2, 4); + + alphaEd = new QColSpinBox(this); + alphaLab = new QLabel(this); +#ifndef QT_NO_SHORTCUT + alphaLab->setBuddy(alphaEd); +#endif + alphaLab->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + gl->addWidget(alphaLab, 3, 1, 1, 3); + gl->addWidget(alphaEd, 3, 4); + alphaEd->hide(); + alphaLab->hide(); + + connect(hEd, SIGNAL(valueChanged(int)), this, SLOT(hsvEd())); + connect(sEd, SIGNAL(valueChanged(int)), this, SLOT(hsvEd())); + connect(vEd, SIGNAL(valueChanged(int)), this, SLOT(hsvEd())); + + connect(rEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd())); + connect(gEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd())); + connect(bEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd())); + connect(alphaEd, SIGNAL(valueChanged(int)), this, SLOT(rgbEd())); + + retranslateStrings(); +} + +inline QRgb QColorDialogPrivate::currentColor() const { return cs->currentColor(); } +inline int QColorDialogPrivate::currentAlpha() const { return cs->currentAlpha(); } +inline void QColorDialogPrivate::setCurrentAlpha(int a) { cs->setCurrentAlpha(a); } +inline void QColorDialogPrivate::showAlpha(bool b) { cs->showAlpha(b); } +inline bool QColorDialogPrivate::isAlphaVisible() const { return cs->isAlphaVisible(); } + +QColor QColorDialogPrivate::currentQColor() const +{ + return cs->currentQColor(); +} + +void QColorShower::showCurrentColor() +{ + lab->setColor(currentColor()); + lab->repaint(); +} + +void QColorShower::rgbEd() +{ + rgbOriginal = true; + curCol = qRgba(rEd->value(), gEd->value(), bEd->value(), currentAlpha()); + + rgb2hsv(currentColor(), hue, sat, val); + + hEd->setValue(hue); + sEd->setValue(sat); + vEd->setValue(val); + + showCurrentColor(); + emit newCol(currentColor()); + updateQColor(); +} + +void QColorShower::hsvEd() +{ + rgbOriginal = false; + hue = hEd->value(); + sat = sEd->value(); + val = vEd->value(); + + QColor c; + c.setHsv(hue, sat, val); + curCol = c.rgb(); + + rEd->setValue(qRed(currentColor())); + gEd->setValue(qGreen(currentColor())); + bEd->setValue(qBlue(currentColor())); + + showCurrentColor(); + emit newCol(currentColor()); + updateQColor(); +} + +void QColorShower::setRgb(QRgb rgb) +{ + rgbOriginal = true; + curCol = rgb; + + rgb2hsv(currentColor(), hue, sat, val); + + hEd->setValue(hue); + sEd->setValue(sat); + vEd->setValue(val); + + rEd->setValue(qRed(currentColor())); + gEd->setValue(qGreen(currentColor())); + bEd->setValue(qBlue(currentColor())); + + showCurrentColor(); + updateQColor(); +} + +void QColorShower::setHsv(int h, int s, int v) +{ + if (h < -1 || (uint)s > 255 || (uint)v > 255) + return; + + rgbOriginal = false; + hue = h; val = v; sat = s; + QColor c; + c.setHsv(hue, sat, val); + curCol = c.rgb(); + + hEd->setValue(hue); + sEd->setValue(sat); + vEd->setValue(val); + + rEd->setValue(qRed(currentColor())); + gEd->setValue(qGreen(currentColor())); + bEd->setValue(qBlue(currentColor())); + + showCurrentColor(); + updateQColor(); +} + +void QColorShower::retranslateStrings() +{ + lblHue->setText(QColorDialog::tr("Hu&e:")); + lblSat->setText(QColorDialog::tr("&Sat:")); + lblVal->setText(QColorDialog::tr("&Val:")); + lblRed->setText(QColorDialog::tr("&Red:")); + lblGreen->setText(QColorDialog::tr("&Green:")); + lblBlue->setText(QColorDialog::tr("Bl&ue:")); + alphaLab->setText(QColorDialog::tr("A&lpha channel:")); +} + +void QColorShower::updateQColor() +{ + QColor oldQColor(curQColor); + curQColor.setRgba(qRgba(qRed(curCol), qGreen(curCol), qBlue(curCol), qAlpha(curCol))); + if (curQColor != oldQColor) + emit currentColorChanged(curQColor); +} + +//sets all widgets to display h,s,v +void QColorDialogPrivate::_q_newHsv(int h, int s, int v) +{ + cs->setHsv(h, s, v); + cp->setCol(h, s); + lp->setCol(h, s, v); +} + +//sets all widgets to display rgb +void QColorDialogPrivate::setCurrentColor(QRgb rgb) +{ + cs->setRgb(rgb); + _q_newColorTypedIn(rgb); +} + +// hack; doesn't keep curCol in sync, so use with care +void QColorDialogPrivate::setCurrentQColor(const QColor &color) +{ + Q_Q(QColorDialog); + if (cs->curQColor != color) { + cs->curQColor = color; + emit q->currentColorChanged(color); + } +} + +bool QColorDialogPrivate::selectColor(const QColor &col) +{ + QRgb color = col.rgb(); + int i = 0, j = 0; + // Check standard colors + if (standard) { + for (i = 0; i < 6; i++) { + for (j = 0; j < 8; j++) { + if (color == stdrgb[i + j*6]) { + _q_newStandard(i, j); + standard->setCurrent(i, j); + standard->setSelected(i, j); + standard->setFocus(); + return true; + } + } + } + } + // Check custom colors + if (custom) { + for (i = 0; i < 2; i++) { + for (j = 0; j < 8; j++) { + if (color == cusrgb[i + j*2]) { + _q_newCustom(i, j); + custom->setCurrent(i, j); + custom->setSelected(i, j); + custom->setFocus(); + return true; + } + } + } + } + return false; +} + +//sets all widgets except cs to display rgb +void QColorDialogPrivate::_q_newColorTypedIn(QRgb rgb) +{ + int h, s, v; + rgb2hsv(rgb, h, s, v); + cp->setCol(h, s); + lp->setCol(h, s, v); +} + +void QColorDialogPrivate::_q_newCustom(int r, int c) +{ + int i = r+2*c; + setCurrentColor(cusrgb[i]); + nextCust = i; + if (standard) + standard->setSelected(-1,-1); +} + +void QColorDialogPrivate::_q_newStandard(int r, int c) +{ + setCurrentColor(stdrgb[r+c*6]); + if (custom) + custom->setSelected(-1,-1); +} + +void QColorDialogPrivate::init(const QColor &initial) +{ + Q_Q(QColorDialog); + + q->setSizeGripEnabled(false); + q->setWindowTitle(QColorDialog::tr("Select Color")); + + nextCust = 0; + QVBoxLayout *mainLay = new QVBoxLayout(q); + // there's nothing in this dialog that benefits from sizing up + mainLay->setSizeConstraint(QLayout::SetFixedSize); + + QHBoxLayout *topLay = new QHBoxLayout(); + mainLay->addLayout(topLay); + + leftLay = 0; + +#if defined(Q_OS_WINCE) + smallDisplay = true; + const int lumSpace = 20; +#else + // small displays (e.g. PDAs) cannot fit the full color dialog, + // so just use the color picker. + smallDisplay = (qApp->desktop()->width() < 480 || qApp->desktop()->height() < 350); + const int lumSpace = topLay->spacing() / 2; +#endif + + if (!smallDisplay) { + leftLay = new QVBoxLayout; + topLay->addLayout(leftLay); + } + + initRGB(); + +#ifndef QT_NO_SETTINGS + if (!customSet) { + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + for (int i = 0; i < 2*8; ++i) { + QVariant v = settings.value(QLatin1String("Qt/customColors/") + QString::number(i)); + if (v.isValid()) { + QRgb rgb = v.toUInt(); + cusrgb[i] = rgb; + } + } + } +#endif + + if (!smallDisplay) { + standard = new QColorWell(q, 6, 8, stdrgb); + lblBasicColors = new QLabel(q); +#ifndef QT_NO_SHORTCUT + lblBasicColors->setBuddy(standard); +#endif + q->connect(standard, SIGNAL(selected(int,int)), SLOT(_q_newStandard(int,int))); + leftLay->addWidget(lblBasicColors); + leftLay->addWidget(standard); + +#if !defined(Q_OS_WINCE) + leftLay->addStretch(); +#endif + + custom = new QColorWell(q, 2, 8, cusrgb); + custom->setAcceptDrops(true); + + q->connect(custom, SIGNAL(selected(int,int)), SLOT(_q_newCustom(int,int))); + lblCustomColors = new QLabel(q); +#ifndef QT_NO_SHORTCUT + lblCustomColors->setBuddy(custom); +#endif + leftLay->addWidget(lblCustomColors); + leftLay->addWidget(custom); + + addCusBt = new QPushButton(q); + QObject::connect(addCusBt, SIGNAL(clicked()), q, SLOT(_q_addCustom())); + leftLay->addWidget(addCusBt); + } else { + // better color picker size for small displays + pWidth = 150; + pHeight = 100; + custom = 0; + standard = 0; + } + + QVBoxLayout *rightLay = new QVBoxLayout; + topLay->addLayout(rightLay); + + QHBoxLayout *pickLay = new QHBoxLayout; + rightLay->addLayout(pickLay); + + QVBoxLayout *cLay = new QVBoxLayout; + pickLay->addLayout(cLay); + cp = new QColorPicker(q); + + cp->setFrameStyle(QFrame::Panel + QFrame::Sunken); + cLay->addSpacing(lumSpace); + cLay->addWidget(cp); + cLay->addSpacing(lumSpace); + + lp = new QColorLuminancePicker(q); + lp->setFixedWidth(20); + pickLay->addWidget(lp); + + QObject::connect(cp, SIGNAL(newCol(int,int)), lp, SLOT(setCol(int,int))); + QObject::connect(lp, SIGNAL(newHsv(int,int,int)), q, SLOT(_q_newHsv(int,int,int))); + + rightLay->addStretch(); + + cs = new QColorShower(q); + QObject::connect(cs, SIGNAL(newCol(QRgb)), q, SLOT(_q_newColorTypedIn(QRgb))); + QObject::connect(cs, SIGNAL(currentColorChanged(const QColor&)), + q, SIGNAL(currentColorChanged(const QColor&))); + rightLay->addWidget(cs); + + buttons = new QDialogButtonBox(q); + mainLay->addWidget(buttons); + + ok = buttons->addButton(QDialogButtonBox::Ok); + QObject::connect(ok, SIGNAL(clicked()), q, SLOT(accept())); + ok->setDefault(true); + cancel = buttons->addButton(QDialogButtonBox::Cancel); + QObject::connect(cancel, SIGNAL(clicked()), q, SLOT(reject())); + + retranslateStrings(); + +#ifdef Q_WS_MAC + delegate = 0; +#endif + + q->setCurrentColor(initial); +} + +void QColorDialogPrivate::_q_addCustom() +{ + cusrgb[nextCust] = cs->currentColor(); + if (custom) + custom->update(); + nextCust = (nextCust+1) % 16; +} + +void QColorDialogPrivate::retranslateStrings() +{ + if (!smallDisplay) { + lblBasicColors->setText(QColorDialog::tr("&Basic colors")); + lblCustomColors->setText(QColorDialog::tr("&Custom colors")); + addCusBt->setText(QColorDialog::tr("&Add to Custom Colors")); + } + + cs->retranslateStrings(); +} + +static const Qt::WindowFlags DefaultWindowFlags = + Qt::Dialog | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint + | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; + +/*! + \class QColorDialog + \brief The QColorDialog class provides a dialog widget for specifying colors. + + \mainclass + \ingroup dialogs + \ingroup multimedia + + The color dialog's function is to allow users to choose colors. + For example, you might use this in a drawing program to allow the + user to set the brush color. + + The static functions provide modal color dialogs. + \omit + If you require a modeless dialog, use the QColorDialog constructor. + \endomit + + The static getColor() function shows the dialog, and allows the + user to specify a color. The getRgba() function does the same, but + also allows the user to specify a color with an alpha channel + (transparency) value. + + The user can store customCount() different custom colors. The + custom colors are shared by all color dialogs, and remembered + during the execution of the program. Use setCustomColor() to set + the custom colors, and use customColor() to get them. + + Additional widgets that allow users to pick colors are available + as \l{Qt Solutions}. + + The \l{dialogs/standarddialogs}{Standard Dialogs} example shows + how to use QColorDialog as well as other built-in Qt dialogs. + + \image plastique-colordialog.png A color dialog in the Plastique widget style. + + \sa QColor, QFileDialog, QPrintDialog, QFontDialog, {Standard Dialogs Example} +*/ + +/*! + \since 4.5 + + Constructs a color dialog with the given \a parent. +*/ +QColorDialog::QColorDialog(QWidget *parent) + : QDialog(*new QColorDialogPrivate, parent, DefaultWindowFlags) +{ + Q_D(QColorDialog); + d->init(Qt::white); +} + +/*! + \since 4.5 + + Constructs a color dialog with the given \a parent and specified + \a initial color. +*/ +QColorDialog::QColorDialog(const QColor &initial, QWidget *parent) + : QDialog(*new QColorDialogPrivate, parent, DefaultWindowFlags) +{ + Q_D(QColorDialog); + d->init(initial); +} + +/*! + \property QColorDialog::currentColor + \brief the currently selected color in the dialog +*/ + +void QColorDialog::setCurrentColor(const QColor &color) +{ + Q_D(QColorDialog); + d->setCurrentColor(color.rgb()); + d->selectColor(color.rgb()); + d->setCurrentAlpha(color.alpha()); + +#ifdef Q_WS_MAC + if (d->delegate) + QColorDialogPrivate::setColor(d->delegate, color); +#endif +} + +QColor QColorDialog::currentColor() const +{ + Q_D(const QColorDialog); + return d->currentQColor(); +} + + +/*! + Returns the color that the user selected by clicking the \gui{OK} + or equivalent button. + + \note This color is not always the same as the color held by the + \l currentColor property since the user can choose different colors + before finally selecting the one to use. +*/ +QColor QColorDialog::selectedColor() const +{ + Q_D(const QColorDialog); + return d->selectedQColor; +} + +/*! + Sets the given \a option to be enabled if \a on is true; + otherwise, clears the given \a option. + + \sa options, testOption() +*/ +void QColorDialog::setOption(ColorDialogOption option, bool on) +{ + Q_D(QColorDialog); + if (!(d->opts & option) != !on) + setOptions(d->opts ^ option); +} + +/*! + \since 4.5 + + Returns true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption() +*/ +bool QColorDialog::testOption(ColorDialogOption option) const +{ + Q_D(const QColorDialog); + return (d->opts & option) != 0; +} + +/*! + \property QColorDialog::options + \brief the various options that affect the look and feel of the dialog + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the + dialog is visible is not guaranteed to have an immediate effect on the + dialog (depending on the option and on the platform). + + \sa setOption(), testOption() +*/ +void QColorDialog::setOptions(ColorDialogOptions options) +{ + Q_D(QColorDialog); + + ColorDialogOptions changed = (options ^ d->opts); + if (!changed) + return; + + d->opts = options; + d->buttons->setVisible(!(options & NoButtons)); + d->showAlpha(options & ShowAlphaChannel); +} + +QColorDialog::ColorDialogOptions QColorDialog::options() const +{ + Q_D(const QColorDialog); + return d->opts; +} + +/*! + \enum QColorDialog::ColorDialogOption + + \since 4.5 + + This enum specifies various options that affect the look and feel + of a color dialog. + + \value ShowAlphaChannel Allow the user to select the alpha component of a color. + \value NoButtons Don't display \gui{OK} and \gui{Cancel} buttons. (Useful for "live dialogs".) + \value DontUseNativeDialog Use Qt's standard color dialog on the Mac instead of Apple's + native color panel. + + \sa options, setOption(), testOption(), windowModality() +*/ + +/*! + \fn void QColorDialog::currentColorChanged(const QColor &color) + + This signal is emitted whenever the current color changes in the dialog. + The current color is specified by \a color. + + \sa color, colorSelected() +*/ + +#ifdef Q_WS_MAC +// can only have one Cocoa color panel active +bool QColorDialogPrivate::sharedColorPanelAvailable = true; +#endif + +/*! + \fn void QColorDialog::colorSelected(const QColor &color); + + This signal is emitted just after the user has clicked \gui{OK} to + select a color to use. The chosen color is specified by \a color. + + \sa color, currentColorChanged() +*/ + +/*! + Changes the visibility of the dialog. If \a visible is true, the dialog + is shown; otherwise, it is hidden. +*/ +void QColorDialog::setVisible(bool visible) +{ + Q_D(QColorDialog); + + if (visible){ + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) + return; + } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) + return; + + if (visible) + d->selectedQColor = QColor(); + +#if defined(Q_WS_MAC) + if (visible) { + if (!d->delegate && QColorDialogPrivate::sharedColorPanelAvailable && + !(testAttribute(Qt::WA_DontShowOnScreen) || (d->opts & DontUseNativeDialog))){ + d->delegate = QColorDialogPrivate::openCocoaColorPanel( + currentColor(), parentWidget(), windowTitle(), options(), d); + QColorDialogPrivate::sharedColorPanelAvailable = false; + setAttribute(Qt::WA_DontShowOnScreen); + } + setWindowFlags(windowModality() == Qt::WindowModal ? Qt::Sheet : DefaultWindowFlags); + } else { + if (d->delegate) { + QColorDialogPrivate::closeCocoaColorPanel(d->delegate); + d->delegate = 0; + QColorDialogPrivate::sharedColorPanelAvailable = true; + setAttribute(Qt::WA_DontShowOnScreen, false); + } + } +#endif + + QDialog::setVisible(visible); +} + +/*! + \overload + \since 4.5 + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QColorDialog::open(QObject *receiver, const char *member) +{ + Q_D(QColorDialog); + connect(this, SIGNAL(colorSelected(const QColor&)), receiver, member); + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +/*! + \fn QColorDialog::open() + + \since 4.5 + Shows the dialog as a \l{QDialog#Modal Dialogs}{window modal dialog}, + returning immediately. + + \sa QDialog::open() +*/ + +/*! + \since 4.5 + + Pops up a modal color dialog with the given window \a title (or "Select Color" if none is + specified), lets the user choose a color, and returns that color. The color is initially set + to \a initial. The dialog is a child of \a parent. It returns an invalid (see + QColor::isValid()) color if the user cancels the dialog. + + The \a options argument allows you to customize the dialog. +*/ +QColor QColorDialog::getColor(const QColor &initial, QWidget *parent, const QString &title, + ColorDialogOptions options) +{ + QColorDialog dlg(parent); + if (!title.isEmpty()) + dlg.setWindowTitle(title); + dlg.setOptions(options); + dlg.setCurrentColor(initial); + dlg.exec(); + return dlg.selectedColor(); +} + +/*! + Pops up a modal color dialog, lets the user choose a color, and + returns that color. The color is initially set to \a initial. The + dialog is a child of \a parent. It returns an invalid (see + QColor::isValid()) color if the user cancels the dialog. +*/ + +QColor QColorDialog::getColor(const QColor &initial, QWidget *parent) +{ + return getColor(initial, parent, QString(), ColorDialogOptions(0)); +} + + +/*! + \obsolete + + Pops up a modal color dialog to allow the user to choose a color + and an alpha channel (transparency) value. The color+alpha is + initially set to \a initial. The dialog is a child of \a parent. + + If \a ok is non-null, \e *\a ok is set to true if the user clicked + \gui{OK}, and to false if the user clicked Cancel. + + If the user clicks Cancel, the \a initial value is returned. + + Use QColorDialog::getColor() instead, passing the + QColorDialog::ShowAlphaChannel option. +*/ + +QRgb QColorDialog::getRgba(QRgb initial, bool *ok, QWidget *parent) +{ + QColor color(getColor(QColor(initial), parent, QString(), ShowAlphaChannel)); + QRgb result = color.isValid() ? color.rgba() : initial; + if (ok) + *ok = color.isValid(); + return result; +} + +/*! + Destroys the color dialog. +*/ + +QColorDialog::~QColorDialog() +{ +#ifndef QT_NO_SETTINGS + if (!customSet) { + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + for (int i = 0; i < 2*8; ++i) + settings.setValue(QLatin1String("Qt/customColors/") + QString::number(i), cusrgb[i]); + } +#endif +} + + +/*! + \reimp +*/ +void QColorDialog::changeEvent(QEvent *e) +{ + Q_D(QColorDialog); + if (e->type() == QEvent::LanguageChange) + d->retranslateStrings(); + QDialog::changeEvent(e); +} + +/*! + Closes the dialog and sets its result code to \a result. If this dialog + is shown with exec(), done() causes the local event loop to finish, + and exec() to return \a result. + + \sa QDialog::done() +*/ +void QColorDialog::done(int result) +{ + Q_D(QColorDialog); + QDialog::done(result); + if (result == Accepted) { + d->selectedQColor = d->currentQColor(); + emit colorSelected(d->selectedQColor); + } else { + d->selectedQColor = QColor(); + } + if (d->receiverToDisconnectOnClose) { + disconnect(this, SIGNAL(colorSelected(const QColor&)), + d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); + d->receiverToDisconnectOnClose = 0; + } + d->memberToDisconnectOnClose.clear(); +} + +QT_END_NAMESPACE + +#include "qcolordialog.moc" +#include "moc_qcolordialog.cpp" + +#endif // QT_NO_COLORDIALOG + +/*! + \fn QColor QColorDialog::getColor(const QColor &init, QWidget *parent, const char *name) + \compat +*/ + +/*! + \fn QRgb QColorDialog::getRgba(QRgb rgba, bool *ok, QWidget *parent, const char *name) + \compat +*/ diff --git a/src/gui/dialogs/qcolordialog.h b/src/gui/dialogs/qcolordialog.h new file mode 100644 index 0000000000..7ee2e0e772 --- /dev/null +++ b/src/gui/dialogs/qcolordialog.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORDIALOG_H +#define QCOLORDIALOG_H + +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_COLORDIALOG + +class QColorDialogPrivate; + +class Q_GUI_EXPORT QColorDialog : public QDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QColorDialog) + Q_ENUMS(ColorDialogOption) + Q_PROPERTY(QColor currentColor READ currentColor WRITE setCurrentColor + NOTIFY currentColorChanged) + Q_PROPERTY(ColorDialogOptions options READ options WRITE setOptions) + +public: + enum ColorDialogOption { + ShowAlphaChannel = 0x00000001, + NoButtons = 0x00000002, + DontUseNativeDialog = 0x00000004 + }; + + Q_DECLARE_FLAGS(ColorDialogOptions, ColorDialogOption) + + explicit QColorDialog(QWidget *parent = 0); + explicit QColorDialog(const QColor &initial, QWidget *parent = 0); + ~QColorDialog(); + + void setCurrentColor(const QColor &color); + QColor currentColor() const; + + QColor selectedColor() const; + + void setOption(ColorDialogOption option, bool on = true); + bool testOption(ColorDialogOption option) const; + void setOptions(ColorDialogOptions options); + ColorDialogOptions options() const; + +#ifdef Q_NO_USING_KEYWORD + void open() { QDialog::open(); } +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + + void setVisible(bool visible); + + // ### Qt 5: merge overloads with title = QString() + static QColor getColor(const QColor &initial, QWidget *parent, const QString &title, + ColorDialogOptions options = 0); + static QColor getColor(const QColor &initial = Qt::white, QWidget *parent = 0); + + // obsolete + static QRgb getRgba(QRgb rgba = 0xffffffff, bool *ok = 0, QWidget *parent = 0); + + // ### Qt 5: use QColor in signatures + static int customCount(); + static QRgb customColor(int index); + static void setCustomColor(int index, QRgb color); + static void setStandardColor(int index, QRgb color); + +#ifdef QT3_SUPPORT + static QColor getColor(const QColor &init, QWidget *parent, const char *name) + { Q_UNUSED(name); return getColor(init, parent); } + static QRgb getRgba(QRgb rgba, bool *ok, QWidget *parent, const char *name) + { Q_UNUSED(name); return getRgba(rgba, ok, parent); } +#endif + +Q_SIGNALS: + void currentColorChanged(const QColor &color); + void colorSelected(const QColor &color); + +protected: + void changeEvent(QEvent *event); + void done(int result); + +private: + Q_DISABLE_COPY(QColorDialog) + + Q_PRIVATE_SLOT(d_func(), void _q_addCustom()) + Q_PRIVATE_SLOT(d_func(), void _q_newHsv(int h, int s, int v)) + Q_PRIVATE_SLOT(d_func(), void _q_newColorTypedIn(QRgb rgb)) + Q_PRIVATE_SLOT(d_func(), void _q_newCustom(int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_newStandard(int, int)) +#if defined(Q_WS_MAC) + Q_PRIVATE_SLOT(d_func(), void _q_macRunNativeAppModalPanel()) +#endif + + friend class QColorShower; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QColorDialog::ColorDialogOptions) + +#endif // QT_NO_COLORDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCOLORDIALOG_H diff --git a/src/gui/dialogs/qcolordialog_mac.mm b/src/gui/dialogs/qcolordialog_mac.mm new file mode 100644 index 0000000000..2556265a59 --- /dev/null +++ b/src/gui/dialogs/qcolordialog_mac.mm @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcolordialog_p.h" +#if !defined(QT_NO_COLORDIALOG) && defined(Q_WS_MAC) +#include <qapplication.h> +#include <qtimer.h> +#include <qdialogbuttonbox.h> +#include <private/qapplication_p.h> +#include <private/qt_mac_p.h> +#include <qdebug.h> +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> + +#if !CGFLOAT_DEFINED +typedef float CGFloat; // Should only not be defined on 32-bit platforms +#endif + +QT_USE_NAMESPACE + +@class QCocoaColorPanelDelegate; + +@interface QCocoaColorPanelDelegate : NSObject { + NSColorPanel *mColorPanel; + NSView *mStolenContentView; + NSButton *mOkButton; + NSButton *mCancelButton; + QColorDialogPrivate *mPriv; + QColor *mQtColor; + CGFloat mMinWidth; // currently unused + CGFloat mExtraHeight; // currently unused + BOOL mHackedPanel; +} +- (id)initWithColorPanel:(NSColorPanel *)panel + stolenContentView:(NSView *)stolenContentView + okButton:(NSButton *)okButton + cancelButton:(NSButton *)cancelButton + priv:(QColorDialogPrivate *)priv; +- (BOOL)windowShouldClose:(id)window; +- (void)windowDidResize:(NSNotification *)notification; +- (void)colorChanged:(NSNotification *)notification; +- (void)relayout; +- (void)onOkClicked; +- (void)onCancelClicked; +- (void)updateQtColor; +- (NSColorPanel *)colorPanel; +- (QColor)qtColor; +- (void)finishOffWithCode:(NSInteger)result; +- (void)cleanUpAfterMyself; +@end + +@implementation QCocoaColorPanelDelegate +- (id)initWithColorPanel:(NSColorPanel *)panel + stolenContentView:(NSView *)stolenContentView + okButton:(NSButton *)okButton + cancelButton:(NSButton *)cancelButton + priv:(QColorDialogPrivate *)priv +{ + self = [super init]; + + mColorPanel = panel; + mStolenContentView = stolenContentView; + mOkButton = okButton; + mCancelButton = cancelButton; + mPriv = priv; + mMinWidth = 0.0; + mExtraHeight = 0.0; + mHackedPanel = (okButton != 0); + + if (mHackedPanel) { + [self relayout]; + + [okButton setAction:@selector(onOkClicked)]; + [okButton setTarget:self]; + + [cancelButton setAction:@selector(onCancelClicked)]; + [cancelButton setTarget:self]; + } + + if (mPriv) + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(colorChanged:) + name:NSColorPanelColorDidChangeNotification + object:mColorPanel]; + mQtColor = new QColor(); + return self; +} + +- (void)dealloc +{ + if (mPriv) + [[NSNotificationCenter defaultCenter] removeObserver:self]; + delete mQtColor; + [super dealloc]; +} + +- (BOOL)windowShouldClose:(id)window +{ + Q_UNUSED(window); + if (mHackedPanel) { + [self onCancelClicked]; + } else { + [self updateQtColor]; + [self finishOffWithCode:NSCancelButton]; + } + return true; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + Q_UNUSED(notification); + if (mHackedPanel) + [self relayout]; +} + +- (void)colorChanged:(NSNotification *)notification; +{ + Q_UNUSED(notification); + if (mPriv) + [self updateQtColor]; +} + +- (void)relayout +{ + Q_ASSERT(mHackedPanel); + + NSRect rect = [[mStolenContentView superview] frame]; + + // should a priori be kept in sync with qfontdialog_mac.mm + const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon + const CGFloat ButtonMinHeight = 32.0; + const CGFloat ButtonSpacing = 0.0; + const CGFloat ButtonTopMargin = 0.0; + const CGFloat ButtonBottomMargin = 7.0; + const CGFloat ButtonSideMargin = 9.0; + + [mOkButton sizeToFit]; + NSSize okSizeHint = [mOkButton frame].size; + + [mCancelButton sizeToFit]; + NSSize cancelSizeHint = [mCancelButton frame].size; + + const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth, + qMax(okSizeHint.width, cancelSizeHint.width)), + CGFloat((rect.size.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5)); + const CGFloat ButtonHeight = qMax(ButtonMinHeight, + qMax(okSizeHint.height, cancelSizeHint.height)); + + NSRect okRect = { { rect.size.width - ButtonSideMargin - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mOkButton setFrame:okRect]; + [mOkButton setNeedsDisplay:YES]; + + NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mCancelButton setFrame:cancelRect]; + [mCancelButton setNeedsDisplay:YES]; + + const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin; + NSRect stolenCVRect = { { 0.0, Y }, + { rect.size.width, rect.size.height - Y } }; + [mStolenContentView setFrame:stolenCVRect]; + [mStolenContentView setNeedsDisplay:YES]; + + [[mStolenContentView superview] setNeedsDisplay:YES]; + mMinWidth = 2 * ButtonSideMargin + ButtonSpacing + 2 * ButtonWidth; + mExtraHeight = Y; +} + +- (void)onOkClicked +{ + Q_ASSERT(mHackedPanel); + [[mStolenContentView window] close]; + [self updateQtColor]; + [self finishOffWithCode:NSOKButton]; +} + +- (void)onCancelClicked +{ + Q_ASSERT(mHackedPanel); + [[mStolenContentView window] close]; + delete mQtColor; + mQtColor = new QColor(); + [self finishOffWithCode:NSCancelButton]; +} + +- (void)updateQtColor +{ + delete mQtColor; + mQtColor = new QColor(); + NSColor *color = [mColorPanel color]; + NSString *colorSpace = [color colorSpaceName]; + if (colorSpace == NSDeviceCMYKColorSpace) { + CGFloat cyan, magenta, yellow, black, alpha; + [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha]; + mQtColor->setCmykF(cyan, magenta, yellow, black, alpha); + } else { + NSColor *tmpColor; + if (colorSpace == NSCalibratedRGBColorSpace || colorSpace == NSDeviceRGBColorSpace) { + tmpColor = color; + } else { + tmpColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + } + CGFloat red, green, blue, alpha; + [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha]; + mQtColor->setRgbF(red, green, blue, alpha); + } + + if (mPriv) + mPriv->setCurrentQColor(*mQtColor); +} + +- (NSColorPanel *)colorPanel +{ + return mColorPanel; +} + +- (QColor)qtColor +{ + return *mQtColor; +} + +- (void)finishOffWithCode:(NSInteger)code +{ + if (mPriv) { + // Finish the QColorDialog as well. But since a call to accept or reject will + // close down the QEventLoop found in QDialog, we need to make sure that the + // current thread has exited the native dialogs modal session/run loop first. + // We ensure this by posting the call: + [NSApp stopModalWithCode:code]; + if (code == NSOKButton) + QMetaObject::invokeMethod(mPriv->colorDialog(), "accept", Qt::QueuedConnection); + else + QMetaObject::invokeMethod(mPriv->colorDialog(), "reject", Qt::QueuedConnection); + } else { + [NSApp stopModalWithCode:code]; + } +} + +- (void)cleanUpAfterMyself +{ + if (mHackedPanel) { + NSView *ourContentView = [mColorPanel contentView]; + + // return stolen stuff to its rightful owner + [mStolenContentView removeFromSuperview]; + [mColorPanel setContentView:mStolenContentView]; + + [mOkButton release]; + [mCancelButton release]; + [ourContentView release]; + } + [mColorPanel setDelegate:nil]; +} +@end + +QT_BEGIN_NAMESPACE + +extern void macStartInterceptNSPanelCtor(); +extern void macStopInterceptNSPanelCtor(); +extern NSButton *macCreateButton(const char *text, NSView *superview); + +void *QColorDialogPrivate::openCocoaColorPanel(const QColor &initial, + QWidget *parent, const QString &title, QColorDialog::ColorDialogOptions options, + QColorDialogPrivate *priv) +{ + Q_UNUSED(parent); // we would use the parent if only NSColorPanel could be a sheet + QMacCocoaAutoReleasePool pool; + + /* + The standard Cocoa color panel has no OK or Cancel button and + is created as a utility window, whereas we want something like + the Carbon color panel. We need to take the following steps: + + 1. Intercept the color panel constructor to turn off the + NSUtilityWindowMask flag. This is done by temporarily + replacing initWithContentRect:styleMask:backing:defer: + in NSPanel by our own method. + + 2. Modify the color panel so that its content view is part + of a new content view that contains it as well as two + buttons (OK and Cancel). + + 3. Lay out the original content view and the buttons when + the color panel is shown and whenever it is resized. + + 4. Clean up after ourselves. + */ + + bool hackColorPanel = !(options & QColorDialog::NoButtons); + + if (hackColorPanel) + macStartInterceptNSPanelCtor(); + NSColorPanel *colorPanel = [NSColorPanel sharedColorPanel]; + if (hackColorPanel) + macStopInterceptNSPanelCtor(); + + [colorPanel setHidesOnDeactivate:false]; + + // set up the Cocoa color panel + [colorPanel setShowsAlpha:options & QColorDialog::ShowAlphaChannel]; + [colorPanel setTitle:(NSString*)(CFStringRef)QCFString(title)]; + + NSView *stolenContentView = 0; + NSButton *okButton = 0; + NSButton *cancelButton = 0; + + if (hackColorPanel) { + // steal the color panel's contents view + stolenContentView = [colorPanel contentView]; + [stolenContentView retain]; + [colorPanel setContentView:0]; + + // create a new content view and add the stolen one as a subview + NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; + [ourContentView addSubview:stolenContentView]; + + // create OK and Cancel buttons and add these as subviews + okButton = macCreateButton("&OK", ourContentView); + cancelButton = macCreateButton("Cancel", ourContentView); + + [colorPanel setContentView:ourContentView]; + [colorPanel setDefaultButtonCell:[okButton cell]]; + } + + // create a delegate and set it + QCocoaColorPanelDelegate *delegate = + [[QCocoaColorPanelDelegate alloc] initWithColorPanel:colorPanel + stolenContentView:stolenContentView + okButton:okButton + cancelButton:cancelButton + priv:priv]; + [colorPanel setDelegate:delegate]; + setColor(delegate, initial); + [colorPanel makeKeyAndOrderFront:colorPanel]; + + return delegate; +} + +void QColorDialogPrivate::closeCocoaColorPanel(void *delegate) +{ + QMacCocoaAutoReleasePool pool; + QCocoaColorPanelDelegate *theDelegate = static_cast<QCocoaColorPanelDelegate *>(delegate); + [[theDelegate colorPanel] close]; + [theDelegate cleanUpAfterMyself]; + [theDelegate autorelease]; +} + +void QColorDialogPrivate::mac_nativeDialogModalHelp() +{ + // Do a queued meta-call to open the native modal dialog so it opens after the new + // event loop has started to execute (in QDialog::exec). Using a timer rather than + // a queued meta call is intentional to ensure that the call is only delivered when + // [NSApp run] runs (timers are handeled special in cocoa). If NSApp is not + // running (which is the case if e.g a top-most QEventLoop has been + // interrupted, and the second-most event loop has not yet been reactivated (regardless + // if [NSApp run] is still on the stack)), showing a native modal dialog will fail. + if (delegate){ + Q_Q(QColorDialog); + QTimer::singleShot(1, q, SLOT(_q_macRunNativeAppModalPanel())); + } +} + +void QColorDialogPrivate::_q_macRunNativeAppModalPanel() +{ + QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); + QMacCocoaAutoReleasePool pool; + QCocoaColorPanelDelegate *delegateCasted = static_cast<QCocoaColorPanelDelegate *>(delegate); + [NSApp runModalForWindow:[delegateCasted colorPanel]]; +} + +void QColorDialogPrivate::setColor(void *delegate, const QColor &color) +{ + QMacCocoaAutoReleasePool pool; + QCocoaColorPanelDelegate *theDelegate = static_cast<QCocoaColorPanelDelegate *>(delegate); + NSColor *nsColor = [NSColor colorWithCalibratedRed:color.red() / 255.0 + green:color.green() / 255.0 + blue:color.blue() / 255.0 + alpha:color.alpha() / 255.0]; + [[theDelegate colorPanel] setColor:nsColor]; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/dialogs/qcolordialog_p.h b/src/gui/dialogs/qcolordialog_p.h new file mode 100644 index 0000000000..1f819858a2 --- /dev/null +++ b/src/gui/dialogs/qcolordialog_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLORDIALOG_P_H +#define QCOLORDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// to version without notice, or even be removed. +// +// We mean it. +// +// + +#include "private/qdialog_p.h" +#include "qcolordialog.h" + +#ifndef QT_NO_COLORDIALOG + +QT_BEGIN_NAMESPACE + +class QColorLuminancePicker; +class QColorPicker; +class QColorShower; +class QDialogButtonBox; +class QLabel; +class QVBoxLayout; +class QPushButton; +class QWellArray; + +class QColorDialogPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QColorDialog) + +public: + void init(const QColor &initial); + QRgb currentColor() const; + QColor currentQColor() const; + void setCurrentColor(QRgb rgb); + void setCurrentQColor(const QColor &color); + bool selectColor(const QColor &color); + + int currentAlpha() const; + void setCurrentAlpha(int a); + void showAlpha(bool b); + bool isAlphaVisible() const; + void retranslateStrings(); + + void _q_addCustom(); + + void _q_newHsv(int h, int s, int v); + void _q_newColorTypedIn(QRgb rgb); + void _q_newCustom(int, int); + void _q_newStandard(int, int); + + QWellArray *custom; + QWellArray *standard; + + QDialogButtonBox *buttons; + QVBoxLayout *leftLay; + QColorPicker *cp; + QColorLuminancePicker *lp; + QColorShower *cs; + QLabel *lblBasicColors; + QLabel *lblCustomColors; + QPushButton *ok; + QPushButton *cancel; + QPushButton *addCusBt; + QColor selectedQColor; + int nextCust; + bool smallDisplay; + QColorDialog::ColorDialogOptions opts; + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; + +#ifdef Q_WS_MAC + static void *openCocoaColorPanel(const QColor &initial, + QWidget *parent, const QString &title, + QColorDialog::ColorDialogOptions options, + QColorDialogPrivate *priv = 0); + static void closeCocoaColorPanel(void *delegate); + static QColor execCocoaColorPanel(const QColor &initial, QWidget *parent, + const QString &title, QColorDialog::ColorDialogOptions options); + static void setColor(void *delegate, const QColor &color); + + inline void done(int result) { q_func()->done(result); } + inline QColorDialog *colorDialog() { return q_func(); } + + void *delegate; + + static bool sharedColorPanelAvailable; + + void _q_macRunNativeAppModalPanel(); + void mac_nativeDialogModalHelp(); +#endif +}; + +#endif // QT_NO_COLORDIALOG + +QT_END_NAMESPACE + +#endif // QCOLORDIALOG_P_H diff --git a/src/gui/dialogs/qdialog.cpp b/src/gui/dialogs/qdialog.cpp new file mode 100644 index 0000000000..14d8162efd --- /dev/null +++ b/src/gui/dialogs/qdialog.cpp @@ -0,0 +1,1159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdialog.h" + + +#include "qevent.h" +#include "qdesktopwidget.h" +#include "qpushbutton.h" +#include "qapplication.h" +#include "qlayout.h" +#include "qsizegrip.h" +#include "qwhatsthis.h" +#include "qmenu.h" +#include "qcursor.h" +#include "private/qdialog_p.h" +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif +#if defined(Q_OS_WINCE) +#include "qt_windows.h" +#include "qmenubar.h" +#include "qpointer.h" +#include "qguifunctions_wince.h" +extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp +extern bool qt_wince_is_smartphone(); //is defined in qguifunctions_wce.cpp +#elif defined(Q_WS_X11) +# include "../kernel/qt_x11_p.h" +#endif + +#ifndef SPI_GETSNAPTODEFBUTTON +# define SPI_GETSNAPTODEFBUTTON 95 +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QDialog + \brief The QDialog class is the base class of dialog windows. + + \ingroup dialogs + \ingroup abstractwidgets + \mainclass + + A dialog window is a top-level window mostly used for short-term + tasks and brief communications with the user. QDialogs may be + modal or modeless. QDialogs can + provide a \link #return return + value\endlink, and they can have \link #default default + buttons\endlink. QDialogs can also have a QSizeGrip in their + lower-right corner, using setSizeGripEnabled(). + + Note that QDialog (an any other widget that has type Qt::Dialog) uses + the parent widget slightly differently from other classes in Qt. A + dialog is always a top-level widget, but if it has a parent, its + default location is centered on top of the parent's top-level widget + (if it is not top-level itself). It will also share the parent's + taskbar entry. + + Use the overload of the QWidget::setParent() function to change + the ownership of a QDialog widget. This function allows you to + explicitly set the window flags of the reparented widget; using + the overloaded function will clear the window flags specifying the + window-system properties for the widget (in particular it will + reset the Qt::Dialog flag). + + \section1 Modal Dialogs + + A \bold{modal} dialog is a dialog that blocks input to other + visible windows in the same application. Dialogs that are used to + request a file name from the user or that are used to set + application preferences are usually modal. Dialogs can be + \l{Qt::ApplicationModal}{application modal} (the default) or + \l{Qt::WindowModal}{window modal}. + + When an application modal dialog is opened, the user must finish + interacting with the dialog and close it before they can access + any other window in the application. Window modal dialogs only + block access to the window associated with the dialog, allowing + the user to continue to use other windows in an application. + + The most common way to display a modal dialog is to call its + exec() function. When the user closes the dialog, exec() will + provide a useful \link #return return value\endlink. Typically, + to get the dialog to close and return the appropriate value, we + connect a default button, e.g. \gui OK, to the accept() slot and a + \gui Cancel button to the reject() slot. + Alternatively you can call the done() slot with \c Accepted or + \c Rejected. + + An alternative is to call setModal(true) or setWindowModality(), + then show(). Unlike exec(), show() returns control to the caller + immediately. Calling setModal(true) is especially useful for + progress dialogs, where the user must have the ability to interact + with the dialog, e.g. to cancel a long running operation. If you + use show() and setModal(true) together to perform a long operation, + you must call QApplication::processEvents() periodically during + processing to enable the user to interact with the dialog. (See + QProgressDialog.) + + \section1 Modeless Dialogs + + A \bold{modeless} dialog is a dialog that operates + independently of other windows in the same application. Find and + replace dialogs in word-processors are often modeless to allow the + user to interact with both the application's main window and with + the dialog. + + Modeless dialogs are displayed using show(), which returns control + to the caller immediately. + + If you invoke the \l{QWidget::show()}{show()} function after hiding + a dialog, the dialog will be displayed in its original position. This is + because the window manager decides the position for windows that + have not been explicitly placed by the programmer. To preserve the + position of a dialog that has been moved by the user, save its position + in your \l{QWidget::closeEvent()}{closeEvent()} handler and then + move the dialog to that position, before showing it again. + + \target default + \section1 Default Button + + A dialog's \e default button is the button that's pressed when the + user presses Enter (Return). This button is used to signify that + the user accepts the dialog's settings and wants to close the + dialog. Use QPushButton::setDefault(), QPushButton::isDefault() + and QPushButton::autoDefault() to set and control the dialog's + default button. + + \target escapekey + \section1 Escape Key + + If the user presses the Esc key in a dialog, QDialog::reject() + will be called. This will cause the window to close: The \link + QCloseEvent close event \endlink cannot be \link + QCloseEvent::ignore() ignored \endlink. + + \section1 Extensibility + + Extensibility is the ability to show the dialog in two ways: a + partial dialog that shows the most commonly used options, and a + full dialog that shows all the options. Typically an extensible + dialog will initially appear as a partial dialog, but with a + \gui More toggle button. If the user presses the \gui More button down, + the dialog is expanded. The \l{Extension Example} shows how to achieve + extensible dialogs using Qt. + + \target return + \section1 Return Value (Modal Dialogs) + + Modal dialogs are often used in situations where a return value is + required, e.g. to indicate whether the user pressed \gui OK or + \gui Cancel. A dialog can be closed by calling the accept() or the + reject() slots, and exec() will return \c Accepted or \c Rejected + as appropriate. The exec() call returns the result of the dialog. + The result is also available from result() if the dialog has not + been destroyed. + + In order to modify your dialog's close behavior, you can reimplement + the functions accept(), reject() or done(). The + \l{QWidget::closeEvent()}{closeEvent()} function should only be + reimplemented to preserve the dialog's position or to override the + standard close or reject behavior. + + \target examples + \section1 Code Examples + + A modal dialog: + + \snippet doc/src/snippets/dialogs/dialogs.cpp 1 + + A modeless dialog: + + \snippet doc/src/snippets/dialogs/dialogs.cpp 0 + + \sa QDialogButtonBox, QTabWidget, QWidget, QProgressDialog, + {fowler}{GUI Design Handbook: Dialogs, Standard}, {Extension Example}, + {Standard Dialogs Example} +*/ + +/*! \enum QDialog::DialogCode + + The value returned by a modal dialog. + + \value Accepted + \value Rejected +*/ + +/*! + \property QDialog::sizeGripEnabled + \brief whether the size grip is enabled + + A QSizeGrip is placed in the bottom-right corner of the dialog when this + property is enabled. By default, the size grip is disabled. +*/ + + +/*! + Constructs a dialog with parent \a parent. + + A dialog is always a top-level widget, but if it has a parent, its + default location is centered on top of the parent. It will also + share the parent's taskbar entry. + + The widget flags \a f are passed on to the QWidget constructor. + If, for example, you don't want a What's This button in the title bar + of the dialog, pass Qt::WindowTitleHint | Qt::WindowSystemMenuHint in \a f. + + \sa QWidget::setWindowFlags() +*/ + +QDialog::QDialog(QWidget *parent, Qt::WindowFlags f) + : QWidget(*new QDialogPrivate, parent, + f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0)) +{ +#ifdef Q_OS_WINCE + if (!qt_wince_is_smartphone()) + setWindowFlags(windowFlags() | Qt::WindowOkButtonHint | QFlag(qt_wince_is_mobile() ? 0 : Qt::WindowCancelButtonHint)); +#endif +} + +#ifdef QT3_SUPPORT +/*! + \overload + \obsolete +*/ +QDialog::QDialog(QWidget *parent, const char *name, bool modal, Qt::WindowFlags f) + : QWidget(*new QDialogPrivate, parent, + f + | QFlag(modal ? Qt::WShowModal : 0) + | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0) + ) +{ + setObjectName(QString::fromAscii(name)); +} +#endif + +/*! + \overload + \internal +*/ +QDialog::QDialog(QDialogPrivate &dd, QWidget *parent, Qt::WindowFlags f) + : QWidget(dd, parent, f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0)) +{ +#ifdef Q_OS_WINCE + if (!qt_wince_is_smartphone()) + setWindowFlags(windowFlags() | Qt::WindowOkButtonHint | QFlag(qt_wince_is_mobile() ? 0 : Qt::WindowCancelButtonHint)); +#endif +} + +/*! + Destroys the QDialog, deleting all its children. +*/ + +QDialog::~QDialog() +{ + // Need to hide() here, as our (to-be) overridden hide() + // will not be called in ~QWidget. + hide(); +} + +/*! + \internal + This function is called by the push button \a pushButton when it + becomes the default button. If \a pushButton is 0, the dialogs + default default button becomes the default button. This is what a + push button calls when it loses focus. +*/ +void QDialogPrivate::setDefault(QPushButton *pushButton) +{ + Q_Q(QDialog); + bool hasMain = false; + QList<QPushButton*> list = qFindChildren<QPushButton*>(q); + for (int i=0; i<list.size(); ++i) { + QPushButton *pb = list.at(i); + if (pb->window() == q) { + if (pb == mainDef) + hasMain = true; + if (pb != pushButton) + pb->setDefault(false); + } + } + if (!pushButton && hasMain) + mainDef->setDefault(true); + if (!hasMain) + mainDef = pushButton; +} + +/*! + \internal + This function sets the default default push button to \a pushButton. + This function is called by QPushButton::setDefault(). +*/ +void QDialogPrivate::setMainDefault(QPushButton *pushButton) +{ + mainDef = 0; + setDefault(pushButton); +} + +/*! + \internal + Hides the default button indicator. Called when non auto-default + push button get focus. + */ +void QDialogPrivate::hideDefault() +{ + Q_Q(QDialog); + QList<QPushButton*> list = qFindChildren<QPushButton*>(q); + for (int i=0; i<list.size(); ++i) { + list.at(i)->setDefault(false); + } +} + +void QDialogPrivate::resetModalitySetByOpen() +{ + Q_Q(QDialog); + if (resetModalityTo != -1 && !q->testAttribute(Qt::WA_SetWindowModality)) { + // open() changed the window modality and the user didn't touch it afterwards; restore it + q->setWindowModality(Qt::WindowModality(resetModalityTo)); + q->setAttribute(Qt::WA_SetWindowModality, wasModalitySet); +#ifdef Q_WS_MAC + Q_ASSERT(resetModalityTo != Qt::WindowModal); + q->setParent(q->parentWidget(), Qt::Dialog); +#endif + } + resetModalityTo = -1; +} + +#ifdef Q_OS_WINCE +#ifdef Q_OS_WINCE_WM +void QDialogPrivate::_q_doneAction() +{ + //Done... + QApplication::postEvent(q_func(), new QEvent(QEvent::OkRequest)); +} +#endif + +/*! + \reimp +*/ +bool QDialog::event(QEvent *e) +{ + bool result = QWidget::event(e); + if (e->type() == QEvent::OkRequest) { + accept(); + result = true; + } + return result; +} +#endif + +/*! + Returns the modal dialog's result code, \c Accepted or \c Rejected. + + Do not call this function if the dialog was constructed with the + Qt::WA_DeleteOnClose attribute. +*/ +int QDialog::result() const +{ + Q_D(const QDialog); + return d->rescode; +} + +/*! + \fn void QDialog::setResult(int i) + + Sets the modal dialog's result code to \a i. + + \note We recommend that you use one of the values defined by + QDialog::DialogCode. +*/ +void QDialog::setResult(int r) +{ + Q_D(QDialog); + d->rescode = r; +} + +/*! + \since 4.5 + + Shows the dialog as a \l{QDialog#Modal Dialogs}{window modal dialog}, + returning immediately. + + \sa exec(), show(), result(), setWindowModality() +*/ +void QDialog::open() +{ + Q_D(QDialog); + + Qt::WindowModality modality = windowModality(); + if (modality != Qt::WindowModal) { + d->resetModalityTo = modality; + d->wasModalitySet = testAttribute(Qt::WA_SetWindowModality); + setWindowModality(Qt::WindowModal); + setAttribute(Qt::WA_SetWindowModality, false); +#ifdef Q_WS_MAC + setParent(parentWidget(), Qt::Sheet); +#endif + } + + setResult(0); + show(); +} + +/*! + Shows the dialog as a \l{QDialog#Modal Dialogs}{modal dialog}, + blocking until the user closes it. The function returns a \l + DialogCode result. + + If the dialog is \l{Qt::ApplicationModal}{application modal}, users cannot + interact with any other window in the same application until they close + the dialog. If the dialog is \l{Qt::ApplicationModal}{window modal}, only + interaction with the parent window is blocked while the dialog is open. + By default, the dialog is application modal. + + \sa open(), show(), result(), setWindowModality() +*/ + +int QDialog::exec() +{ + Q_D(QDialog); + + if (d->eventLoop) { + qWarning("QDialog::exec: Recursive call detected"); + return -1; + } + + bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_DeleteOnClose, false); + + d->resetModalitySetByOpen(); + + bool wasShowModal = testAttribute(Qt::WA_ShowModal); + setAttribute(Qt::WA_ShowModal, true); + setResult(0); + +//On Windows Mobile we create an empty menu to hide the current menu +#ifdef Q_OS_WINCE_WM +#ifndef QT_NO_MENUBAR + QMenuBar *menuBar = 0; + if (!findChild<QMenuBar *>()) + menuBar = new QMenuBar(this); + if (qt_wince_is_smartphone()) { + QAction *doneAction = new QAction(tr("Done"), this); + menuBar->setDefaultAction(doneAction); + connect(doneAction, SIGNAL(triggered()), this, SLOT(_q_doneAction())); + } +#endif //QT_NO_MENUBAR +#endif //Q_OS_WINCE_WM + + show(); + +#ifdef Q_WS_MAC + d->mac_nativeDialogModalHelp(); +#endif + + QEventLoop eventLoop; + d->eventLoop = &eventLoop; + QPointer<QDialog> guard = this; + (void) eventLoop.exec(QEventLoop::DialogExec); + if (guard.isNull()) + return QDialog::Rejected; + d->eventLoop = 0; + + setAttribute(Qt::WA_ShowModal, wasShowModal); + + int res = result(); + if (deleteOnClose) + delete this; +#ifdef Q_OS_WINCE_WM +#ifndef QT_NO_MENUBAR + else if (menuBar) + delete menuBar; +#endif //QT_NO_MENUBAR +#endif //Q_OS_WINCE_WM + return res; +} + + +/*! + Closes the dialog and sets its result code to \a r. If this dialog + is shown with exec(), done() causes the local event loop to finish, + and exec() to return \a r. + + As with QWidget::close(), done() deletes the dialog if the + Qt::WA_DeleteOnClose flag is set. If the dialog is the application's + main widget, the application terminates. If the dialog is the + last window closed, the QApplication::lastWindowClosed() signal is + emitted. + + \sa accept(), reject(), QApplication::activeWindow(), QApplication::quit() +*/ + +void QDialog::done(int r) +{ + Q_D(QDialog); + hide(); + setResult(r); + + d->close_helper(QWidgetPrivate::CloseNoEvent); + d->resetModalitySetByOpen(); + + emit finished(r); + if (r == Accepted) + emit accepted(); + else if (r == Rejected) + emit rejected(); +} + +/*! + Hides the modal dialog and sets the result code to \c Accepted. + + \sa reject() done() +*/ + +void QDialog::accept() +{ + done(Accepted); +} + +/*! + Hides the modal dialog and sets the result code to \c Rejected. + + \sa accept() done() +*/ + +void QDialog::reject() +{ + done(Rejected); +} + +/*! \reimp */ +bool QDialog::eventFilter(QObject *o, QEvent *e) +{ + return QWidget::eventFilter(o, e); +} + +/***************************************************************************** + Event handlers + *****************************************************************************/ + +#ifndef QT_NO_CONTEXTMENU +/*! \reimp */ +void QDialog::contextMenuEvent(QContextMenuEvent *e) +{ +#if defined(QT_NO_WHATSTHIS) || defined(QT_NO_MENU) + Q_UNUSED(e); +#else + QWidget *w = childAt(e->pos()); + if (!w) { + w = rect().contains(e->pos()) ? this : 0; + if (!w) + return; + } + while (w && w->whatsThis().size() == 0 && !w->testAttribute(Qt::WA_CustomWhatsThis)) + w = w->isWindow() ? 0 : w->parentWidget(); + if (w) { + QMenu p(this); + QAction *wt = p.addAction(tr("What's This?")); + if (p.exec(e->globalPos()) == wt) { + QHelpEvent e(QEvent::WhatsThis, w->rect().center(), + w->mapToGlobal(w->rect().center())); + QApplication::sendEvent(w, &e); + } + } +#endif +} +#endif // QT_NO_CONTEXTMENU + +/*! \reimp */ +void QDialog::keyPressEvent(QKeyEvent *e) +{ + // Calls reject() if Escape is pressed. Simulates a button + // click for the default button if Enter is pressed. Move focus + // for the arrow keys. Ignore the rest. +#ifdef Q_WS_MAC + if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { + reject(); + } else +#endif + if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) { + switch (e->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: { + QList<QPushButton*> list = qFindChildren<QPushButton*>(this); + for (int i=0; i<list.size(); ++i) { + QPushButton *pb = list.at(i); + if (pb->isDefault() && pb->isVisible()) { + if (pb->isEnabled()) + pb->click(); + return; + } + } + } + break; + case Qt::Key_Escape: + reject(); + break; + case Qt::Key_Up: + case Qt::Key_Left: + if (focusWidget() && + (focusWidget()->focusPolicy() == Qt::StrongFocus || + focusWidget()->focusPolicy() == Qt::WheelFocus)) { + e->ignore(); + break; + } + // call ours, since c++ blocks us from calling the one + // belonging to focusWidget(). + focusNextPrevChild(false); + break; + case Qt::Key_Down: + case Qt::Key_Right: + if (focusWidget() && + (focusWidget()->focusPolicy() == Qt::StrongFocus || + focusWidget()->focusPolicy() == Qt::WheelFocus)) { + e->ignore(); + break; + } + focusNextPrevChild(true); + break; + default: + e->ignore(); + return; + } + } else { + e->ignore(); + } +} + +/*! \reimp */ +void QDialog::closeEvent(QCloseEvent *e) +{ +#ifndef QT_NO_WHATSTHIS + if (isModal() && QWhatsThis::inWhatsThisMode()) + QWhatsThis::leaveWhatsThisMode(); +#endif + if (isVisible()) { + QPointer<QObject> that = this; + reject(); + if (that && isVisible()) + e->ignore(); + } else { + e->accept(); + } +} + +/***************************************************************************** + Geometry management. + *****************************************************************************/ + +/*! \reimp +*/ + +void QDialog::setVisible(bool visible) +{ + Q_D(QDialog); + if (visible) { + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) + return; + + if (!testAttribute(Qt::WA_Moved)) { + Qt::WindowStates state = windowState(); + adjustPosition(parentWidget()); + setAttribute(Qt::WA_Moved, false); // not really an explicit position + if (state != windowState()) + setWindowState(state); + } + QWidget::setVisible(visible); + showExtension(d->doShowExtension); + QWidget *fw = window()->focusWidget(); + if (!fw) + fw = this; + + /* + The following block is to handle a special case, and does not + really follow propper logic in concern of autoDefault and TAB + order. However, it's here to ease usage for the users. If a + dialog has a default QPushButton, and first widget in the TAB + order also is a QPushButton, then we give focus to the main + default QPushButton. This simplifies code for the developers, + and actually catches most cases... If not, then they simply + have to use [widget*]->setFocus() themselves... + */ + if (d->mainDef && fw->focusPolicy() == Qt::NoFocus) { + QWidget *first = fw; + while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus) + ; + if (first != d->mainDef && qobject_cast<QPushButton*>(first)) + d->mainDef->setFocus(); + } + if (!d->mainDef && isWindow()) { + QWidget *w = fw; + while ((w = w->nextInFocusChain()) != fw) { + QPushButton *pb = qobject_cast<QPushButton *>(w); + if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) { + pb->setDefault(true); + break; + } + } + } + if (fw && !fw->hasFocus()) { + QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason); + QApplication::sendEvent(fw, &e); + } + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::DialogStart); +#endif + + } else { + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) + return; + +#ifndef QT_NO_ACCESSIBILITY + if (isVisible()) + QAccessible::updateAccessibility(this, 0, QAccessible::DialogEnd); +#endif + + // Reimplemented to exit a modal event loop when the dialog is hidden. + QWidget::setVisible(visible); + if (d->eventLoop) + d->eventLoop->exit(); + } +#ifdef Q_WS_WIN + if (d->mainDef && isActiveWindow()) { + BOOL snapToDefault = false; + if ( QT_WA_INLINE( SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &snapToDefault, 0) , + SystemParametersInfoA(SPI_GETSNAPTODEFBUTTON, 0, &snapToDefault, 0) )) { + if (snapToDefault) + QCursor::setPos(d->mainDef->mapToGlobal(d->mainDef->rect().center())); + } + } +#endif +} + +/*!\reimp */ +void QDialog::showEvent(QShowEvent *event) +{ + if (!event->spontaneous() && !testAttribute(Qt::WA_Moved)) { + Qt::WindowStates state = windowState(); + adjustPosition(parentWidget()); + setAttribute(Qt::WA_Moved, false); // not really an explicit position + if (state != windowState()) + setWindowState(state); + } +} + +/*! \internal */ +void QDialog::adjustPosition(QWidget* w) +{ +#ifdef Q_WS_X11 + // if the WM advertises that it will place the windows properly for us, let it do it :) + if (X11->isSupportedByWM(ATOM(_NET_WM_FULL_PLACEMENT))) + return; +#endif + + QPoint p(0, 0); + int extraw = 0, extrah = 0, scrn = 0; + if (w) + w = w->window(); + QRect desk; + if (w) { + scrn = QApplication::desktop()->screenNumber(w); + } else if (QApplication::desktop()->isVirtualDesktop()) { + scrn = QApplication::desktop()->screenNumber(QCursor::pos()); + } else { + scrn = QApplication::desktop()->screenNumber(this); + } + desk = QApplication::desktop()->availableGeometry(scrn); + + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = 0; (extraw == 0 || extrah == 0) && i < list.size(); ++i) { + QWidget * current = list.at(i); + if (current->isVisible()) { + int framew = current->geometry().x() - current->x(); + int frameh = current->geometry().y() - current->y(); + + extraw = qMax(extraw, framew); + extrah = qMax(extrah, frameh); + } + } + + // sanity check for decoration frames. With embedding, we + // might get extraordinary values + if (extraw == 0 || extrah == 0 || extraw >= 10 || extrah >= 40) { + extrah = 40; + extraw = 10; + } + + + if (w) { + // Use mapToGlobal rather than geometry() in case w might + // be embedded in another application + QPoint pp = w->mapToGlobal(QPoint(0,0)); + p = QPoint(pp.x() + w->width()/2, + pp.y() + w->height()/ 2); + } else { + // p = middle of the desktop + p = QPoint(desk.x() + desk.width()/2, desk.y() + desk.height()/2); + } + + // p = origin of this + p = QPoint(p.x()-width()/2 - extraw, + p.y()-height()/2 - extrah); + + + if (p.x() + extraw + width() > desk.x() + desk.width()) + p.setX(desk.x() + desk.width() - width() - extraw); + if (p.x() < desk.x()) + p.setX(desk.x()); + + if (p.y() + extrah + height() > desk.y() + desk.height()) + p.setY(desk.y() + desk.height() - height() - extrah); + if (p.y() < desk.y()) + p.setY(desk.y()); + + move(p); +} + + +/*! + \obsolete + + If \a orientation is Qt::Horizontal, the extension will be displayed + to the right of the dialog's main area. If \a orientation is + Qt::Vertical, the extension will be displayed below the dialog's main + area. + + Instead of using this functionality, we recommend that you simply call + show() or hide() on the part of the dialog that you want to use as an + extension. See the \l{Extension Example} for details. + + \sa setExtension() +*/ +void QDialog::setOrientation(Qt::Orientation orientation) +{ + Q_D(QDialog); + d->orientation = orientation; +} + +/*! + \obsolete + + Returns the dialog's extension orientation. + + Instead of using this functionality, we recommend that you simply call + show() or hide() on the part of the dialog that you want to use as an + extension. See the \l{Extension Example} for details. + + \sa extension() +*/ +Qt::Orientation QDialog::orientation() const +{ + Q_D(const QDialog); + return d->orientation; +} + +/*! + \obsolete + + Sets the widget, \a extension, to be the dialog's extension, + deleting any previous extension. The dialog takes ownership of the + extension. Note that if 0 is passed any existing extension will be + deleted. This function must only be called while the dialog is hidden. + + Instead of using this functionality, we recommend that you simply call + show() or hide() on the part of the dialog that you want to use as an + extension. See the \l{Extension Example} for details. + + \sa showExtension(), setOrientation() +*/ +void QDialog::setExtension(QWidget* extension) +{ + Q_D(QDialog); + delete d->extension; + d->extension = extension; + + if (!extension) + return; + + if (extension->parentWidget() != this) + extension->setParent(this); + extension->hide(); +} + +/*! + \obsolete + + Returns the dialog's extension or 0 if no extension has been + defined. + + Instead of using this functionality, we recommend that you simply call + show() or hide() on the part of the dialog that you want to use as an + extension. See the \l{Extension Example} for details. + + \sa showExtension(), setOrientation() +*/ +QWidget* QDialog::extension() const +{ + Q_D(const QDialog); + return d->extension; +} + + +/*! + \obsolete + + If \a showIt is true, the dialog's extension is shown; otherwise the + extension is hidden. + + Instead of using this functionality, we recommend that you simply call + show() or hide() on the part of the dialog that you want to use as an + extension. See the \l{Extension Example} for details. + + \sa show(), setExtension(), setOrientation() +*/ +void QDialog::showExtension(bool showIt) +{ + Q_D(QDialog); + d->doShowExtension = showIt; + if (!d->extension) + return; + if (!testAttribute(Qt::WA_WState_Visible)) + return; + if (d->extension->isVisible() == showIt) + return; + + if (showIt) { + d->size = size(); + d->min = minimumSize(); + d->max = maximumSize(); + if (layout()) + layout()->setEnabled(false); + QSize s(d->extension->sizeHint() + .expandedTo(d->extension->minimumSize()) + .boundedTo(d->extension->maximumSize())); + if (d->orientation == Qt::Horizontal) { + int h = qMax(height(), s.height()); + d->extension->setGeometry(width(), 0, s.width(), h); + setFixedSize(width() + s.width(), h); + } else { + int w = qMax(width(), s.width()); + d->extension->setGeometry(0, height(), w, s.height()); + setFixedSize(w, height() + s.height()); + } + d->extension->show(); +#ifndef QT_NO_SIZEGRIP + const bool sizeGripEnabled = isSizeGripEnabled(); + setSizeGripEnabled(false); + d->sizeGripEnabled = sizeGripEnabled; +#endif + } else { + d->extension->hide(); + // workaround for CDE window manager that won't shrink with (-1,-1) + setMinimumSize(d->min.expandedTo(QSize(1, 1))); + setMaximumSize(d->max); + resize(d->size); + if (layout()) + layout()->setEnabled(true); +#ifndef QT_NO_SIZEGRIP + setSizeGripEnabled(d->sizeGripEnabled); +#endif + } +} + + +/*! \reimp */ +QSize QDialog::sizeHint() const +{ + Q_D(const QDialog); + if (d->extension) { + if (d->orientation == Qt::Horizontal) + return QSize(QWidget::sizeHint().width(), + qMax(QWidget::sizeHint().height(),d->extension->sizeHint().height())); + else + return QSize(qMax(QWidget::sizeHint().width(), d->extension->sizeHint().width()), + QWidget::sizeHint().height()); + } + + return QWidget::sizeHint(); +} + + +/*! \reimp */ +QSize QDialog::minimumSizeHint() const +{ + Q_D(const QDialog); + if (d->extension) { + if (d->orientation == Qt::Horizontal) + return QSize(QWidget::minimumSizeHint().width(), + qMax(QWidget::minimumSizeHint().height(), d->extension->minimumSizeHint().height())); + else + return QSize(qMax(QWidget::minimumSizeHint().width(), d->extension->minimumSizeHint().width()), + QWidget::minimumSizeHint().height()); + } + + return QWidget::minimumSizeHint(); +} + +/*! + \property QDialog::modal + \brief whether show() should pop up the dialog as modal or modeless + + By default, this property is false and show() pops up the dialog + as modeless. Setting his property to true is equivalent to setting + QWidget::windowModality to Qt::ApplicationModal. + + exec() ignores the value of this property and always pops up the + dialog as modal. + + \sa QWidget::windowModality, show(), exec() +*/ + +void QDialog::setModal(bool modal) +{ + setAttribute(Qt::WA_ShowModal, modal); +} + + +bool QDialog::isSizeGripEnabled() const +{ +#ifndef QT_NO_SIZEGRIP + Q_D(const QDialog); + return !!d->resizer; +#else + return false; +#endif +} + + +void QDialog::setSizeGripEnabled(bool enabled) +{ +#ifdef QT_NO_SIZEGRIP + Q_UNUSED(enabled); +#else + Q_D(QDialog); +#ifndef QT_NO_SIZEGRIP + d->sizeGripEnabled = enabled; + if (enabled && d->doShowExtension) + return; +#endif + if (!enabled != !d->resizer) { + if (enabled) { + d->resizer = new QSizeGrip(this); + // adjustSize() processes all events, which is suboptimal + d->resizer->resize(d->resizer->sizeHint()); + if (isRightToLeft()) + d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft()); + else + d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight()); + d->resizer->raise(); + d->resizer->show(); + } else { + delete d->resizer; + d->resizer = 0; + } + } +#endif //QT_NO_SIZEGRIP +} + + + +/*! \reimp */ +void QDialog::resizeEvent(QResizeEvent *) +{ +#ifndef QT_NO_SIZEGRIP + Q_D(QDialog); + if (d->resizer) { + if (isRightToLeft()) + d->resizer->move(rect().bottomLeft() -d->resizer->rect().bottomLeft()); + else + d->resizer->move(rect().bottomRight() -d->resizer->rect().bottomRight()); + d->resizer->raise(); + } +#endif +} + +/*! \fn void QDialog::finished(int result) + \since 4.1 + + This signal is emitted when the dialog's \a result code has been + set, either by the user or by calling done(), accept(), or + reject(). + + Note that this signal is \e not emitted when hiding the dialog + with hide() or setVisible(false). This includes deleting the + dialog while it is visible. + + \sa accepted(), rejected() +*/ + +/*! \fn void QDialog::accepted() + \since 4.1 + + This signal is emitted when the dialog has been accepted either by + the user or by calling accept() or done() with the + QDialog::Accepted argument. + + Note that this signal is \e not emitted when hiding the dialog + with hide() or setVisible(false). This includes deleting the + dialog while it is visible. + + \sa finished(), rejected() +*/ + +/*! \fn void QDialog::rejected() + \since 4.1 + + This signal is emitted when the dialog has been rejected either by + the user or by calling reject() or done() with the + QDialog::Rejected argument. + + Note that this signal is \e not emitted when hiding the dialog + with hide() or setVisible(false). This includes deleting the + dialog while it is visible. + + \sa finished(), accepted() +*/ + +QT_END_NAMESPACE +#include "moc_qdialog.cpp" diff --git a/src/gui/dialogs/qdialog.h b/src/gui/dialogs/qdialog.h new file mode 100644 index 0000000000..ee1997af41 --- /dev/null +++ b/src/gui/dialogs/qdialog.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIALOG_H +#define QDIALOG_H + +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPushButton; +class QDialogPrivate; + +class Q_GUI_EXPORT QDialog : public QWidget +{ + Q_OBJECT + friend class QPushButton; + + Q_PROPERTY(bool sizeGripEnabled READ isSizeGripEnabled WRITE setSizeGripEnabled) + Q_PROPERTY(bool modal READ isModal WRITE setModal) + +public: + explicit QDialog(QWidget *parent = 0, Qt::WindowFlags f = 0); +#ifdef QT3_SUPPORT + QT3_SUPPORT_CONSTRUCTOR QDialog(QWidget *parent, const char *name, bool modal = false, + Qt::WindowFlags f = 0); +#endif + ~QDialog(); + + enum DialogCode { Rejected, Accepted }; + + int result() const; + + void setVisible(bool visible); + + void setOrientation(Qt::Orientation orientation); + Qt::Orientation orientation() const; + + void setExtension(QWidget* extension); + QWidget* extension() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + void setSizeGripEnabled(bool); + bool isSizeGripEnabled() const; + + void setModal(bool modal); + void setResult(int r); + +Q_SIGNALS: + void finished(int result); + void accepted(); + void rejected(); + +public Q_SLOTS: + void open(); + int exec(); + virtual void done(int); + virtual void accept(); + virtual void reject(); + + void showExtension(bool); + +protected: + QDialog(QDialogPrivate &, QWidget *parent, Qt::WindowFlags f = 0); + +#ifdef Q_OS_WINCE + bool event(QEvent *e); +#endif + void keyPressEvent(QKeyEvent *); + void closeEvent(QCloseEvent *); + void showEvent(QShowEvent *); + void resizeEvent(QResizeEvent *); +#ifndef QT_NO_CONTEXTMENU + void contextMenuEvent(QContextMenuEvent *); +#endif + bool eventFilter(QObject *, QEvent *); + void adjustPosition(QWidget*); + +private: + Q_DECLARE_PRIVATE(QDialog) + Q_DISABLE_COPY(QDialog) + +#ifdef Q_OS_WINCE_WM + Q_PRIVATE_SLOT(d_func(), void _q_doneAction()) +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDIALOG_H diff --git a/src/gui/dialogs/qdialog_p.h b/src/gui/dialogs/qdialog_p.h new file mode 100644 index 0000000000..81a7b19dff --- /dev/null +++ b/src/gui/dialogs/qdialog_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIALOG_P_H +#define QDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qwidget_p.h" +#include "QtCore/qeventloop.h" +#include "QtCore/qpointer.h" +#include "QtGui/qdialog.h" +#include "QtGui/qpushbutton.h" + +QT_BEGIN_NAMESPACE + +class QSizeGrip; + +class QDialogPrivate : public QWidgetPrivate +{ + Q_DECLARE_PUBLIC(QDialog) +public: + + QDialogPrivate() + : mainDef(0), orientation(Qt::Horizontal),extension(0), doShowExtension(false), +#ifndef QT_NO_SIZEGRIP + resizer(0), + sizeGripEnabled(false), +#endif + rescode(0), resetModalityTo(-1), wasModalitySet(true), eventLoop(0) + {} + + QPointer<QPushButton> mainDef; + Qt::Orientation orientation; + QWidget *extension; + bool doShowExtension; + QSize size, min, max; +#ifndef QT_NO_SIZEGRIP + QSizeGrip *resizer; + bool sizeGripEnabled; +#endif + QPoint lastRMBPress; + + void setDefault(QPushButton *); + void setMainDefault(QPushButton *); + void hideDefault(); + void resetModalitySetByOpen(); + +#ifdef Q_OS_WINCE_WM + void _q_doneAction(); +#endif + +#ifdef Q_WS_MAC + virtual void mac_nativeDialogModalHelp(){}; +#endif + + int rescode; + int resetModalityTo; + bool wasModalitySet; + + QPointer<QEventLoop> eventLoop; +}; + +QT_END_NAMESPACE + +#endif // QDIALOG_P_H diff --git a/src/gui/dialogs/qdialogsbinarycompat_win.cpp b/src/gui/dialogs/qdialogsbinarycompat_win.cpp new file mode 100644 index 0000000000..5c0efd90f3 --- /dev/null +++ b/src/gui/dialogs/qdialogsbinarycompat_win.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qglobal.h> + +// ### Qt 5: eliminate this file + +/* + This is evil. MSVC doesn't let us remove private symbols, nor change their + visibility; yet there are some symbols we really needed to make public, e.g., + ~QColorDialog(), and then there were some totally needless symbols in our + header files, e.g., setSelectedAlpha(). So we define a new version of + QColorDialog & Co. with only the private symbols that we removed from the + public header files. The friends are there only to prevent potential compiler + warnings. + + It would have been nicer to export the missing symbols as mangled "C" symbols + instead but unfortunately MSVC uses out-of-reach characters like @ and . in + their mangled C++ symbols. +*/ + +#if QT_VERSION < 0x050000 && defined(Q_CC_MSVC) + +QT_BEGIN_NAMESPACE + +#include <QtGui/QColor> +#include <QtGui/QFont> + +class QColorDialogPrivate; +class QFontDialogPrivate; +class QInputDialogPrivate; +class QWidget; + +class Q_GUI_EXPORT QColorDialog +{ +private: + explicit QColorDialog(QWidget *, bool); + ~QColorDialog(); + + void setColor(const QColor &); + QColor color() const; + bool selectColor(const QColor &); + void setSelectedAlpha(int); + int selectedAlpha() const; + + friend class QColorDialogPrivate; +}; + +QColorDialog::QColorDialog(QWidget *, bool) {} +QColorDialog::~QColorDialog() {} +void QColorDialog::setColor(const QColor &) {} +QColor QColorDialog::color() const { return QColor(); } +bool QColorDialog::selectColor(const QColor &) { return false; } +void QColorDialog::setSelectedAlpha(int) {} +int QColorDialog::selectedAlpha() const { return 0; } + +class Q_GUI_EXPORT QFontDialog +{ +private: + explicit QFontDialog(QWidget *, bool, Qt::WindowFlags); + ~QFontDialog(); + + QFont font() const; + void setFont(const QFont &); + void updateFamilies(); + void updateStyles(); + void updateSizes(); + + static QFont getFont(bool *, const QFont *, QWidget *); + + friend class QFontDialogPrivate; +}; + +QFontDialog::QFontDialog(QWidget *, bool, Qt::WindowFlags) {} +QFontDialog::~QFontDialog() {} +QFont QFontDialog::font() const { return QFont(); } +void QFontDialog::setFont(const QFont &) { } +void QFontDialog::updateFamilies() {} +void QFontDialog::updateStyles() {} +void QFontDialog::updateSizes() {} +QFont QFontDialog::getFont(bool *, const QFont *, QWidget *) { return QFont(); } + +class Q_GUI_EXPORT QInputDialog +{ +private: + enum Type { LineEdit, SpinBox, DoubleSpinBox, ComboBox, EditableComboBox }; + + QInputDialog(const QString &, QWidget *, Type, Qt::WindowFlags); + QInputDialog(const QString &, const QString &, QWidget *, QWidget *, Qt::WindowFlags); + ~QInputDialog(); +}; + +QInputDialog::QInputDialog(const QString &, QWidget *, Type, Qt::WindowFlags) {} +QInputDialog::QInputDialog(const QString &, const QString &, QWidget *, QWidget *, Qt::WindowFlags) {} +QInputDialog::~QInputDialog() {} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/dialogs/qerrormessage.cpp b/src/gui/dialogs/qerrormessage.cpp new file mode 100644 index 0000000000..f79c6b2ad4 --- /dev/null +++ b/src/gui/dialogs/qerrormessage.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qerrormessage.h" + +#ifndef QT_NO_ERRORMESSAGE + +#include "qapplication.h" +#include "qcheckbox.h" +#include "qlabel.h" +#include "qlayout.h" +#include "qmessagebox.h" +#include "qpushbutton.h" +#include "qstringlist.h" +#include "qtextedit.h" +#include "qdialog_p.h" +#include "qpixmap.h" +#include "qmetaobject.h" +#include "qthread.h" +#include "qqueue.h" +#include "qset.h" + +#include <stdio.h> +#include <stdlib.h> + +#ifdef Q_OS_WINCE +extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp +extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp + +#include "qguifunctions_wince.h" +#endif + +QT_BEGIN_NAMESPACE + +class QErrorMessagePrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QErrorMessage) +public: + QPushButton * ok; + QCheckBox * again; + QTextEdit * errors; + QLabel * icon; + QQueue<QPair<QString, QString> > pending; + QSet<QString> doNotShow; + QSet<QString> doNotShowType; + QString currentMessage; + QString currentType; + + bool nextPending(); + void retranslateStrings(); +}; + +class QErrorMessageTextView : public QTextEdit +{ +public: + QErrorMessageTextView(QWidget *parent) + : QTextEdit(parent) { setReadOnly(true); } + + virtual QSize minimumSizeHint() const; + virtual QSize sizeHint() const; +}; + +QSize QErrorMessageTextView::minimumSizeHint() const +{ +#ifdef Q_OS_WINCE + if (qt_wince_is_mobile()) + if (qt_wince_is_high_dpi()) + return QSize(200, 200); + else + return QSize(100, 100); + else + return QSize(70, 70); +#else + return QSize(50, 50); +#endif +} + +QSize QErrorMessageTextView::sizeHint() const +{ +#ifdef Q_OS_WINCE + if (qt_wince_is_mobile()) + if (qt_wince_is_high_dpi()) + return QSize(400, 200); + else + return QSize(320, 120); + else + return QSize(300, 100); +#else + return QSize(250, 75); +#endif +} + +/*! + \class QErrorMessage + + \brief The QErrorMessage class provides an error message display dialog. + + \ingroup dialogs + \ingroup misc + + An error message widget consists of a text label and a checkbox. The + checkbox lets the user control whether the same error message will be + displayed again in the future, typically displaying the text, + "Show this message again" translated into the appropriate local + language. + + For production applications, the class can be used to display messages which + the user only needs to see once. To use QErrorMessage like this, you create + the dialog in the usual way, and show it by calling the showMessage() slot or + connecting signals to it. + + The static qtHandler() function installs a message handler + using qInstallMsgHandler() and creates a QErrorMessage that displays + qDebug(), qWarning() and qFatal() messages. This is most useful in + environments where no console is available to display warnings and + error messages. + + In both cases QErrorMessage will queue pending messages and display + them in order, with each new message being shown as soon as the user + has accepted the previous message. Once the user has specified that a + message is not to be shown again it is automatically skipped, and the + dialog will show the next appropriate message in the queue. + + The \l{dialogs/standarddialogs}{Standard Dialogs} example shows + how to use QErrorMessage as well as other built-in Qt dialogs. + + \img qerrormessage.png + + \sa QMessageBox, QStatusBar::showMessage(), {Standard Dialogs Example} +*/ + +static QErrorMessage * qtMessageHandler = 0; + +static void deleteStaticcQErrorMessage() // post-routine +{ + if (qtMessageHandler) { + delete qtMessageHandler; + qtMessageHandler = 0; + } +} + +static bool metFatal = false; + +static void jump(QtMsgType t, const char * m) +{ + if (!qtMessageHandler) + return; + + QString rich; + + switch (t) { + case QtDebugMsg: + default: + rich = QErrorMessage::tr("Debug Message:"); + break; + case QtWarningMsg: + rich = QErrorMessage::tr("Warning:"); + break; + case QtFatalMsg: + rich = QErrorMessage::tr("Fatal Error:"); + } + rich = QString::fromLatin1("<p><b>%1</b></p>").arg(rich); + rich += Qt::convertFromPlainText(QLatin1String(m), Qt::WhiteSpaceNormal); + + // ### work around text engine quirk + if (rich.endsWith(QLatin1String("</p>"))) + rich.chop(4); + + if (!metFatal) { + if (QThread::currentThread() == qApp->thread()) { + qtMessageHandler->showMessage(rich); + } else { + QMetaObject::invokeMethod(qtMessageHandler, + "showMessage", + Qt::QueuedConnection, + Q_ARG(QString, rich)); + } + metFatal = (t == QtFatalMsg); + } +} + + +/*! + Constructs and installs an error handler window with the given \a + parent. +*/ + +QErrorMessage::QErrorMessage(QWidget * parent) + : QDialog(*new QErrorMessagePrivate, parent) +{ + Q_D(QErrorMessage); + QGridLayout * grid = new QGridLayout(this); + d->icon = new QLabel(this); +#ifndef QT_NO_MESSAGEBOX + d->icon->setPixmap(QMessageBox::standardIcon(QMessageBox::Information)); + d->icon->setAlignment(Qt::AlignHCenter | Qt::AlignTop); +#endif + grid->addWidget(d->icon, 0, 0, Qt::AlignTop); + d->errors = new QErrorMessageTextView(this); + grid->addWidget(d->errors, 0, 1); + d->again = new QCheckBox(this); + d->again->setChecked(true); + grid->addWidget(d->again, 1, 1, Qt::AlignTop); + d->ok = new QPushButton(this); +#ifdef Q_OS_WINCE + d->ok->setFixedSize(0,0); +#endif + connect(d->ok, SIGNAL(clicked()), this, SLOT(accept())); + d->ok->setFocus(); + grid->addWidget(d->ok, 2, 0, 1, 2, Qt::AlignCenter); + grid->setColumnStretch(1, 42); + grid->setRowStretch(0, 42); + d->retranslateStrings(); +} + + +/*! + Destroys the error message dialog. +*/ + +QErrorMessage::~QErrorMessage() +{ + if (this == qtMessageHandler) { + qtMessageHandler = 0; + QtMsgHandler tmp = qInstallMsgHandler(0); + // in case someone else has later stuck in another... + if (tmp != jump) + qInstallMsgHandler(tmp); + } +} + + +/*! \reimp */ + +void QErrorMessage::done(int a) +{ + Q_D(QErrorMessage); + if (!d->again->isChecked() && !d->currentMessage.isEmpty() && d->currentType.isEmpty()) { + d->doNotShow.insert(d->currentMessage); + } + if (!d->again->isChecked() && !d->currentType.isEmpty()) { + d->doNotShowType.insert(d->currentType); + } + d->currentMessage.clear(); + d->currentType.clear(); + if (!d->nextPending()) { + QDialog::done(a); + if (this == qtMessageHandler && metFatal) + exit(1); + } +} + + +/*! + Returns a pointer to a QErrorMessage object that outputs the + default Qt messages. This function creates such an object, if there + isn't one already. +*/ + +QErrorMessage * QErrorMessage::qtHandler() +{ + if (!qtMessageHandler) { + qtMessageHandler = new QErrorMessage(0); + qAddPostRoutine(deleteStaticcQErrorMessage); // clean up + qtMessageHandler->setWindowTitle(qApp->applicationName()); + qInstallMsgHandler(jump); + } + return qtMessageHandler; +} + + +/*! \internal */ + +bool QErrorMessagePrivate::nextPending() +{ + while (!pending.isEmpty()) { + QPair<QString,QString> pendingMessage = pending.dequeue(); + QString message = pendingMessage.first; + QString type = pendingMessage.second; + if (!message.isEmpty() && ((type.isEmpty() && !doNotShow.contains(message)) || (!type.isEmpty() && !doNotShowType.contains(type)))) { +#ifndef QT_NO_TEXTHTMLPARSER + errors->setHtml(message); +#else + errors->setPlainText(message); +#endif + currentMessage = message; + currentType = type; + return true; + } + } + return false; +} + + +/*! + Shows the given message, \a message, and returns immediately. If the user + has requested for the message not to be shown again, this function does + nothing. + + Normally, the message is displayed immediately. However, if there are + pending messages, it will be queued to be displayed later. +*/ + +void QErrorMessage::showMessage(const QString &message) +{ + Q_D(QErrorMessage); + if (d->doNotShow.contains(message)) + return; + d->pending.enqueue(qMakePair(message,QString())); + if (!isVisible() && d->nextPending()) + show(); +} + +/*! + \since 4.5 + \overload + + Shows the given message, \a message, and returns immediately. If the user + has requested for messages of type, \a type, not to be shown again, this + function does nothing. + + Normally, the message is displayed immediately. However, if there are + pending messages, it will be queued to be displayed later. + + \sa showMessage() +*/ + +void QErrorMessage::showMessage(const QString &message, const QString &type) +{ + Q_D(QErrorMessage); + if (d->doNotShow.contains(message) && d->doNotShowType.contains(type)) + return; + d->pending.push_back(qMakePair(message,type)); + if (!isVisible() && d->nextPending()) + show(); +} + + +/*! + \reimp +*/ +void QErrorMessage::changeEvent(QEvent *e) +{ + Q_D(QErrorMessage); + if (e->type() == QEvent::LanguageChange) { + d->retranslateStrings(); + } + QDialog::changeEvent(e); +} + +void QErrorMessagePrivate::retranslateStrings() +{ + again->setText(QErrorMessage::tr("&Show this message again")); + ok->setText(QErrorMessage::tr("&OK")); +} + +/*! + \fn void QErrorMessage::message(const QString & message) + + Use showMessage(\a message) instead. +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_ERRORMESSAGE diff --git a/src/gui/dialogs/qerrormessage.h b/src/gui/dialogs/qerrormessage.h new file mode 100644 index 0000000000..798631d52b --- /dev/null +++ b/src/gui/dialogs/qerrormessage.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QERRORMESSAGE_H +#define QERRORMESSAGE_H + +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_ERRORMESSAGE + +class QErrorMessagePrivate; + +class Q_GUI_EXPORT QErrorMessage: public QDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QErrorMessage) +public: + explicit QErrorMessage(QWidget* parent = 0); + ~QErrorMessage(); + + static QErrorMessage * qtHandler(); + +public Q_SLOTS: + void showMessage(const QString &message); + void showMessage(const QString &message, const QString &type); +#ifdef QT3_SUPPORT + inline QT_MOC_COMPAT void message(const QString &text) { showMessage(text); } +#endif + +protected: + void done(int); + void changeEvent(QEvent *e); + +private: + Q_DISABLE_COPY(QErrorMessage) +}; + +#endif // QT_NO_ERRORMESSAGE + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QERRORMESSAGE_H diff --git a/src/gui/dialogs/qfiledialog.cpp b/src/gui/dialogs/qfiledialog.cpp new file mode 100644 index 0000000000..2ce55632e4 --- /dev/null +++ b/src/gui/dialogs/qfiledialog.cpp @@ -0,0 +1,3299 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qvariant.h> +#include <private/qwidgetitemdata_p.h> +#include "qfiledialog.h" + +#ifndef QT_NO_FILEDIALOG +#include "qfiledialog_p.h" +#include <qfontmetrics.h> +#include <qaction.h> +#include <qheaderview.h> +#include <qshortcut.h> +#include <qgridlayout.h> +#include <qmenu.h> +#include <qmessagebox.h> +#include <qinputdialog.h> +#include <stdlib.h> +#include <qsettings.h> +#include <qdebug.h> +#include <qapplication.h> +#include <qstylepainter.h> +#ifndef Q_OS_WINCE +#include "ui_qfiledialog.h" +#else +#include "ui_qfiledialog_wince.h" +extern bool qt_priv_ptr_valid; +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QString, lastVisitedDir) + +/* + \internal + + Exported hooks that can be used to customize the static functions. + */ +typedef QString (*_qt_filedialog_existing_directory_hook)(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options); +Q_GUI_EXPORT _qt_filedialog_existing_directory_hook qt_filedialog_existing_directory_hook = 0; + +typedef QString (*_qt_filedialog_open_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); +Q_GUI_EXPORT _qt_filedialog_open_filename_hook qt_filedialog_open_filename_hook = 0; + +typedef QStringList (*_qt_filedialog_open_filenames_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); +Q_GUI_EXPORT _qt_filedialog_open_filenames_hook qt_filedialog_open_filenames_hook = 0; + +typedef QString (*_qt_filedialog_save_filename_hook)(QWidget * parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options); +Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook = 0; + +/*! + \class QFileDialog + \brief The QFileDialog class provides a dialog that allow users to select files or directories. + \ingroup dialogs + \mainclass + + The QFileDialog class enables a user to traverse the file system in + order to select one or many files or a directory. + + The easiest way to create a QFileDialog is to use the static + functions. On Windows, these static functions will call the native + Windows file dialog, and on Mac OS X these static function will call + the native Mac OS X file dialog. + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 0 + + In the above example, a modal QFileDialog is created using a static + function. The dialog initially displays the contents of the "/home/jana" + directory, and displays files matching the patterns given in the + string "Image Files (*.png *.jpg *.bmp)". The parent of the file dialog + is set to \e this, and the window title is set to "Open Image". + + If you want to use multiple filters, separate each one with + \e two semicolons. For example: + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 1 + + You can create your own QFileDialog without using the static + functions. By calling setFileMode(), you can specify what the user must + select in the dialog: + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 2 + + In the above example, the mode of the file dialog is set to + AnyFile, meaning that the user can select any file, or even specify a + file that doesn't exist. This mode is useful for creating a + "Save As" file dialog. Use ExistingFile if the user must select an + existing file, or \l Directory if only a directory may be selected. + See the \l QFileDialog::FileMode enum for the complete list of modes. + + The fileMode property contains the mode of operation for the dialog; + this indicates what types of objects the user is expected to select. + Use setNameFilter() to set the dialog's file filter. For example: + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 3 + + In the above example, the filter is set to \c{"Images (*.png *.xpm *.jpg)"}, + this means that only files with the extension \c png, \c xpm, + or \c jpg will be shown in the QFileDialog. You can apply + several filters by using setNameFilters(). Use selectNameFilter() to select + one of the filters you've given as the file dialog's default filter. + + The file dialog has two view modes: \l{QFileDialog::}{List} and + \l{QFileDialog::}{Detail}. + \l{QFileDialog::}{List} presents the contents of the current directory + as a list of file and directory names. \l{QFileDialog::}{Detail} also + displays a list of file and directory names, but provides additional + information alongside each name, such as the file size and modification + date. Set the mode with setViewMode(): + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 4 + + The last important function you will need to use when creating your + own file dialog is selectedFiles(). + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 5 + + In the above example, a modal file dialog is created and shown. If + the user clicked OK, the file they selected is put in \c fileName. + + The dialog's working directory can be set with setDirectory(). + Each file in the current directory can be selected using + the selectFile() function. + + The \l{dialogs/standarddialogs}{Standard Dialogs} example shows + how to use QFileDialog as well as other built-in Qt dialogs. + + \sa QDir, QFileInfo, QFile, QPrintDialog, QColorDialog, QFontDialog, {Standard Dialogs Example}, + {Application Example} +*/ + +/*! + \enum QFileDialog::AcceptMode + + \value AcceptOpen + \value AcceptSave +*/ + +/*! + \enum QFileDialog::ViewMode + + This enum describes the view mode of the file dialog; i.e. what + information about each file will be displayed. + + \value Detail Displays an icon, a name, and details for each item in + the directory. + \value List Displays only an icon and a name for each item in the + directory. + + \sa setViewMode() +*/ + +/*! + \enum QFileDialog::FileMode + + This enum is used to indicate what the user may select in the file + dialog; i.e. what the dialog will return if the user clicks OK. + + \value AnyFile The name of a file, whether it exists or not. + \value ExistingFile The name of a single existing file. + \value Directory The name of a directory. Both files and + directories are displayed. + \value ExistingFiles The names of zero or more existing files. + + This value is obsolete since Qt 4.5: + + \value DirectoryOnly Use \c Directory and setOption(ShowDirsOnly, true) instead. + + \sa setFileMode() +*/ + +/*! + \enum QFileDialog::Option + + \value ShowDirsOnly Only show directories in the file dialog. By default both files and + directories are shown. (Valid only in the \l Directory file mode.) + \value DontResolveSymlinks Don't resolve symlinks in the file dialog. By default symlinks + are resolved. + \value DontConfirmOverwrite Don't ask for confirmation if an existing file is selected. + By default confirmation is requested. + \value DontUseNativeDialog Don't use the native file dialog. By default on Mac OS X and Windows, + the native file dialog is used unless you use a subclass of QFileDialog that contains the + Q_OBJECT macro. + \value ReadOnly Indicates that the model is readonly. + \value HideNameFilterDetails Indicates if the is hidden or not. + + This value is obsolete and does nothing since Qt 4.5: + + \value DontUseSheet In previous versions of Qt, the static functions would + create a sheet by default if the static function was given a parent. This + is no longer supported in Qt 4.5, The static functions will always be an + application modal dialog. If you want to use sheets, use + QFileDialog::open() instead. + +*/ + +/*! + \enum QFileDialog::DialogLabel + + \value LookIn + \value FileName + \value FileType + \value Accept + \value Reject +*/ + +/*! + \fn void QFileDialog::filesSelected(const QStringList &selected) + + When the selection changes and the dialog is accepted, this signal is + emitted with the (possibly empty) list of \a selected files. + + \sa currentChanged(), QDialog::Accepted +*/ + + +/*! + \fn void QFileDialog::fileSelected(const QString &file) + + When the selection changes and the dialog is accepted, this signal is + emitted with the (possibly empty) selected \a file. + + \sa currentChanged(), QDialog::Accepted +*/ + + +/*! + \fn void QFileDialog::currentChanged(const QString &path) + + When the current file changes, this signal is emitted with the + new file name as the \a path parameter. + + \sa filesSelected() +*/ + +/*! + \fn void QFileDialog::directoryEntered(const QString &directory) + \since 4.3 + + This signal is emitted when the user enters a \a directory. +*/ + +/*! + \fn void QFileDialog::filterSelected(const QString &filter) + \since 4.3 + + This signal is emitted when the user selects a \a filter. +*/ + +#if defined(Q_WS_WIN) || defined(Q_WS_MAC) +bool Q_GUI_EXPORT qt_use_native_dialogs = true; // for the benefit of testing tools, until we have a proper API +#endif + +QT_BEGIN_INCLUDE_NAMESPACE +#ifdef Q_WS_WIN +#include <qwindowsstyle.h> +#endif +#include <qshortcut.h> +#ifdef Q_WS_MAC +#include <private/qunicodetables_p.h> +#include <qmacstyle_mac.h> +#endif +QT_END_INCLUDE_NAMESPACE + +/*! + \fn QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags flags) + + Constructs a file dialog with the given \a parent and widget \a flags. +*/ +QFileDialog::QFileDialog(QWidget *parent, Qt::WindowFlags f) + : QDialog(*new QFileDialogPrivate, parent, f) +{ + Q_D(QFileDialog); + d->init(); +} + +/*! + Constructs a file dialog with the given \a parent and \a caption that + initially displays the contents of the specified \a directory. + The contents of the directory are filtered before being shown in the + dialog, using a semicolon-separated list of filters specified by + \a filter. +*/ +QFileDialog::QFileDialog(QWidget *parent, + const QString &caption, + const QString &directory, + const QString &filter) + : QDialog(*new QFileDialogPrivate, parent, 0) +{ + Q_D(QFileDialog); + d->init(directory, filter, caption); +} + +/*! + \internal +*/ +QFileDialog::QFileDialog(const QFileDialogArgs &args) + : QDialog(*new QFileDialogPrivate, args.parent, 0) +{ + Q_D(QFileDialog); + d->init(args.directory, args.filter, args.caption); + setFileMode(args.mode); + setOptions(args.options); + selectFile(args.selection); + d->lineEdit()->selectAll(); +} + +/*! + Destroys the file dialog. +*/ +QFileDialog::~QFileDialog() +{ + Q_D(QFileDialog); +#ifndef QT_NO_SETTINGS + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + settings.setValue(QLatin1String("filedialog"), saveState()); +#endif + delete d->qFileDialogUi; + d->deleteNativeDialog_sys(); +} + +/*! + \since 4.3 + Sets the \a urls that are located in the sidebar. + + For instance: + + \snippet doc/src/snippets/filedialogurls.cpp 0 + + The file dialog will then look like this: + + \image filedialogurls.png + + \sa sidebarUrls() +*/ +void QFileDialog::setSidebarUrls(const QList<QUrl> &urls) +{ + Q_D(QFileDialog); + d->qFileDialogUi->sidebar->setUrls(urls); +} + +/*! + \since 4.3 + Returns a list of urls that are currently in the sidebar +*/ +QList<QUrl> QFileDialog::sidebarUrls() const +{ + Q_D(const QFileDialog); + return d->qFileDialogUi->sidebar->urls(); +} + +static const qint32 QFileDialogMagic = 0xbe; + +/*! + \since 4.3 + Saves the state of the dialog's layout, history and current directory. + + Typically this is used in conjunction with QSettings to remember the size + for a future session. A version number is stored as part of the data. +*/ +QByteArray QFileDialog::saveState() const +{ + Q_D(const QFileDialog); + int version = 3; + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + stream << qint32(QFileDialogMagic); + stream << qint32(version); + stream << d->qFileDialogUi->splitter->saveState(); + stream << d->qFileDialogUi->sidebar->urls(); + stream << history(); + stream << *lastVisitedDir(); + stream << d->qFileDialogUi->treeView->header()->saveState(); + stream << qint32(viewMode()); + return data; +} + +/*! + \since 4.3 + Restores the dialogs's layout, history and current directory to the \a state specified. + + Typically this is used in conjunction with QSettings to restore the size + from a past session. + + Returns false if there are errors +*/ +bool QFileDialog::restoreState(const QByteArray &state) +{ + Q_D(QFileDialog); + int version = 3; + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + if (stream.atEnd()) + return false; + QByteArray splitterState; + QByteArray headerData; + QList<QUrl> bookmarks; + QStringList history; + QString currentDirectory; + qint32 marker; + qint32 v; + qint32 viewMode; + stream >> marker; + stream >> v; + if (marker != QFileDialogMagic || v != version) + return false; + + stream >> splitterState + >> bookmarks + >> history + >> currentDirectory + >> headerData + >> viewMode; + + if (!d->qFileDialogUi->splitter->restoreState(splitterState)) + return false; + QList<int> list = d->qFileDialogUi->splitter->sizes(); + if (list.count() >= 2 && list.at(0) == 0 && list.at(1) == 0) { + for (int i = 0; i < list.count(); ++i) + list[i] = d->qFileDialogUi->splitter->widget(i)->sizeHint().width(); + d->qFileDialogUi->splitter->setSizes(list); + } + + d->qFileDialogUi->sidebar->setUrls(bookmarks); + while (history.count() > 5) + history.pop_front(); + setHistory(history); + setDirectory(lastVisitedDir()->isEmpty() ? currentDirectory : *lastVisitedDir()); + if (!d->qFileDialogUi->treeView->header()->restoreState(headerData)) + return false; + + setViewMode(ViewMode(viewMode)); + return true; +} + +/*! + \reimp +*/ +void QFileDialog::changeEvent(QEvent *e) +{ + Q_D(QFileDialog); + if (e->type() == QEvent::LanguageChange) { + d->retranslateWindowTitle(); + d->retranslateStrings(); + } + QDialog::changeEvent(e); +} + +void QFileDialogPrivate::retranslateWindowTitle() +{ + Q_Q(QFileDialog); + if (!useDefaultCaption || setWindowTitle != q->windowTitle()) + return; + if (acceptMode == QFileDialog::AcceptOpen) { + if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) + q->setWindowTitle(QFileDialog::tr("Find Directory")); + else + q->setWindowTitle(QFileDialog::tr("Open")); + } else + q->setWindowTitle(QFileDialog::tr("Save As")); + + setWindowTitle = q->windowTitle(); +} + +void QFileDialogPrivate::setLastVisitedDirectory(const QString &dir) +{ + *lastVisitedDir() = dir; +} + +void QFileDialogPrivate::retranslateStrings() +{ + Q_Q(QFileDialog); + /* WIDGETS */ + if (defaultFileTypes) + q->setNameFilter(QFileDialog::tr("All Files (*)")); + + QList<QAction*> actions = qFileDialogUi->treeView->header()->actions(); + QAbstractItemModel *abstractModel = model; +#ifndef QT_NO_PROXYMODEL + if (proxyModel) + abstractModel = proxyModel; +#endif + int total = qMin(abstractModel->columnCount(QModelIndex()), actions.count() + 1); + for (int i = 1; i < total; ++i) { + actions.at(i - 1)->setText(QFileDialog::tr("Show ") + abstractModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString()); + } + + /* MENU ACTIONS */ + renameAction->setText(QFileDialog::tr("&Rename")); + deleteAction->setText(QFileDialog::tr("&Delete")); + showHiddenAction->setText(QFileDialog::tr("Show &hidden files")); + newFolderAction->setText(QFileDialog::tr("&New Folder")); + qFileDialogUi->retranslateUi(q); + + if (!fileNameLabelExplicitlySat){ + if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) { + q->setLabelText(QFileDialog::FileName, QFileDialog::tr("Directory:")); + } else { + q->setLabelText(QFileDialog::FileName, QFileDialog::tr("File &name:")); + } + fileNameLabelExplicitlySat = false; + } +} + +void QFileDialogPrivate::emitFilesSelected(const QStringList &files) +{ + Q_Q(QFileDialog); + emit q->filesSelected(files); + if (files.count() == 1) + emit q->fileSelected(files.first()); +} + +bool QFileDialogPrivate::canBeNativeDialog() +{ + Q_Q(QFileDialog); + if (nativeDialogInUse) + return true; + if (q->testAttribute(Qt::WA_DontShowOnScreen)) + return false; + if (opts & QFileDialog::DontUseNativeDialog) + return false; + + QLatin1String staticName(QFileDialog::staticMetaObject.className()); + QLatin1String dynamicName(q->metaObject()->className()); + return (staticName == dynamicName); +} + +/*! + Sets the given \a option to be enabled if \a on is true; + otherwise, clears the given \a option. + + \sa options, testOption() +*/ +void QFileDialog::setOption(Option option, bool on) +{ + Q_D(QFileDialog); + if (!(d->opts & option) != !on) + setOptions(d->opts ^ option); +} + +/*! + \since 4.5 + + Returns true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption() +*/ +bool QFileDialog::testOption(Option option) const +{ + Q_D(const QFileDialog); + return (d->opts & option) != 0; +} + +/*! + \property QFileDialog::options + \brief the various options that affect the look and feel of the dialog + \since 4.5 + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the + dialog is visible is not guaranteed to have an immediate effect on the + dialog (depending on the option and on the platform). + + \sa setOption(), testOption() +*/ +void QFileDialog::setOptions(Options options) +{ + Q_D(QFileDialog); + + Options changed = (options ^ d->opts); + if (!changed) + return; + + d->opts = options; + if (changed & DontResolveSymlinks) + d->model->setResolveSymlinks(!(options & DontResolveSymlinks)); + if (changed & ReadOnly) { + bool ro = (options & ReadOnly); + d->model->setReadOnly(ro); + d->qFileDialogUi->newFolderButton->setEnabled(!ro); + d->renameAction->setEnabled(!ro); + d->deleteAction->setEnabled(!ro); + } + if (changed & HideNameFilterDetails) + setNameFilters(d->nameFilters); +} + +QFileDialog::Options QFileDialog::options() const +{ + Q_D(const QFileDialog); + return d->opts; +} + +/*! + \overload + + \since 4.5 + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QFileDialog::open(QObject *receiver, const char *member) +{ + Q_D(QFileDialog); + const char *signal = (fileMode() == ExistingFiles) ? SIGNAL(filesSelected(const QStringList&)) + : SIGNAL(fileSelected(const QString&)); + connect(this, signal, receiver, member); + d->signalToDisconnectOnClose = signal; + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + + QDialog::open(); +} + + +/*! + \reimp +*/ +void QFileDialog::setVisible(bool visible) +{ + Q_D(QFileDialog); + if (visible){ + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) + return; + } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) + return; + + if (d->canBeNativeDialog()){ + if (d->setVisible_sys(visible)){ + d->nativeDialogInUse = true; + // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below + // updates the state correctly, but skips showing the non-native version: + setAttribute(Qt::WA_DontShowOnScreen); + } else { + d->nativeDialogInUse = false; + setAttribute(Qt::WA_DontShowOnScreen, false); + } + } + + if (!d->nativeDialogInUse) + d->qFileDialogUi->fileNameEdit->setFocus(); + + QDialog::setVisible(visible); +} + +/*! + \internal + set the directory to url +*/ +void QFileDialogPrivate::_q_goToUrl(const QUrl &url) +{ + QModelIndex idx = model->index(url.toLocalFile()); + _q_enterDirectory(idx); +} + +/*! + \fn void QFileDialog::setDirectory(const QDir &directory) + + \overload +*/ + +/*! + Sets the file dialog's current \a directory. +*/ +void QFileDialog::setDirectory(const QString &directory) +{ + Q_D(QFileDialog); + QString newDirectory = directory; + QFileInfo info(directory); + //we remove .. and . from the given path if exist + if (!directory.isEmpty()) + newDirectory = QDir::cleanPath(directory); + + if (!directory.isEmpty() && newDirectory.isEmpty()) + return; + + d->setLastVisitedDirectory(newDirectory); + + if (d->nativeDialogInUse){ + d->setDirectory_sys(newDirectory); + return; + } + if (d->rootPath() == newDirectory) + return; + QModelIndex root = d->model->setRootPath(newDirectory); + d->qFileDialogUi->newFolderButton->setEnabled(d->model->flags(root) & Qt::ItemIsDropEnabled); + if (root != d->rootIndex()) { +#ifndef QT_NO_COMPLETER + if (directory.endsWith(QLatin1Char('/'))) + d->completer->setCompletionPrefix(newDirectory); + else + d->completer->setCompletionPrefix(newDirectory + QLatin1Char('/')); +#endif + d->setRootIndex(root); + } + d->qFileDialogUi->listView->selectionModel()->clear(); +} + +/*! + Returns the directory currently being displayed in the dialog. +*/ +QDir QFileDialog::directory() const +{ + Q_D(const QFileDialog); + return QDir(d->nativeDialogInUse ? d->directory_sys() : d->rootPath()); +} + +/*! + Selects the given \a filename in the file dialog. + + \sa selectedFiles() +*/ +void QFileDialog::selectFile(const QString &filename) +{ + Q_D(QFileDialog); + if (filename.isEmpty()) + return; + + if (d->nativeDialogInUse){ + d->selectFile_sys(filename); + return; + } + + if (!QDir::isRelativePath(filename)) { + QFileInfo info(filename); + QString filenamePath = info.absoluteDir().path(); + + if (d->model->rootPath() != filenamePath) + setDirectory(filenamePath); + } + + QModelIndex index = d->model->index(filename); + if (!index.isValid()) { + // save as dialog where we want to input a default value + QString text = filename; + if (QFileInfo(filename).isAbsolute()) { + QString current = d->rootPath(); + text.remove(current); + if (text.at(0) == QDir::separator() +#ifdef Q_OS_WIN + //On Windows both cases can happen + || text.at(0) == QLatin1Char('/') +#endif + ) + text = text.remove(0,1); + } + if (!isVisible() || !d->lineEdit()->hasFocus()) + d->lineEdit()->setText(text); + } else { + d->qFileDialogUi->listView->selectionModel()->clear(); + if (!isVisible() || !d->lineEdit()->hasFocus()) + d->lineEdit()->setText(index.data().toString()); + } +} + +/** + Returns the text in the line edit which can be one or more file names + */ +QStringList QFileDialogPrivate::typedFiles() const +{ + QStringList files; + QString editText = lineEdit()->text(); + if (!editText.contains(QLatin1Char('"'))) + files << editText; + else { + // " is used to separate files like so: "file1" "file2" "file3" ... + // ### need escape character for filenames with quotes (") + QStringList tokens = editText.split(QLatin1Char('\"')); + for (int i=0; i<tokens.size(); ++i) { + if ((i % 2) == 0) + continue; // Every even token is a separator + files << toInternal(tokens.at(i)); + } + } + return addDefaultSuffixToFiles(files); +} + +QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList filesToFix) const +{ + QStringList files; + for (int i=0; i<filesToFix.size(); ++i) { + QString name = toInternal(filesToFix.at(i)); + QFileInfo info(name); + // if the filename has no suffix, add the default suffix + if (!defaultSuffix.isEmpty() && !info.isDir() && name.lastIndexOf(QLatin1Char('.')) == -1) + name += QLatin1Char('.') + defaultSuffix; + if (info.isAbsolute()) { + files.append(name); + } else { + // at this point the path should only have Qt path separators. + // This check is needed since we might be at the root directory + // and on Windows it already ends with slash. + QString path = rootPath(); + if (!path.endsWith(QLatin1String("/"))) + path += QLatin1String("/"); + path += name; + files.append(path); + } + } + return files; +} + + +/*! + Returns a list of strings containing the absolute paths of the + selected files in the dialog. If no files are selected, or + the mode is not ExistingFiles or ExistingFile, selectedFiles() contains the current path in the viewport. + + \sa selectedNameFilter(), selectFile() +*/ +QStringList QFileDialog::selectedFiles() const +{ + Q_D(const QFileDialog); + if (d->nativeDialogInUse) + return d->addDefaultSuffixToFiles(d->selectedFiles_sys()); + + QModelIndexList indexes = d->qFileDialogUi->listView->selectionModel()->selectedRows(); + QStringList files; + for (int i = 0; i < indexes.count(); ++i) + files.append(indexes.at(i).data(QFileSystemModel::FilePathRole).toString()); + + if (files.isEmpty() && !d->lineEdit()->text().isEmpty()) + files = d->typedFiles(); + + if (files.isEmpty() && !(d->fileMode == ExistingFile || d->fileMode == ExistingFiles)) + files.append(d->rootIndex().data(QFileSystemModel::FilePathRole).toString()); + return files; +} + +/* + Makes a list of filters from ;;-separated text. + Used by the mac and windows implementations +*/ +QStringList qt_make_filter_list(const QString &filter) +{ + QString f(filter); + + if (f.isEmpty()) + return QStringList(); + + QString sep(QLatin1String(";;")); + int i = f.indexOf(sep, 0); + if (i == -1) { + if (f.indexOf(QLatin1Char('\n'), 0) != -1) { + sep = QLatin1Char('\n'); + i = f.indexOf(sep, 0); + } + } + + return f.split(sep); +} + +/*! + \since 4.4 + + Sets the filter used in the file dialog to the given \a filter. + + If \a filter contains a pair of parentheses containing one or more + of \bold{anything*something}, separated by spaces, then only the + text contained in the parentheses is used as the filter. This means + that these calls are all equivalent: + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 6 + + \sa setNameFilters() +*/ +void QFileDialog::setNameFilter(const QString &filter) +{ + setNameFilters(qt_make_filter_list(filter)); +} + +/*! + \obsolete + + Use setNameFilter() instead. +*/ +void QFileDialog::setFilter(const QString &filter) +{ + setNameFilter(filter); +} + +/*! + \property QFileDialog::nameFilterDetailsVisible + \obsolete + \brief This property holds whether the filter details is shown or not. + \since 4.4 + + When this property is true (the default), the filter details are shown + in the combo box. When the property is set to false, these are hidden. + + Use setOption(HideNameFilterDetails, !\e enabled) or + !testOption(HideNameFilterDetails). +*/ +void QFileDialog::setNameFilterDetailsVisible(bool enabled) +{ + setOption(HideNameFilterDetails, !enabled); +} + +bool QFileDialog::isNameFilterDetailsVisible() const +{ + return !testOption(HideNameFilterDetails); +} + + +/*! + \since 4.4 + + Sets the \a filters used in the file dialog. + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 7 +*/ +void QFileDialog::setNameFilters(const QStringList &filters) +{ + Q_D(QFileDialog); + d->defaultFileTypes = (filters == QStringList(QFileDialog::tr("All Files (*)"))); + d->nameFilters = filters; + + if (d->nativeDialogInUse){ + d->setNameFilters_sys(filters); + return; + } + + d->qFileDialogUi->fileTypeCombo->clear(); + if (filters.isEmpty()) + return; + + if (testOption(HideNameFilterDetails)) { + QStringList strippedFilters; + for (int i = 0; i < filters.count(); ++i) { + strippedFilters.append(filters[i].mid(0, filters[i].indexOf(QLatin1String(" (")))); + } + d->qFileDialogUi->fileTypeCombo->addItems(strippedFilters); + } else { + d->qFileDialogUi->fileTypeCombo->addItems(filters); + } + d->_q_useNameFilter(0); +} + +/*! + \obsolete + + Use setNameFilters() instead. +*/ +void QFileDialog::setFilters(const QStringList &filters) +{ + setNameFilters(filters); +} + +/*! + \since 4.4 + + Returns the file type filters that are in operation on this file + dialog. +*/ +QStringList QFileDialog::nameFilters() const +{ + return d_func()->nameFilters; +} + +/*! + \obsolete + + Use nameFilters() instead. +*/ + +QStringList QFileDialog::filters() const +{ + return nameFilters(); +} + +/*! + \since 4.4 + + Sets the current file type \a filter. Multiple filters can be + passed in \a filter by separating them with semicolons or spaces. + + \sa setNameFilter(), setNameFilters(), selectedNameFilter() +*/ +void QFileDialog::selectNameFilter(const QString &filter) +{ + Q_D(QFileDialog); + if (d->nativeDialogInUse) { + d->selectNameFilter_sys(filter); + return; + } + + int i = d->qFileDialogUi->fileTypeCombo->findText(filter); + if (i >= 0) { + d->qFileDialogUi->fileTypeCombo->setCurrentIndex(i); + d->_q_useNameFilter(d->qFileDialogUi->fileTypeCombo->currentIndex()); + } +} + +/*! + \obsolete + + Use selectNameFilter() instead. +*/ + +void QFileDialog::selectFilter(const QString &filter) +{ + selectNameFilter(filter); +} + +/*! + \since 4.4 + + Returns the filter that the user selected in the file dialog. + + \sa selectedFiles() +*/ +QString QFileDialog::selectedNameFilter() const +{ + Q_D(const QFileDialog); + if (d->nativeDialogInUse) + return d->selectedNameFilter_sys(); + + return d->qFileDialogUi->fileTypeCombo->currentText(); +} + +/*! + \obsolete + + Use selectedNameFilter() instead. +*/ +QString QFileDialog::selectedFilter() const +{ + return selectedNameFilter(); +} + +/*! + \since 4.4 + + Returns the filter that is used when displaying files. + + \sa setFilter() +*/ +QDir::Filters QFileDialog::filter() const +{ + Q_D(const QFileDialog); + return d->model->filter(); +} + +/*! + \since 4.4 + + Sets the filter used by the model to \a filters. The filter is used + to specify the kind of files that should be shown. + + \sa filter() +*/ + +void QFileDialog::setFilter(QDir::Filters filters) +{ + Q_D(QFileDialog); + d->model->setFilter(filters); + if (d->nativeDialogInUse){ + d->setFilter_sys(); + return; + } + + d->showHiddenAction->setChecked((filters & QDir::Hidden)); +} + +/*! + \property QFileDialog::viewMode + \brief the way files and directories are displayed in the dialog + + By default, the \c Detail mode is used to display information about + files and directories. + + \sa ViewMode +*/ +void QFileDialog::setViewMode(QFileDialog::ViewMode mode) +{ + Q_D(QFileDialog); + if (mode == Detail) + d->_q_showDetailsView(); + else + d->_q_showListView(); +} + +QFileDialog::ViewMode QFileDialog::viewMode() const +{ + Q_D(const QFileDialog); + return (d->qFileDialogUi->stackedWidget->currentWidget() == d->qFileDialogUi->listView->parent() ? QFileDialog::List : QFileDialog::Detail); +} + +/*! + \property QFileDialog::fileMode + \brief the file mode of the dialog + + The file mode defines the number and type of items that the user is + expected to select in the dialog. + + By default, this property is set to AnyFile. + + This function will set the labels for the FileName and + \l{QFileDialog::}{Accept} \l{DialogLabel}s. It is possible to set + custom text after the call to setFileMode(). + + \sa FileMode +*/ +void QFileDialog::setFileMode(QFileDialog::FileMode mode) +{ + Q_D(QFileDialog); + if (d->nativeDialogInUse){ + d->model->setFilter(d->filterForMode(filter())); + d->setFilter_sys(); + return; + } + + d->fileMode = mode; + d->retranslateWindowTitle(); + + // keep ShowDirsOnly option in sync with fileMode (BTW, DirectoryOnly is obsolete) + setOption(ShowDirsOnly, mode == DirectoryOnly); + + // set selection mode and behavior + QAbstractItemView::SelectionMode selectionMode; + if (mode == QFileDialog::ExistingFiles) + selectionMode = QAbstractItemView::ExtendedSelection; + else + selectionMode = QAbstractItemView::SingleSelection; + d->qFileDialogUi->listView->setSelectionMode(selectionMode); + d->qFileDialogUi->treeView->setSelectionMode(selectionMode); + // set filter + d->model->setFilter(d->filterForMode(filter())); + // setup file type for directory + QString buttonText = (d->acceptMode == AcceptOpen ? tr("&Open") : tr("&Save")); + if (mode == DirectoryOnly || mode == Directory) { + d->qFileDialogUi->fileTypeCombo->clear(); + d->qFileDialogUi->fileTypeCombo->addItem(tr("Directories")); + d->qFileDialogUi->fileTypeCombo->setEnabled(false); + + if (!d->fileNameLabelExplicitlySat){ + setLabelText(FileName, tr("Directory:")); + d->fileNameLabelExplicitlySat = false; + } + buttonText = tr("&Choose"); + } else { + if (!d->fileNameLabelExplicitlySat){ + setLabelText(FileName, tr("File &name:")); + d->fileNameLabelExplicitlySat = false; + } + } + setLabelText(Accept, buttonText); + d->qFileDialogUi->fileTypeCombo->setEnabled(!testOption(ShowDirsOnly)); + d->_q_updateOkButton(); +} + +QFileDialog::FileMode QFileDialog::fileMode() const +{ + Q_D(const QFileDialog); + return d->fileMode; +} + +/*! + \property QFileDialog::acceptMode + \brief the accept mode of the dialog + + The action mode defines whether the dialog is for opening or saving files. + + By default, this property is set to \l{AcceptOpen}. + + \sa AcceptMode +*/ +void QFileDialog::setAcceptMode(QFileDialog::AcceptMode mode) +{ + Q_D(QFileDialog); + d->acceptMode = mode; + bool directoryMode = (d->fileMode == Directory || d->fileMode == DirectoryOnly); + QDialogButtonBox::StandardButton button = (mode == AcceptOpen ? QDialogButtonBox::Open : QDialogButtonBox::Save); + d->qFileDialogUi->buttonBox->setStandardButtons(button | QDialogButtonBox::Cancel); + d->qFileDialogUi->buttonBox->button(button)->setEnabled(false); + d->_q_updateOkButton(); + if (mode == AcceptOpen && directoryMode) + setLabelText(Accept, tr("&Choose")); + else + setLabelText(Accept, (mode == AcceptOpen ? tr("&Open") : tr("&Save"))); + if (mode == AcceptSave) { + d->qFileDialogUi->lookInCombo->setEditable(false); + } + d->retranslateWindowTitle(); +} + +/* + Returns the file system model index that is the root index in the + views +*/ +QModelIndex QFileDialogPrivate::rootIndex() const { + return mapToSource(qFileDialogUi->listView->rootIndex()); +} + +QAbstractItemView *QFileDialogPrivate::currentView() const { + if (!qFileDialogUi->stackedWidget) + return 0; + if (qFileDialogUi->stackedWidget->currentWidget() == qFileDialogUi->listView->parent()) + return qFileDialogUi->listView; + return qFileDialogUi->treeView; +} + +QLineEdit *QFileDialogPrivate::lineEdit() const { + return (QLineEdit*)qFileDialogUi->fileNameEdit; +} + +/* + Sets the view root index to be the file system model index +*/ +void QFileDialogPrivate::setRootIndex(const QModelIndex &index) const { + Q_ASSERT(index.isValid() ? index.model() == model : true); + QModelIndex idx = mapFromSource(index); + qFileDialogUi->treeView->setRootIndex(idx); + qFileDialogUi->listView->setRootIndex(idx); +} +/* + Select a file system model index + returns the index that was selected (or not depending upon sortfilterproxymodel) +*/ +QModelIndex QFileDialogPrivate::select(const QModelIndex &index) const { + Q_ASSERT(index.isValid() ? index.model() == model : true); + + QModelIndex idx = mapFromSource(index); + if (idx.isValid() && !qFileDialogUi->listView->selectionModel()->isSelected(idx)) + qFileDialogUi->listView->selectionModel()->select(idx, + QItemSelectionModel::Select | QItemSelectionModel::Rows); + return idx; +} + +QFileDialog::AcceptMode QFileDialog::acceptMode() const +{ + Q_D(const QFileDialog); + return d->acceptMode; +} + +/*! + \property QFileDialog::readOnly + \obsolete + \brief Whether the filedialog is read-only + + If this property is set to false, the file dialog will allow renaming, + and deleting of files and directories and creating directories. + + Use setOption(ReadOnly, \e enabled) or testOption(ReadOnly) instead. +*/ +void QFileDialog::setReadOnly(bool enabled) +{ + setOption(ReadOnly, enabled); +} + +bool QFileDialog::isReadOnly() const +{ + return testOption(ReadOnly); +} + +/*! + \property QFileDialog::resolveSymlinks + \obsolete + \brief whether the filedialog should resolve shortcuts + + If this property is set to true, the file dialog will resolve + shortcuts or symbolic links. + + Use setOption(DontResolveSymlinks, !\a enabled) or + !testOption(DontResolveSymlinks). +*/ +void QFileDialog::setResolveSymlinks(bool enabled) +{ + setOption(DontResolveSymlinks, !enabled); +} + +bool QFileDialog::resolveSymlinks() const +{ + return !testOption(DontResolveSymlinks); +} + +/*! + \property QFileDialog::confirmOverwrite + \obsolete + \brief whether the filedialog should ask before accepting a selected file, + when the accept mode is AcceptSave + + Use setOption(DontConfirmOverwrite, !\e enabled) or + !testOption(DontConfirmOverwrite) instead. +*/ +void QFileDialog::setConfirmOverwrite(bool enabled) +{ + setOption(DontConfirmOverwrite, !enabled); +} + +bool QFileDialog::confirmOverwrite() const +{ + return !testOption(DontConfirmOverwrite); +} + +/*! + \property QFileDialog::defaultSuffix + \brief suffix added to the filename if no other suffix was specified + + This property specifies a string that will be added to the + filename if it has no suffix already. The suffix is typically + used to indicate the file type (e.g. "txt" indicates a text + file). +*/ +void QFileDialog::setDefaultSuffix(const QString &suffix) +{ + Q_D(QFileDialog); + d->defaultSuffix = suffix; +} + +QString QFileDialog::defaultSuffix() const +{ + Q_D(const QFileDialog); + return d->defaultSuffix; +} + +/*! + Sets the browsing history of the filedialog to contain the given + \a paths. +*/ +void QFileDialog::setHistory(const QStringList &paths) +{ + Q_D(QFileDialog); + d->qFileDialogUi->lookInCombo->setHistory(paths); +} + +void QFileDialogComboBox::setHistory(const QStringList &paths) +{ + m_history = paths; + // Only populate the first item, showPopup will populate the rest if needed + QList<QUrl> list; + QModelIndex idx = d_ptr->model->index(d_ptr->rootPath()); + //On windows the popup display the "C:\", convert to nativeSeparators + QUrl url = QUrl::fromLocalFile(QDir::toNativeSeparators(idx.data(QFileSystemModel::FilePathRole).toString())); + if (url.isValid()) + list.append(url); + urlModel->setUrls(list); +} + +/*! + Returns the browsing history of the filedialog as a list of paths. +*/ +QStringList QFileDialog::history() const +{ + Q_D(const QFileDialog); + QStringList currentHistory = d->qFileDialogUi->lookInCombo->history(); + //On windows the popup display the "C:\", convert to nativeSeparators + QString newHistory = QDir::toNativeSeparators(d->rootIndex().data(QFileSystemModel::FilePathRole).toString()); + if (!currentHistory.contains(newHistory)) + currentHistory << newHistory; + return currentHistory; +} + +/*! + Sets the item delegate used to render items in the views in the + file dialog to the given \a delegate. + + \warning You should not share the same instance of a delegate between views. + Doing so can cause incorrect or unintuitive editing behavior since each + view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} + signal, and attempt to access, modify or close an editor that has already been closed. + + Note that the model used is QFileSystemModel. It has custom item data roles, which is + described by the \l{QFileSystemModel::}{Roles} enum. You can use a QFileIconProvider if + you only want custom icons. + + \sa itemDelegate(), setIconProvider(), QFileSystemModel +*/ +void QFileDialog::setItemDelegate(QAbstractItemDelegate *delegate) +{ + Q_D(QFileDialog); + d->qFileDialogUi->listView->setItemDelegate(delegate); + d->qFileDialogUi->treeView->setItemDelegate(delegate); +} + +/*! + Returns the item delegate used to render the items in the views in the filedialog. +*/ +QAbstractItemDelegate *QFileDialog::itemDelegate() const +{ + Q_D(const QFileDialog); + return d->qFileDialogUi->listView->itemDelegate(); +} + +/*! + Sets the icon provider used by the filedialog to the specified \a provider. +*/ +void QFileDialog::setIconProvider(QFileIconProvider *provider) +{ + Q_D(QFileDialog); + d->model->setIconProvider(provider); +} + +/*! + Returns the icon provider used by the filedialog. +*/ +QFileIconProvider *QFileDialog::iconProvider() const +{ + Q_D(const QFileDialog); + return d->model->iconProvider(); +} + +/*! + Sets the \a text shown in the filedialog in the specified \a label. +*/ +void QFileDialog::setLabelText(DialogLabel label, const QString &text) +{ + Q_D(QFileDialog); + QPushButton *button; + switch (label) { + case LookIn: + d->qFileDialogUi->lookInLabel->setText(text); + break; + case FileName: + d->qFileDialogUi->fileNameLabel->setText(text); + d->fileNameLabelExplicitlySat = true; + break; + case FileType: + d->qFileDialogUi->fileTypeLabel->setText(text); + break; + case Accept: + d->acceptLabel = text; + if (acceptMode() == AcceptOpen) + button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open); + else + button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save); + if (button) + button->setText(text); + break; + case Reject: + button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel); + if (button) + button->setText(text); + break; + } +} + +/*! + Returns the text shown in the filedialog in the specified \a label. +*/ +QString QFileDialog::labelText(DialogLabel label) const +{ + QPushButton *button; + Q_D(const QFileDialog); + switch (label) { + case LookIn: + return d->qFileDialogUi->lookInLabel->text(); + case FileName: + return d->qFileDialogUi->fileNameLabel->text(); + case FileType: + return d->qFileDialogUi->fileTypeLabel->text(); + case Accept: + if (acceptMode() == AcceptOpen) + button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Open); + else + button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Save); + if (button) + return button->text(); + case Reject: + button = d->qFileDialogUi->buttonBox->button(QDialogButtonBox::Cancel); + if (button) + return button->text(); + } + return QString(); +} + +/* + For the native file dialogs +*/ + +#if defined(Q_WS_WIN) +extern QString qt_win_get_open_file_name(const QFileDialogArgs &args, + QString *initialDirectory, + QString *selectedFilter); + +extern QString qt_win_get_save_file_name(const QFileDialogArgs &args, + QString *initialDirectory, + QString *selectedFilter); + +extern QStringList qt_win_get_open_file_names(const QFileDialogArgs &args, + QString *initialDirectory, + QString *selectedFilter); + +extern QString qt_win_get_existing_directory(const QFileDialogArgs &args); +#endif + +/*! + This is a convenience static function that returns an existing file + selected by the user. If the user presses Cancel, it returns a null + string. + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 8 + + The function creates a modal file dialog with the given \a parent widget. + If the parent is not 0, the dialog will be shown centered over the + parent widget. + + The file dialog's working directory will be set to \a dir. If \a + dir includes a file name, the file will be selected. Only files + that match the given \a filter are shown. The filter selected is + set to \a selectedFilter. The parameters \a dir, \a + selectedFilter, and \a filter may be empty strings. If you want + multiple filters, separate them with ';;', for example: + + \code + "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" + \endcode + + The \a options argument holds various + options about how to run the dialog, see the QFileDialog::Option enum for + more information on the flags you can pass. + + The dialog's caption is set to \a caption. If \a caption is not + specified then a default caption will be used. + + Under Windows and Mac OS X, this static function will use the native + file dialog and not a QFileDialog. + + Note that on Windows the dialog will spin a blocking modal event loop + that will not dispatch any QTimers, and if parent is not 0 then it will + position the dialog just under the parent's title bar. + + Under Unix/X11, the normal behavior of the file dialog is to resolve + and follow symlinks. For example, if \c{/usr/tmp} is a symlink to + \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after + entering \c{/usr/tmp}. If \a options includes DontResolveSymlinks, + the file dialog will treat symlinks as regular directories. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFileDialog constructors. + + \sa getOpenFileNames(), getSaveFileName(), getExistingDirectory() +*/ +QString QFileDialog::getOpenFileName(QWidget *parent, + const QString &caption, + const QString &dir, + const QString &filter, + QString *selectedFilter, + Options options) +{ + if (qt_filedialog_open_filename_hook && !(options & DontUseNativeDialog)) + return qt_filedialog_open_filename_hook(parent, caption, dir, filter, selectedFilter, options); + QFileDialogArgs args; + args.parent = parent; + args.caption = caption; + args.directory = QFileDialogPrivate::workingDirectory(dir); + args.selection = QFileDialogPrivate::initialSelection(dir); + args.filter = filter; + args.mode = ExistingFile; + args.options = options; +#if defined(Q_WS_WIN) + if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) { + return qt_win_get_open_file_name(args, &(args.directory), selectedFilter); + } +#endif + + // create a qt dialog + QFileDialog dialog(args); + if (selectedFilter) + dialog.selectNameFilter(*selectedFilter); + if (dialog.exec() == QDialog::Accepted) { + if (selectedFilter) + *selectedFilter = dialog.selectedFilter(); + return dialog.selectedFiles().value(0); + } + return QString(); +} + +/*! + This is a convenience static function that will return one or more + existing files selected by the user. + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 9 + + This function creates a modal file dialog with the given \a parent + widget. If the parent is not 0, the dialog will be shown centered + over the parent widget. + + The file dialog's working directory will be set to \a dir. If \a + dir includes a file name, the file will be selected. The filter + is set to \a filter so that only those files which match the filter + are shown. The filter selected is set to \a selectedFilter. The parameters + \a dir, \a selectedFilter and \a filter may be empty strings. If you + need multiple filters, separate them with ';;', for instance: + + \code + "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" + \endcode + + The dialog's caption is set to \a caption. If \a caption is not + specified then a default caption will be used. + + Under Windows and Mac OS X, this static function will use the native + file dialog and not a QFileDialog. On Mac OS X, the \a dir argument + is ignored, the native dialog always displays the last visited directory. + + Note that on Windows the dialog will spin a blocking modal event loop + that will not dispatch any QTimers, and if parent is not 0 then it will + position the dialog just under the parent's title bar. + + Under Unix/X11, the normal behavior of the file dialog is to resolve + and follow symlinks. For example, if \c{/usr/tmp} is a symlink to + \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after + entering \c{/usr/tmp}. The \a options argument holds various + options about how to run the dialog, see the QFileDialog::Option enum for + more information on the flags you can pass. + + Note that if you want to iterate over the list of files, you should + iterate over a copy. For example: + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 10 + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFileDialog constructors. + + \sa getOpenFileName(), getSaveFileName(), getExistingDirectory() +*/ +QStringList QFileDialog::getOpenFileNames(QWidget *parent, + const QString &caption, + const QString &dir, + const QString &filter, + QString *selectedFilter, + Options options) +{ + if (qt_filedialog_open_filenames_hook) + return qt_filedialog_open_filenames_hook(parent, caption, dir, filter, selectedFilter, options); + QFileDialogArgs args; + args.parent = parent; + args.caption = caption; + args.directory = QFileDialogPrivate::workingDirectory(dir); + args.selection = QFileDialogPrivate::initialSelection(dir); + args.filter = filter; + args.mode = ExistingFiles; + args.options = options; + +#if defined(Q_WS_WIN) + if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) { + return qt_win_get_open_file_names(args, &(args.directory), selectedFilter); + } +#endif + + // create a qt dialog + QFileDialog dialog(args); + if (selectedFilter) + dialog.selectNameFilter(*selectedFilter); + if (dialog.exec() == QDialog::Accepted) { + if (selectedFilter) + *selectedFilter = dialog.selectedFilter(); + return dialog.selectedFiles(); + } + return QStringList(); +} + +/*! + This is a convenience static function that will return a file name + selected by the user. The file does not have to exist. + + It creates a modal file dialog with the given \a parent widget. If the + parent is not 0, the dialog will be shown centered over the parent + widget. + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 11 + + The file dialog's working directory will be set to \a dir. If \a + dir includes a file name, the file will be selected. Only files that + match the \a filter are shown. The filter selected is set to + \a selectedFilter. The parameters \a dir, \a selectedFilter, and + \a filter may be empty strings. Multiple filters are separated with ';;'. + For instance: + + \code + "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" + \endcode + + The \a options argument holds various + options about how to run the dialog, see the QFileDialog::Option enum for + more information on the flags you can pass. + + The default filter can be chosen by setting \a selectedFilter to the desired value. + + The dialog's caption is set to \a caption. If \a caption is not + specified then a default caption will be used. + + Under Windows and Mac OS X, this static function will use the native + file dialog and not a QFileDialog. + + Note that on Windows the dialog will spin a blocking modal event loop + that will not dispatch any QTimers, and if parent is not 0 then it will + position the dialog just under the parent's title bar. + On Mac OS X, the filter argument is ignored. + + Under Unix/X11, the normal behavior of the file dialog is to resolve + and follow symlinks. For example, if \c{/usr/tmp} is a symlink to + \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after + entering \c{/usr/tmp}. If \a options includes DontResolveSymlinks, + the file dialog will treat symlinks as regular directories. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFileDialog constructors. + + \sa getOpenFileName(), getOpenFileNames(), getExistingDirectory() +*/ +QString QFileDialog::getSaveFileName(QWidget *parent, + const QString &caption, + const QString &dir, + const QString &filter, + QString *selectedFilter, + Options options) +{ + if (qt_filedialog_save_filename_hook) + return qt_filedialog_save_filename_hook(parent, caption, dir, filter, selectedFilter, options); + QFileDialogArgs args; + args.parent = parent; + args.caption = caption; + args.directory = QFileDialogPrivate::workingDirectory(dir); + args.selection = QFileDialogPrivate::initialSelection(dir); + args.filter = filter; + args.mode = AnyFile; + args.options = options; + +#if defined(Q_WS_WIN) + if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) { + return qt_win_get_save_file_name(args, &(args.directory), selectedFilter); + } +#endif + + // create a qt dialog + QFileDialog dialog(args); + dialog.setAcceptMode(AcceptSave); + if (selectedFilter) + dialog.selectNameFilter(*selectedFilter); + if (dialog.exec() == QDialog::Accepted) { + if (selectedFilter) + *selectedFilter = dialog.selectedFilter(); + return dialog.selectedFiles().value(0); + } + + return QString(); +} + +/*! + This is a convenience static function that will return an existing + directory selected by the user. + + \snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 12 + + This function creates a modal file dialog with the given \a parent + widget. If the parent is not 0, the dialog will be shown centered over + the parent widget. + + The dialog's working directory is set to \a dir, and the caption is + set to \a caption. Either of these may be an empty string in which case + the current directory and a default caption will be used + respectively. + + The \a options argument holds various + options about how to run the dialog, see the QFileDialog::Option enum for + more information on the flags you can pass. Note that \l{QFileDialog::}{ShowDirsOnly} + must be set to ensure a native file dialog. + + Under Windows and Mac OS X, this static function will use the native + file dialog and not a QFileDialog. On Mac OS X, the \a dir argument + is ignored, the native dialog always displays the last visited directory. + On Windows CE, if the device has no native file dialog, a QFileDialog + will be used. + + Under Unix/X11, the normal behavior of the file dialog is to resolve + and follow symlinks. For example, if \c{/usr/tmp} is a symlink to + \c{/var/tmp}, the file dialog will change to \c{/var/tmp} after + entering \c{/usr/tmp}. If \a options includes DontResolveSymlinks, + the file dialog will treat symlinks as regular directories. + + Note that on Windows the dialog will spin a blocking modal event loop + that will not dispatch any QTimers, and if parent is not 0 then it will + position the dialog just under the parent's title bar. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFileDialog constructors. + + \sa getOpenFileName(), getOpenFileNames(), getSaveFileName() +*/ +QString QFileDialog::getExistingDirectory(QWidget *parent, + const QString &caption, + const QString &dir, + Options options) +{ + if (qt_filedialog_existing_directory_hook) + return qt_filedialog_existing_directory_hook(parent, caption, dir, options); + QFileDialogArgs args; + args.parent = parent; + args.caption = caption; + args.directory = QFileDialogPrivate::workingDirectory(dir); + args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory); + args.options = options; + +#if defined(Q_WS_WIN) + if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog) && (options & ShowDirsOnly) +#if defined(Q_OS_WINCE) + && qt_priv_ptr_valid +#endif + ) { + return qt_win_get_existing_directory(args); + } +#endif + + // create a qt dialog + QFileDialog dialog(args); + if (dialog.exec() == QDialog::Accepted) { + return dialog.selectedFiles().value(0); + } + return QString(); +} + +inline static QString _qt_get_directory(const QString &path) +{ + QFileInfo info = QFileInfo(QDir::current(), path); + if (info.exists() && info.isDir()) + return QDir::cleanPath(info.absoluteFilePath()); + info.setFile(info.absolutePath()); + if (info.exists() && info.isDir()) + return info.absoluteFilePath(); + return QString(); +} +/* + Get the initial directory path + + \sa initialSelection() + */ +QString QFileDialogPrivate::workingDirectory(const QString &path) +{ + if (!path.isEmpty()) { + QString directory = _qt_get_directory(path); + if (!directory.isEmpty()) + return directory; + } + QString directory = _qt_get_directory(*lastVisitedDir()); + if (!directory.isEmpty()) + return directory; + return QDir::currentPath(); +} + +/* + Get the initial selection given a path. The initial directory + can contain both the initial directory and initial selection + /home/user/foo.txt + + \sa workingDirectory() + */ +QString QFileDialogPrivate::initialSelection(const QString &path) +{ + if (!path.isEmpty()) { + QFileInfo info(path); + if (!info.isDir()) + return info.fileName(); + } + return QString(); +} + +/*! + \reimp +*/ +void QFileDialog::done(int result) +{ + Q_D(QFileDialog); + + QDialog::done(result); + + if (d->receiverToDisconnectOnClose) { + disconnect(this, d->signalToDisconnectOnClose, + d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); + d->receiverToDisconnectOnClose = 0; + } + d->memberToDisconnectOnClose.clear(); + d->signalToDisconnectOnClose.clear(); +} + +/*! + \reimp +*/ +void QFileDialog::accept() +{ + Q_D(QFileDialog); + QStringList files = selectedFiles(); + if (files.isEmpty()) + return; + if (d->nativeDialogInUse){ + d->emitFilesSelected(files); + QDialog::accept(); + return; + } + + QString lineEditText = d->lineEdit()->text(); + // "hidden feature" type .. and then enter, and it will move up a dir + // special case for ".." + if (lineEditText == QLatin1String("..")) { + d->_q_navigateToParent(); + bool block = d->qFileDialogUi->fileNameEdit->blockSignals(true); + d->lineEdit()->selectAll(); + d->qFileDialogUi->fileNameEdit->blockSignals(block); + return; + } + + switch (d->fileMode) { + case DirectoryOnly: + case Directory: { + QString fn = files.first(); + QFileInfo info(fn); + if (!info.exists()) + info = QFileInfo(d->getEnvironmentVariable(fn)); + if (!info.exists()) { +#ifndef QT_NO_MESSAGEBOX + QString message = tr("%1\nDirectory not found.\nPlease verify the " + "correct directory name was given."); + QMessageBox::warning(this, windowTitle(), message.arg(info.fileName())); +#endif // QT_NO_MESSAGEBOX + return; + } + if (info.isDir()) { + d->emitFilesSelected(files); + QDialog::accept(); + } + return; + } + + case AnyFile: { + QString fn = files.first(); + QFileInfo info(fn); + if (info.isDir()) { + setDirectory(info.absoluteFilePath()); + return; + } + + if (!info.exists()) { + int maxNameLength = d->maxNameLength(info.path()); + if (maxNameLength >= 0 && info.fileName().length() > maxNameLength) + return; + } + + // check if we have to ask for permission to overwrite the file + if (!info.exists() || !confirmOverwrite() || acceptMode() == AcceptOpen) { + d->emitFilesSelected(QStringList(fn)); + QDialog::accept(); +#ifndef QT_NO_MESSAGEBOX + } else { + if (QMessageBox::warning(this, windowTitle(), + tr("%1 already exists.\nDo you want to replace it?") + .arg(info.fileName()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::Yes) { + d->emitFilesSelected(QStringList(fn)); + QDialog::accept(); + } +#endif + } + return; + } + + case ExistingFile: + case ExistingFiles: + for (int i = 0; i < files.count(); ++i) { + QFileInfo info(files.at(i)); + if (!info.exists()) + info = QFileInfo(d->getEnvironmentVariable(files.at(i))); + if (!info.exists()) { +#ifndef QT_NO_MESSAGEBOX + QString message = tr("%1\nFile not found.\nPlease verify the " + "correct file name was given."); + QMessageBox::warning(this, windowTitle(), message.arg(info.fileName())); +#endif // QT_NO_MESSAGEBOX + return; + } + if (info.isDir()) { + setDirectory(info.absoluteFilePath()); + d->lineEdit()->clear(); + return; + } + } + d->emitFilesSelected(files); + QDialog::accept(); + return; + } +} + +/*! + \internal + + Create widgets, layout and set default values +*/ +void QFileDialogPrivate::init(const QString &directory, const QString &nameFilter, + const QString &caption) +{ + Q_Q(QFileDialog); + if (!caption.isEmpty()) { + useDefaultCaption = false; + setWindowTitle = caption; + q->setWindowTitle(caption); + } + + createWidgets(); + createMenuActions(); + retranslateStrings(); + q->setFileMode(fileMode); + +#ifndef QT_NO_SETTINGS + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + if (!directory.isEmpty()) + setLastVisitedDirectory(workingDirectory(directory)); + q->restoreState(settings.value(QLatin1String("filedialog")).toByteArray()); +#endif + +#ifdef Q_OS_WINCE + qFileDialogUi->lookInLabel->setVisible(false); + qFileDialogUi->fileNameLabel->setVisible(false); + qFileDialogUi->fileTypeLabel->setVisible(false); + qFileDialogUi->sidebar->hide(); +#endif + // Default case + if (!nameFilter.isEmpty()) + q->setNameFilter(nameFilter); + q->setAcceptMode(QFileDialog::AcceptOpen); + q->setDirectory(workingDirectory(directory)); + q->selectFile(initialSelection(directory)); + + _q_updateOkButton(); + q->resize(q->sizeHint()); +} + +/*! + \internal + + Create the widgets, set properties and connections +*/ +void QFileDialogPrivate::createWidgets() +{ + Q_Q(QFileDialog); + model = new QFileSystemModel(q); + model->setObjectName(QLatin1String("qt_filesystem_model")); +#ifdef Q_WS_MAC + model->setNameFilterDisables(true); +#else + model->setNameFilterDisables(false); +#endif + QFileDialog::connect(model, SIGNAL(fileRenamed(const QString &, const QString &, const QString &)), q, SLOT(_q_fileRenamed(const QString &, const QString &, const QString &))); + QFileDialog::connect(model, SIGNAL(rootPathChanged(const QString &)), + q, SLOT(_q_pathChanged(const QString &))); + QFileDialog::connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + q, SLOT(_q_rowsInserted(const QModelIndex &))); + model->setReadOnly(false); + + qFileDialogUi = new Ui_QFileDialog(); + qFileDialogUi->setupUi(q); + + QList<QUrl> initialBookmarks; + initialBookmarks << QUrl::fromLocalFile(QLatin1String("")) + << QUrl::fromLocalFile(QDir::homePath()); + qFileDialogUi->sidebar->init(model, initialBookmarks); + QFileDialog::connect(qFileDialogUi->sidebar, SIGNAL(goToUrl(const QUrl &)), + q, SLOT(_q_goToUrl(const QUrl &))); + + QObject::connect(qFileDialogUi->buttonBox, SIGNAL(accepted()), q, SLOT(accept())); + QObject::connect(qFileDialogUi->buttonBox, SIGNAL(rejected()), q, SLOT(reject())); + + + qFileDialogUi->lookInCombo->init(this); + QObject::connect(qFileDialogUi->lookInCombo, SIGNAL(activated(QString)), q, SLOT(_q_goToDirectory(QString))); + + qFileDialogUi->lookInCombo->setInsertPolicy(QComboBox::NoInsert); + qFileDialogUi->lookInCombo->setDuplicatesEnabled(false); + + // filename + qFileDialogUi->fileNameEdit->init(this); +#ifndef QT_NO_SHORTCUT + qFileDialogUi->fileNameLabel->setBuddy(qFileDialogUi->fileNameEdit); +#endif +#ifndef QT_NO_COMPLETER + completer = new QFSCompletor(model, q); + qFileDialogUi->fileNameEdit->setCompleter(completer); + QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)), + q, SLOT(_q_autoCompleteFileName(QString))); +#endif // QT_NO_COMPLETER + QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)), + q, SLOT(_q_updateOkButton())); + + QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(returnPressed()), q, SLOT(accept())); + + // filetype + qFileDialogUi->fileTypeCombo->setDuplicatesEnabled(false); + qFileDialogUi->fileTypeCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + qFileDialogUi->fileTypeCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + QObject::connect(qFileDialogUi->fileTypeCombo, SIGNAL(activated(int)), + q, SLOT(_q_useNameFilter(int))); + QObject::connect(qFileDialogUi->fileTypeCombo, SIGNAL(activated(const QString &)), + q, SIGNAL(filterSelected(const QString &))); + + qFileDialogUi->listView->init(this); + qFileDialogUi->listView->setModel(model); + QObject::connect(qFileDialogUi->listView, SIGNAL(activated(QModelIndex)), + q, SLOT(_q_enterDirectory(QModelIndex))); + QObject::connect(qFileDialogUi->listView, SIGNAL(customContextMenuRequested(QPoint)), + q, SLOT(_q_showContextMenu(QPoint))); +#ifndef QT_NO_SHORTCUT + QShortcut *shortcut = new QShortcut(qFileDialogUi->listView); + shortcut->setKey(QKeySequence(QLatin1String("Delete"))); + QObject::connect(shortcut, SIGNAL(activated()), q, SLOT(_q_deleteCurrent())); +#endif + + qFileDialogUi->treeView->init(this); + qFileDialogUi->treeView->setModel(model); + QHeaderView *treeHeader = qFileDialogUi->treeView->header(); + QFontMetrics fm(q->font()); + treeHeader->resizeSection(0, fm.width(QLatin1String("wwwwwwwwwwwwwwwwwwwwwwwwww"))); + treeHeader->resizeSection(1, fm.width(QLatin1String("128.88 GB"))); + treeHeader->resizeSection(2, fm.width(QLatin1String("mp3Folder"))); + treeHeader->resizeSection(3, fm.width(QLatin1String("10/29/81 02:02PM"))); + treeHeader->setContextMenuPolicy(Qt::ActionsContextMenu); + + QActionGroup *showActionGroup = new QActionGroup(q); + showActionGroup->setExclusive(false); + QObject::connect(showActionGroup, SIGNAL(triggered(QAction *)), + q, SLOT(_q_showHeader(QAction *)));; + + QAbstractItemModel *abstractModel = model; +#ifndef QT_NO_PROXYMODEL + if (proxyModel) + abstractModel = proxyModel; +#endif + for (int i = 1; i < abstractModel->columnCount(QModelIndex()); ++i) { + QAction *showHeader = new QAction(showActionGroup); + showHeader->setCheckable(true); + showHeader->setChecked(true); + treeHeader->addAction(showHeader); + } + + QItemSelectionModel *selModel = qFileDialogUi->treeView->selectionModel(); + qFileDialogUi->treeView->setSelectionModel(qFileDialogUi->listView->selectionModel()); + delete selModel; + QObject::connect(qFileDialogUi->treeView, SIGNAL(activated(QModelIndex)), + q, SLOT(_q_enterDirectory(QModelIndex))); + QObject::connect(qFileDialogUi->treeView, SIGNAL(customContextMenuRequested(QPoint)), + q, SLOT(_q_showContextMenu(QPoint))); +#ifndef QT_NO_SHORTCUT + shortcut = new QShortcut(qFileDialogUi->treeView); + shortcut->setKey(QKeySequence(QLatin1String("Delete"))); + QObject::connect(shortcut, SIGNAL(activated()), q, SLOT(_q_deleteCurrent())); +#endif + + // Selections + QItemSelectionModel *selections = qFileDialogUi->listView->selectionModel(); + QObject::connect(selections, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + q, SLOT(_q_selectionChanged())); + QObject::connect(selections, SIGNAL(currentChanged(QModelIndex,QModelIndex)), + q, SLOT(_q_currentChanged(QModelIndex))); + qFileDialogUi->splitter->setStretchFactor(qFileDialogUi->splitter->indexOf(qFileDialogUi->splitter->widget(1)), QSizePolicy::Expanding); + + createToolButtons(); +} + +void QFileDialogPrivate::_q_showHeader(QAction *action) +{ + Q_Q(QFileDialog); + QActionGroup *actionGroup = qobject_cast<QActionGroup*>(q->sender()); + qFileDialogUi->treeView->header()->setSectionHidden(actionGroup->actions().indexOf(action) + 1, !action->isChecked()); +} + +#ifndef QT_NO_PROXYMODEL +/*! + \since 4.3 + + Sets the model for the views to the given \a proxyModel. This is useful if you + want to modify the underlying model; for example, to add columns, filter + data or add drives. + + Any existing proxy model will be removed, but not deleted. The file dialog + will take ownership of the \a proxyModel. + + \sa proxyModel() +*/ +void QFileDialog::setProxyModel(QAbstractProxyModel *proxyModel) +{ + Q_D(QFileDialog); + if ((!proxyModel && !d->proxyModel) + || (proxyModel == d->proxyModel)) + return; + + QModelIndex idx = d->rootIndex(); + if (d->proxyModel) { + disconnect(d->proxyModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(_q_rowsInserted(const QModelIndex &))); + } else { + disconnect(d->model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(_q_rowsInserted(const QModelIndex &))); + } + + if (proxyModel != 0) { + proxyModel->setParent(this); + d->proxyModel = proxyModel; + proxyModel->setSourceModel(d->model); + d->qFileDialogUi->listView->setModel(d->proxyModel); + d->qFileDialogUi->treeView->setModel(d->proxyModel); + connect(d->proxyModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(_q_rowsInserted(const QModelIndex &))); + } else { + d->proxyModel = 0; + d->qFileDialogUi->listView->setModel(d->model); + d->qFileDialogUi->treeView->setModel(d->model); + connect(d->model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(_q_rowsInserted(const QModelIndex &))); + } + QItemSelectionModel *selModel = d->qFileDialogUi->treeView->selectionModel(); + d->qFileDialogUi->treeView->setSelectionModel(d->qFileDialogUi->listView->selectionModel()); + delete selModel; + d->setRootIndex(idx); + + // reconnect selection + QItemSelectionModel *selections = d->qFileDialogUi->listView->selectionModel(); + QObject::connect(selections, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(_q_selectionChanged())); + QObject::connect(selections, SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(_q_currentChanged(QModelIndex))); +} + +/*! + Returns the proxy model used by the file dialog. By default no proxy is set. + + \sa setProxyModel() +*/ +QAbstractProxyModel *QFileDialog::proxyModel() const +{ + Q_D(const QFileDialog); + return d->proxyModel; +} +#endif // QT_NO_PROXYMODEL + +/*! + \internal + + Create tool buttons, set properties and connections +*/ +void QFileDialogPrivate::createToolButtons() +{ + Q_Q(QFileDialog); + qFileDialogUi->backButton->setIcon(q->style()->standardIcon(QStyle::SP_ArrowBack, 0, q)); + qFileDialogUi->backButton->setAutoRaise(true); + qFileDialogUi->backButton->setEnabled(false); + QObject::connect(qFileDialogUi->backButton, SIGNAL(clicked()), q, SLOT(_q_navigateBackward())); + + qFileDialogUi->forwardButton->setIcon(q->style()->standardIcon(QStyle::SP_ArrowForward, 0, q)); + qFileDialogUi->forwardButton->setAutoRaise(true); + qFileDialogUi->forwardButton->setEnabled(false); + QObject::connect(qFileDialogUi->forwardButton, SIGNAL(clicked()), q, SLOT(_q_navigateForward())); + + qFileDialogUi->toParentButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogToParent, 0, q)); + qFileDialogUi->toParentButton->setAutoRaise(true); + qFileDialogUi->toParentButton->setEnabled(false); + QObject::connect(qFileDialogUi->toParentButton, SIGNAL(clicked()), q, SLOT(_q_navigateToParent())); + + qFileDialogUi->listModeButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogListView, 0, q)); + qFileDialogUi->listModeButton->setAutoRaise(true); + qFileDialogUi->listModeButton->setDown(true); + QObject::connect(qFileDialogUi->listModeButton, SIGNAL(clicked()), q, SLOT(_q_showListView())); + + qFileDialogUi->detailModeButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogDetailedView, 0, q)); + qFileDialogUi->detailModeButton->setAutoRaise(true); + QObject::connect(qFileDialogUi->detailModeButton, SIGNAL(clicked()), q, SLOT(_q_showDetailsView())); + + QSize toolSize(qFileDialogUi->fileNameEdit->sizeHint().height(), qFileDialogUi->fileNameEdit->sizeHint().height()); + qFileDialogUi->backButton->setFixedSize(toolSize); + qFileDialogUi->listModeButton->setFixedSize(toolSize); + qFileDialogUi->detailModeButton->setFixedSize(toolSize); + qFileDialogUi->forwardButton->setFixedSize(toolSize); + qFileDialogUi->toParentButton->setFixedSize(toolSize); + + qFileDialogUi->newFolderButton->setIcon(q->style()->standardIcon(QStyle::SP_FileDialogNewFolder, 0, q)); + qFileDialogUi->newFolderButton->setFixedSize(toolSize); + qFileDialogUi->newFolderButton->setAutoRaise(true); + qFileDialogUi->newFolderButton->setEnabled(false); + QObject::connect(qFileDialogUi->newFolderButton, SIGNAL(clicked()), q, SLOT(_q_createDirectory())); +} + +/*! + \internal + + Create actions which will be used in the right click. +*/ +void QFileDialogPrivate::createMenuActions() +{ + Q_Q(QFileDialog); + + QAction *goHomeAction = new QAction(q); +#ifndef QT_NO_SHORTCUT + goHomeAction->setShortcut(Qt::CTRL + Qt::Key_H + Qt::SHIFT); +#endif + QObject::connect(goHomeAction, SIGNAL(triggered()), q, SLOT(_q_goHome())); + q->addAction(goHomeAction); + + // ### TODO add Desktop & Computer actions + + QAction *goToParent = new QAction(q); + goToParent->setObjectName(QLatin1String("qt_goto_parent_action")); +#ifndef QT_NO_SHORTCUT + goToParent->setShortcut(Qt::CTRL + Qt::UpArrow); +#endif + QObject::connect(goToParent, SIGNAL(triggered()), q, SLOT(_q_navigateToParent())); + q->addAction(goToParent); + + renameAction = new QAction(q); + renameAction->setEnabled(false); + renameAction->setObjectName(QLatin1String("qt_rename_action")); + QObject::connect(renameAction, SIGNAL(triggered()), q, SLOT(_q_renameCurrent())); + + deleteAction = new QAction(q); + deleteAction->setEnabled(false); + deleteAction->setObjectName(QLatin1String("qt_delete_action")); + QObject::connect(deleteAction, SIGNAL(triggered()), q, SLOT(_q_deleteCurrent())); + + showHiddenAction = new QAction(q); + showHiddenAction->setObjectName(QLatin1String("qt_show_hidden_action")); + showHiddenAction->setCheckable(true); + QObject::connect(showHiddenAction, SIGNAL(triggered()), q, SLOT(_q_showHidden())); + + newFolderAction = new QAction(q); + newFolderAction->setObjectName(QLatin1String("qt_new_folder_action")); + QObject::connect(newFolderAction, SIGNAL(triggered()), q, SLOT(_q_createDirectory())); +} + +void QFileDialogPrivate::_q_goHome() +{ + Q_Q(QFileDialog); + q->setDirectory(QDir::homePath()); +} + +/*! + \internal + + Update history with new path, buttons, and combo +*/ +void QFileDialogPrivate::_q_pathChanged(const QString &newPath) +{ + Q_Q(QFileDialog); + QDir dir(model->rootDirectory()); + qFileDialogUi->toParentButton->setEnabled(dir.exists()); + qFileDialogUi->sidebar->selectUrl(QUrl::fromLocalFile(newPath)); + q->setHistory(qFileDialogUi->lookInCombo->history()); + + if (currentHistoryLocation < 0 || currentHistory.value(currentHistoryLocation) != QDir::toNativeSeparators(newPath)) { + while (currentHistoryLocation >= 0 && currentHistoryLocation + 1 < currentHistory.count()) { + currentHistory.removeLast(); + } + currentHistory.append(QDir::toNativeSeparators(newPath)); + ++currentHistoryLocation; + } + qFileDialogUi->forwardButton->setEnabled(currentHistory.size() - currentHistoryLocation > 1); + qFileDialogUi->backButton->setEnabled(currentHistoryLocation > 0); +} + +/*! + \internal + + Navigates to the last directory viewed in the dialog. +*/ +void QFileDialogPrivate::_q_navigateBackward() +{ + Q_Q(QFileDialog); + if (!currentHistory.isEmpty() && currentHistoryLocation > 0) { + --currentHistoryLocation; + QString previousHistory = currentHistory.at(currentHistoryLocation); + q->setDirectory(previousHistory); + } +} + +/*! + \internal + + Navigates to the last directory viewed in the dialog. +*/ +void QFileDialogPrivate::_q_navigateForward() +{ + Q_Q(QFileDialog); + if (!currentHistory.isEmpty() && currentHistoryLocation < currentHistory.size() - 1) { + ++currentHistoryLocation; + QString nextHistory = currentHistory.at(currentHistoryLocation); + q->setDirectory(nextHistory); + } +} + +/*! + \internal + + Navigates to the parent directory of the currently displayed directory + in the dialog. +*/ +void QFileDialogPrivate::_q_navigateToParent() +{ + Q_Q(QFileDialog); + QDir dir(model->rootDirectory()); + QString newDirectory; + if (dir.isRoot()) { + newDirectory = model->myComputer().toString(); + } else { + dir.cdUp(); + newDirectory = dir.absolutePath(); + } + q->setDirectory(newDirectory); + emit q->directoryEntered(newDirectory); +} + +/*! + \internal + + Creates a new directory, first asking the user for a suitable name. +*/ +void QFileDialogPrivate::_q_createDirectory() +{ + Q_Q(QFileDialog); + qFileDialogUi->listView->clearSelection(); + + QString newFolderString = QFileDialog::tr("New Folder"); + QString folderName = newFolderString; + QString prefix = q->directory().absolutePath() + QDir::separator(); + if (QFile::exists(prefix + folderName)) { + qlonglong suffix = 2; + while (QFile::exists(prefix + folderName)) { + folderName = newFolderString + QString::number(suffix++); + } + } + + QModelIndex parent = rootIndex(); + QModelIndex index = model->mkdir(parent, folderName); + if (!index.isValid()) + return; + + index = select(index); + if (index.isValid()) { + qFileDialogUi->treeView->setCurrentIndex(index); + currentView()->edit(index); + } +} + +void QFileDialogPrivate::_q_showListView() +{ + qFileDialogUi->listModeButton->setDown(true); + qFileDialogUi->detailModeButton->setDown(false); + qFileDialogUi->treeView->hide(); + qFileDialogUi->listView->show(); + qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->listView->parentWidget()); + qFileDialogUi->listView->doItemsLayout(); +} + +void QFileDialogPrivate::_q_showDetailsView() +{ + qFileDialogUi->listModeButton->setDown(false); + qFileDialogUi->detailModeButton->setDown(true); + qFileDialogUi->listView->hide(); + qFileDialogUi->treeView->show(); + qFileDialogUi->stackedWidget->setCurrentWidget(qFileDialogUi->treeView->parentWidget()); + qFileDialogUi->treeView->doItemsLayout(); +} + +/*! + \internal + + Show the context menu for the file/dir under position +*/ +void QFileDialogPrivate::_q_showContextMenu(const QPoint &position) +{ +#ifdef QT_NO_MENU + Q_UNUSED(position); +#else + Q_Q(QFileDialog); + QAbstractItemView *view = 0; + if (q->viewMode() == QFileDialog::Detail) + view = qFileDialogUi->treeView; + else + view = qFileDialogUi->listView; + QModelIndex index = view->indexAt(position); + index = mapToSource(index.sibling(index.row(), 0)); + + QMenu menu(view); + if (index.isValid()) { + // file context menu + QFile::Permissions p(index.parent().data(QFileSystemModel::FilePermissions).toInt()); + renameAction->setEnabled(p & QFile::WriteUser); + menu.addAction(renameAction); + deleteAction->setEnabled(p & QFile::WriteUser); + menu.addAction(deleteAction); + menu.addSeparator(); + } + menu.addAction(showHiddenAction); + if (qFileDialogUi->newFolderButton->isVisible()) { + newFolderAction->setEnabled(qFileDialogUi->newFolderButton->isEnabled()); + menu.addAction(newFolderAction); + } + menu.exec(view->viewport()->mapToGlobal(position)); +#endif // QT_NO_MENU +} + +/*! + \internal +*/ +void QFileDialogPrivate::_q_renameCurrent() +{ + Q_Q(QFileDialog); + QModelIndex index = qFileDialogUi->listView->currentIndex(); + index = index.sibling(index.row(), 0); + if (q->viewMode() == QFileDialog::List) + qFileDialogUi->listView->edit(index); + else + qFileDialogUi->treeView->edit(index); +} + +bool QFileDialogPrivate::removeDirectory(const QString &path) +{ + QModelIndex modelIndex = model->index(path); + return model->remove(modelIndex); +} + +/*! + \internal + + Deletes the currently selected item in the dialog. +*/ +void QFileDialogPrivate::_q_deleteCurrent() +{ + if (model->isReadOnly()) + return; + + QModelIndexList list = qFileDialogUi->listView->selectionModel()->selectedRows(); + for (int i = list.count() - 1; i >= 0; --i) { + QModelIndex index = list.at(i); + if (index == qFileDialogUi->listView->rootIndex()) + continue; + + index = mapToSource(index.sibling(index.row(), 0)); + if (!index.isValid()) + continue; + + QString fileName = index.data(QFileSystemModel::FileNameRole).toString(); + QString filePath = index.data(QFileSystemModel::FilePathRole).toString(); + bool isDir = model->isDir(index); + + QFile::Permissions p(index.parent().data(QFileSystemModel::FilePermissions).toInt()); +#ifndef QT_NO_MESSAGEBOX + Q_Q(QFileDialog); + if (!(p & QFile::WriteUser) && (QMessageBox::warning(q_func(), q_func()->windowTitle(), + QFileDialog::tr("'%1' is write protected.\nDo you want to delete it anyway?") + .arg(fileName), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)) + return; + else if (QMessageBox::warning(q_func(), q_func()->windowTitle(), + QFileDialog::tr("Are sure you want to delete '%1'?") + .arg(fileName), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) + return; + +#else + if (!(p & QFile::WriteUser)) + return; +#endif // QT_NO_MESSAGEBOX + + // the event loop has run, we can NOT reuse index because the model might have removed it. + if (isDir) { + if (!removeDirectory(filePath)) { +#ifndef QT_NO_MESSAGEBOX + QMessageBox::warning(q, q->windowTitle(), + QFileDialog::tr("Could not delete directory.")); +#endif + } + } else { + model->remove(index); + } + } +} + +void QFileDialogPrivate::_q_autoCompleteFileName(const QString &text) +{ + if (text.startsWith(QLatin1String("//")) || text.startsWith(QLatin1String("\\"))) { + qFileDialogUi->listView->selectionModel()->clearSelection(); + return; + } + + QStringList multipleFiles = typedFiles(); + if (multipleFiles.count() > 0) { + QModelIndexList oldFiles = qFileDialogUi->listView->selectionModel()->selectedRows(); + QModelIndexList newFiles; + for (int i = 0; i < multipleFiles.count(); ++i) { + QModelIndex idx = model->index(multipleFiles.at(i)); + if (oldFiles.contains(idx)) + oldFiles.removeAll(idx); + else + newFiles.append(idx); + } + for (int i = 0; i < newFiles.count(); ++i) + select(newFiles.at(i)); + if (lineEdit()->hasFocus()) + for (int i = 0; i < oldFiles.count(); ++i) + qFileDialogUi->listView->selectionModel()->select(oldFiles.at(i), + QItemSelectionModel::Toggle | QItemSelectionModel::Rows); + } +} + +/*! + \internal +*/ +void QFileDialogPrivate::_q_updateOkButton() +{ + Q_Q(QFileDialog); + QPushButton *button = qFileDialogUi->buttonBox->button((acceptMode == QFileDialog::AcceptOpen) + ? QDialogButtonBox::Open : QDialogButtonBox::Save); + if (!button) + return; + + bool enableButton = true; + bool isOpenDirectory = false; + + QStringList files = q->selectedFiles(); + QString lineEditText = lineEdit()->text(); + + if (lineEditText.startsWith(QLatin1String("//")) || lineEditText.startsWith(QLatin1String("\\"))) { + button->setEnabled(true); + if (acceptMode == QFileDialog::AcceptSave) + button->setText(isOpenDirectory ? QFileDialog::tr("&Open") : acceptLabel); + return; + } + + if (files.isEmpty()) { + enableButton = false; + } else if (lineEditText == QLatin1String("..")) { + isOpenDirectory = true; + } else { + switch (fileMode) { + case QFileDialog::DirectoryOnly: + case QFileDialog::Directory: { + QString fn = files.first(); + QModelIndex idx = model->index(fn); + if (!idx.isValid()) + idx = model->index(getEnvironmentVariable(fn)); + if (!idx.isValid() || !model->isDir(idx)) + enableButton = false; + break; + } + case QFileDialog::AnyFile: { + QString fn = files.first(); + QFileInfo info(fn); + QModelIndex idx = model->index(fn); + QString fileDir; + QString fileName; + if (info.isDir()) { + fileDir = info.canonicalFilePath(); + } else { + fileDir = fn.mid(0, fn.lastIndexOf(QLatin1Char('/'))); + fileName = fn.mid(fileDir.length() + 1); + } + if (lineEditText.contains(QLatin1String(".."))) { + fileDir = info.canonicalFilePath(); + fileName = info.fileName(); + } + + if (fileDir == q->directory().canonicalPath() && fileName.isEmpty()) { + enableButton = false; + break; + } + if (idx.isValid() && model->isDir(idx)) { + isOpenDirectory = true; + enableButton = true; + break; + } + if (!idx.isValid()) { + int maxLength = maxNameLength(fileDir); + enableButton = maxLength < 0 || fileName.length() <= maxLength; + } + break; + } + case QFileDialog::ExistingFile: + case QFileDialog::ExistingFiles: + for (int i = 0; i < files.count(); ++i) { + QModelIndex idx = model->index(files.at(i)); + if (!idx.isValid()) + idx = model->index(getEnvironmentVariable(files.at(i))); + if (!idx.isValid()) { + enableButton = false; + break; + } + if (idx.isValid() && model->isDir(idx)) { + isOpenDirectory = true; + break; + } + } + break; + default: + break; + } + } + + button->setEnabled(enableButton); + if (acceptMode == QFileDialog::AcceptSave) + button->setText(isOpenDirectory ? QFileDialog::tr("&Open") : acceptLabel); +} + +/*! + \internal +*/ +void QFileDialogPrivate::_q_currentChanged(const QModelIndex &index) +{ + _q_updateOkButton(); + emit q_func()->currentChanged(index.data(QFileSystemModel::FilePathRole).toString()); +} + +/*! + \internal + + This is called when the user double clicks on a file with the corresponding + model item \a index. +*/ +void QFileDialogPrivate::_q_enterDirectory(const QModelIndex &index) +{ + Q_Q(QFileDialog); + // My Computer or a directory + QString path = index.data(QFileSystemModel::FilePathRole).toString(); + if (path.isEmpty() || model->isDir(index)) { + q->setDirectory(path); + emit q->directoryEntered(path); + if (fileMode == QFileDialog::Directory + || fileMode == QFileDialog::DirectoryOnly) { + // ### find out why you have to do both of these. + lineEdit()->setText(QString()); + lineEdit()->clear(); + } + } else { + q->accept(); + } +} + +/*! + \internal + + Changes the file dialog's current directory to the one specified + by \a path. +*/ +void QFileDialogPrivate::_q_goToDirectory(const QString &path) +{ + #ifndef QT_NO_MESSAGEBOX + Q_Q(QFileDialog); +#endif + QModelIndex index = qFileDialogUi->lookInCombo->model()->index(qFileDialogUi->lookInCombo->currentIndex(), + qFileDialogUi->lookInCombo->modelColumn(), + qFileDialogUi->lookInCombo->rootModelIndex()); + QString path2 = path; + if (!index.isValid()) + index = mapFromSource(model->index(getEnvironmentVariable(path))); + else { + path2 = index.data(UrlRole).toUrl().toLocalFile(); + index = mapFromSource(model->index(path2)); + } + QDir dir(path2); + if (!dir.exists()) + dir = getEnvironmentVariable(path2); + + if (dir.exists() || path2.isEmpty() || path2 == model->myComputer().toString()) { + _q_enterDirectory(index); +#ifndef QT_NO_MESSAGEBOX + } else { + QString message = QFileDialog::tr("%1\nDirectory not found.\nPlease verify the " + "correct directory name was given."); + QMessageBox::warning(q, q->windowTitle(), message.arg(path2)); +#endif // QT_NO_MESSAGEBOX + } +} + +const char *qt_file_dialog_filter_reg_exp = + "^([^()]*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; + +// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)" +QStringList qt_clean_filter_list(const QString &filter) +{ + QRegExp regexp(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); + QString f = filter; + int i = regexp.indexIn(f); + if (i >= 0) + f = regexp.cap(2); + return f.split(QLatin1Char(' '), QString::SkipEmptyParts); +} + +/*! + \internal + + Sets the current name filter to be nameFilter and + update the qFileDialogUi->fileNameEdit when in AcceptSave mode with the new extension. +*/ +void QFileDialogPrivate::_q_useNameFilter(int index) +{ + if (index == nameFilters.size()) { + QAbstractItemModel *comboModel = qFileDialogUi->fileTypeCombo->model(); + nameFilters.append(comboModel->index(comboModel->rowCount() - 1, 0).data().toString()); + } + + QString nameFilter = nameFilters.at(index); + QStringList newNameFilters = qt_clean_filter_list(nameFilter); + if (acceptMode == QFileDialog::AcceptSave) { + QString newNameFilterExtension; + if (newNameFilters.count() > 0) + newNameFilterExtension = QFileInfo(newNameFilters.at(0)).suffix(); + + QString fileName = lineEdit()->text(); + const QString fileNameExtension = QFileInfo(fileName).suffix(); + if (!fileNameExtension.isEmpty() && !newNameFilterExtension.isEmpty()) { + const int fileNameExtensionLength = fileNameExtension.count(); + fileName.replace(fileName.count() - fileNameExtensionLength, + fileNameExtensionLength, newNameFilterExtension); + lineEdit()->setText(fileName); + } + } + + model->setNameFilters(newNameFilters); +} + +/*! + \internal + + This is called when the model index corresponding to the current file is changed + from \a index to \a current. +*/ +void QFileDialogPrivate::_q_selectionChanged() +{ + QModelIndexList indexes = qFileDialogUi->listView->selectionModel()->selectedRows(); + bool stripDirs = (fileMode != QFileDialog::DirectoryOnly && fileMode != QFileDialog::Directory); + + QStringList allFiles; + for (int i = 0; i < indexes.count(); ++i) { + if (stripDirs && model->isDir(mapToSource(indexes.at(i)))) + continue; + allFiles.append(indexes.at(i).data().toString()); + } + if (allFiles.count() > 1) + for (int i = 0; i < allFiles.count(); ++i) { + allFiles.replace(i, QString(QLatin1Char('"') + allFiles.at(i) + QLatin1Char('"'))); + } + + QString finalFiles = allFiles.join(QLatin1String(" ")); + if (!finalFiles.isEmpty() && !lineEdit()->hasFocus() && lineEdit()->isVisible()) + lineEdit()->setText(finalFiles); + else + _q_updateOkButton(); +} + +/*! + \internal + + Includes hidden files and directories in the items displayed in the dialog. +*/ +void QFileDialogPrivate::_q_showHidden() +{ + Q_Q(QFileDialog); + QDir::Filters dirFilters = q->filter(); + if (showHiddenAction->isChecked()) + dirFilters |= QDir::Hidden; + else + dirFilters &= ~QDir::Hidden; + q->setFilter(dirFilters); +} + +/*! + \internal + + When parent is root and rows have been inserted when none was there before + then select the first one. +*/ +void QFileDialogPrivate::_q_rowsInserted(const QModelIndex &parent) +{ + if (!qFileDialogUi->treeView + || parent != qFileDialogUi->treeView->rootIndex() + || !qFileDialogUi->treeView->selectionModel() + || qFileDialogUi->treeView->selectionModel()->hasSelection() + || qFileDialogUi->treeView->model()->rowCount(parent) == 0) + return; +} + +void QFileDialogPrivate::_q_fileRenamed(const QString &path, const QString oldName, const QString newName) +{ + if (fileMode == QFileDialog::Directory || fileMode == QFileDialog::DirectoryOnly) { + if (path == rootPath() && lineEdit()->text() == oldName) + lineEdit()->setText(newName); + } +} + +/*! + \internal + + For the list and tree view watch keys to goto parent and back in the history + + returns true if handled +*/ +bool QFileDialogPrivate::itemViewKeyboardEvent(QKeyEvent *event) { + + Q_Q(QFileDialog); + switch (event->key()) { + case Qt::Key_Backspace: + _q_navigateToParent(); + return true; + case Qt::Key_Back: +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled()) + return false; +#endif + case Qt::Key_Left: + if (event->key() == Qt::Key_Back || event->modifiers() == Qt::AltModifier) { + _q_navigateBackward(); + return true; + } + break; + case Qt::Key_Escape: + q->hide(); + return true; + default: + break; + } + return false; +} + +QString QFileDialogPrivate::getEnvironmentVariable(const QString &string) +{ +#ifdef Q_OS_UNIX + if (string.size() > 1 && string.startsWith(QLatin1Char('$'))) { + return QString::fromLocal8Bit(getenv(string.mid(1).toLatin1().constData())); + } +#else + if (string.size() > 2 && string.startsWith(QLatin1Char('%')) && string.endsWith(QLatin1Char('%'))) { + return QString::fromLocal8Bit(qgetenv(string.mid(1, string.size() - 2).toLatin1().constData())); + } +#endif + return string; +} + +void QFileDialogComboBox::init(QFileDialogPrivate *d_pointer) { + d_ptr = d_pointer; + urlModel = new QUrlModel(this); + urlModel->showFullPath = true; + urlModel->setFileSystemModel(d_ptr->model); + setModel(urlModel); +} + +void QFileDialogComboBox::showPopup() +{ + if (model()->rowCount() > 1) + QComboBox::showPopup(); + + urlModel->setUrls(QList<QUrl>()); + QList<QUrl> list; + QModelIndex idx = d_ptr->model->index(d_ptr->rootPath()); + while (idx.isValid()) { + QUrl url = QUrl::fromLocalFile(idx.data(QFileSystemModel::FilePathRole).toString()); + if (url.isValid()) + list.append(url); + idx = idx.parent(); + } + // add "my computer" + list.append(QUrl::fromLocalFile(QLatin1String(""))); + urlModel->addUrls(list, 0); + idx = model()->index(model()->rowCount() - 1, 0); + + // append history + QList<QUrl> urls; + for (int i = 0; i < m_history.count(); ++i) { + QUrl path = QUrl::fromLocalFile(m_history.at(i)); + if (!urls.contains(path)) + urls.prepend(path); + } + if (urls.count() > 0) { + model()->insertRow(model()->rowCount()); + idx = model()->index(model()->rowCount()-1, 0); + // ### TODO maybe add a horizontal line before this + model()->setData(idx, QFileDialog::tr("Recent Places")); + QStandardItemModel *m = qobject_cast<QStandardItemModel*>(model()); + if (m) { + Qt::ItemFlags flags = m->flags(idx); + flags &= ~Qt::ItemIsEnabled; + m->item(idx.row(), idx.column())->setFlags(flags); + } + urlModel->addUrls(urls, -1, false); + } + setCurrentIndex(0); + + QComboBox::showPopup(); +} + +// Exact same as QComboBox::paintEvent(), except we elide the text. +void QFileDialogComboBox::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + + QRect editRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxEditField, this); + int size = editRect.width() - opt.iconSize.width() - 4; + opt.currentText = opt.fontMetrics.elidedText(opt.currentText, Qt::ElideMiddle, size); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); +} + +QFileDialogListView::QFileDialogListView(QWidget *parent) : QListView(parent) +{ +} + +void QFileDialogListView::init(QFileDialogPrivate *d_pointer) +{ + d_ptr = d_pointer; + setSelectionBehavior(QAbstractItemView::SelectRows); + setWrapping(true); + setResizeMode(QListView::Adjust); + setEditTriggers(QAbstractItemView::EditKeyPressed); + setContextMenuPolicy(Qt::CustomContextMenu); +#ifndef QT_NO_DRAGANDDROP + setDragDropMode(QAbstractItemView::InternalMove); +#endif +} + +QSize QFileDialogListView::sizeHint() const +{ + int height = qMax(10, sizeHintForRow(0)); + return QSize(QListView::sizeHint().width() * 2, height * 30); +} + +void QFileDialogListView::keyPressEvent(QKeyEvent *e) +{ + if (!d_ptr->itemViewKeyboardEvent(e)) { + QListView::keyPressEvent(e); + } + e->accept(); +} + +QFileDialogTreeView::QFileDialogTreeView(QWidget *parent) : QTreeView(parent) +{ +} + +void QFileDialogTreeView::init(QFileDialogPrivate *d_pointer) +{ + d_ptr = d_pointer; + setSelectionBehavior(QAbstractItemView::SelectRows); + setRootIsDecorated(false); + setItemsExpandable(false); + setSortingEnabled(true); + header()->setSortIndicator(0, Qt::AscendingOrder); + header()->setStretchLastSection(false); + setTextElideMode(Qt::ElideMiddle); + setEditTriggers(QAbstractItemView::EditKeyPressed); + setContextMenuPolicy(Qt::CustomContextMenu); +#ifndef QT_NO_DRAGANDDROP + setDragDropMode(QAbstractItemView::InternalMove); +#endif +} + +void QFileDialogTreeView::keyPressEvent(QKeyEvent *e) +{ + if (!d_ptr->itemViewKeyboardEvent(e)) { + QTreeView::keyPressEvent(e); + } + e->accept(); +} + +QSize QFileDialogTreeView::sizeHint() const +{ + int height = qMax(10, sizeHintForRow(0)); + QSize sizeHint = header()->sizeHint(); + return QSize(sizeHint.width() * 4, height * 30); +} + +/*! + // FIXME: this is a hack to avoid propagating key press events + // to the dialog and from there to the "Ok" button +*/ +void QFileDialogLineEdit::keyPressEvent(QKeyEvent *e) +{ + int key = e->key(); + QLineEdit::keyPressEvent(e); + if (key != Qt::Key_Escape) + e->accept(); + if (hideOnEsc && (key == Qt::Key_Escape || key == Qt::Key_Return || key == Qt::Key_Enter)) { + e->accept(); + hide(); + d_ptr->currentView()->setFocus(Qt::ShortcutFocusReason); + } +} + +#ifndef QT_NO_COMPLETER + +QString QFSCompletor::pathFromIndex(const QModelIndex &index) const +{ + const QFileSystemModel *dirModel = static_cast<const QFileSystemModel *>(model()); + QString currentLocation = dirModel->rootPath(); + QString path = index.data(QFileSystemModel::FilePathRole).toString(); + if (!currentLocation.isEmpty() && path.startsWith(currentLocation)) { + return path.mid(currentLocation.length() + 1); + } + return index.data(QFileSystemModel::FilePathRole).toString(); +} + +QStringList QFSCompletor::splitPath(const QString &path) const +{ + if (path.isEmpty()) + return QStringList(completionPrefix()); + + QString pathCopy = QDir::toNativeSeparators(path); + QString sep = QDir::separator(); +#ifdef Q_OS_WIN + if (pathCopy == QLatin1String("\\") || pathCopy == QLatin1String("\\\\")) + return QStringList(pathCopy); + QString doubleSlash(QLatin1String("\\\\")); + if (pathCopy.startsWith(doubleSlash)) + pathCopy = pathCopy.mid(2); + else + doubleSlash.clear(); +#endif + + QRegExp re(QLatin1String("[") + QRegExp::escape(sep) + QLatin1String("]")); + +#ifdef Q_OS_WIN + QStringList parts = pathCopy.split(re, QString::SkipEmptyParts); + if (!doubleSlash.isEmpty() && !parts.isEmpty()) + parts[0].prepend(doubleSlash); + if (pathCopy.endsWith(sep)) + parts.append(QString()); +#else + QStringList parts = pathCopy.split(re); + if (path[0] == sep[0]) // read the "/" at the beginning as the split removed it + parts[0] = sep[0]; +#endif + +#ifdef Q_OS_WIN + bool startsFromRoot = !parts.isEmpty() && parts[0].endsWith(QLatin1Char(':')); +#else + bool startsFromRoot = path[0] == sep[0]; +#endif + if (parts.count() == 1 || (parts.count() > 1 && !startsFromRoot)) { + const QFileSystemModel *dirModel = static_cast<const QFileSystemModel *>(model()); + QString currentLocation = QDir::toNativeSeparators(dirModel->rootPath()); + if (currentLocation.contains(sep) && path != currentLocation) { + QStringList currentLocationList = splitPath(currentLocation); + while (!currentLocationList.isEmpty() + && parts.count() > 0 + && parts.at(0) == QLatin1String("..")) { + parts.removeFirst(); + currentLocationList.removeLast(); + } + if (!currentLocationList.isEmpty() && currentLocationList.last().isEmpty()) + currentLocationList.removeLast(); + return currentLocationList + parts; + } + } + return parts; +} + +#endif // QT_NO_COMPLETER + +#ifdef QT3_SUPPORT +/*! + Use selectedFiles() instead. + + \oldcode + QString selected = dialog->selectedFile(); + \newcode + QStringList files = dialog->selectedFiles(); + QString selected; + if (!files.isEmpty()) + selected = files[0]; + \endcode +*/ +QString QFileDialog::selectedFile() const +{ + QStringList files = selectedFiles(); + return files.size() ? files.at(0) : QString(); +} + +/*! + \typedef QFileDialog::Mode + + Use QFileDialog::FileMode instead. +*/ + +/*! + \fn void QFileDialog::setMode(FileMode m) + + Use setFileMode() instead. +*/ + +/*! + \fn FileMode QFileDialog::mode() const + + Use fileMode() instead. +*/ + +/*! + \fn void QFileDialog::setDir(const QString &directory) + + Use setDirectory() instead. +*/ + +/*! + \fn void QFileDialog::setDir( const QDir &directory ) + + Use setDirectory() instead. +*/ + +/*! + \fn QStringList QFileDialog::getOpenFileNames(const QString &filter, + const QString &dir, QWidget *parent, const char* name, + const QString &caption, QString *selectedFilter, bool resolveSymlinks) + + Use the getOpenFileNames() overload that takes \a parent as the first + argument instead. +*/ + +/*! + \fn QString QFileDialog::getOpenFileName(const QString &dir, + const QString &filter, QWidget *parent = 0, const char *name, + const QString &caption, QString *selectedFilter, bool resolveSymlinks) + + Use the getOpenFileName() overload that takes \a parent as the first + argument instead. +*/ + +/*! + \fn QString QFileDialog::getSaveFileName(const QString &dir, + const QString &filter, QWidget *parent, const char *name, + const QString &caption, QString *selectedFilter, bool resolveSymlinks) + + Use the getSaveFileName() overload that takes \a parent as the first + argument instead. +*/ + +/*! + \fn QString QFileDialog::getExistingDirectory(const QString &dir, + QWidget *parent, const char *name, const QString &caption, + bool dirOnly, bool resolveSymlinks) + + Use the getExistingDirectory() overload that takes \a parent as + the first argument instead. +*/ + +#endif // QT3_SUPPORT + +QT_END_NAMESPACE + +#include "moc_qfiledialog.cpp" + +#endif // QT_NO_FILEDIALOG diff --git a/src/gui/dialogs/qfiledialog.h b/src/gui/dialogs/qfiledialog.h new file mode 100644 index 0000000000..d4b8d855d8 --- /dev/null +++ b/src/gui/dialogs/qfiledialog.h @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILEDIALOG_H +#define QFILEDIALOG_H + +#include <QtCore/qdir.h> +#include <QtCore/qstring.h> +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_FILEDIALOG + +class QModelIndex; +class QItemSelection; +struct QFileDialogArgs; +class QFileIconProvider; +class QFileDialogPrivate; +class QAbstractItemDelegate; +class QAbstractProxyModel; +class QUrl; + +class Q_GUI_EXPORT QFileDialog : public QDialog +{ + Q_OBJECT + Q_ENUMS(ViewMode FileMode AcceptMode Option) + Q_PROPERTY(ViewMode viewMode READ viewMode WRITE setViewMode) + Q_PROPERTY(FileMode fileMode READ fileMode WRITE setFileMode) + Q_PROPERTY(AcceptMode acceptMode READ acceptMode WRITE setAcceptMode) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE false) + Q_PROPERTY(bool resolveSymlinks READ resolveSymlinks WRITE setResolveSymlinks DESIGNABLE false) + Q_PROPERTY(bool confirmOverwrite READ confirmOverwrite WRITE setConfirmOverwrite DESIGNABLE false) + Q_PROPERTY(QString defaultSuffix READ defaultSuffix WRITE setDefaultSuffix) + Q_PROPERTY(bool nameFilterDetailsVisible READ isNameFilterDetailsVisible + WRITE setNameFilterDetailsVisible DESIGNABLE false) + Q_PROPERTY(Options options READ options WRITE setOptions) + +public: + enum ViewMode { Detail, List }; + enum FileMode { AnyFile, ExistingFile, Directory, ExistingFiles, DirectoryOnly }; + enum AcceptMode { AcceptOpen, AcceptSave }; + enum DialogLabel { LookIn, FileName, FileType, Accept, Reject }; + + // ### Rename to FileDialogOption and FileDialogOptions for Qt 5.0 + enum Option + { + ShowDirsOnly = 0x00000001, + DontResolveSymlinks = 0x00000002, + DontConfirmOverwrite = 0x00000004, + DontUseSheet = 0x00000008, + DontUseNativeDialog = 0x00000010, + ReadOnly = 0x00000020, + HideNameFilterDetails = 0x00000040 + }; + Q_DECLARE_FLAGS(Options, Option) + + QFileDialog(QWidget *parent, Qt::WindowFlags f); + explicit QFileDialog(QWidget *parent = 0, + const QString &caption = QString(), + const QString &directory = QString(), + const QString &filter = QString()); + ~QFileDialog(); + + void setDirectory(const QString &directory); + inline void setDirectory(const QDir &directory); + QDir directory() const; + + void selectFile(const QString &filename); + QStringList selectedFiles() const; + +#ifdef QT_DEPRECATED + QT_DEPRECATED void setFilter(const QString &filter); + QT_DEPRECATED void setFilters(const QStringList &filters); + QT_DEPRECATED QStringList filters() const; + QT_DEPRECATED void selectFilter(const QString &filter); + QT_DEPRECATED QString selectedFilter() const; +#endif + void setNameFilterDetailsVisible(bool enabled); + bool isNameFilterDetailsVisible() const; + + void setNameFilter(const QString &filter); + void setNameFilters(const QStringList &filters); + QStringList nameFilters() const; + void selectNameFilter(const QString &filter); + QString selectedNameFilter() const; + + QDir::Filters filter() const; + void setFilter(QDir::Filters filters); + + void setViewMode(ViewMode mode); + ViewMode viewMode() const; + + void setFileMode(FileMode mode); + FileMode fileMode() const; + + void setAcceptMode(AcceptMode mode); + AcceptMode acceptMode() const; + + void setReadOnly(bool enabled); + bool isReadOnly() const; + + void setResolveSymlinks(bool enabled); + bool resolveSymlinks() const; + + void setSidebarUrls(const QList<QUrl> &urls); + QList<QUrl> sidebarUrls() const; + + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + + void setConfirmOverwrite(bool enabled); + bool confirmOverwrite() const; + + void setDefaultSuffix(const QString &suffix); + QString defaultSuffix() const; + + void setHistory(const QStringList &paths); + QStringList history() const; + + void setItemDelegate(QAbstractItemDelegate *delegate); + QAbstractItemDelegate *itemDelegate() const; + + void setIconProvider(QFileIconProvider *provider); + QFileIconProvider *iconProvider() const; + + void setLabelText(DialogLabel label, const QString &text); + QString labelText(DialogLabel label) const; + +#ifndef QT_NO_PROXYMODEL + void setProxyModel(QAbstractProxyModel *model); + QAbstractProxyModel *proxyModel() const; +#endif + + void setOption(Option option, bool on = true); + bool testOption(Option option) const; + void setOptions(Options options); + Options options() const; + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + void setVisible(bool visible); + +Q_SIGNALS: + void fileSelected(const QString &file); + void filesSelected(const QStringList &files); + void currentChanged(const QString &path); + void directoryEntered(const QString &directory); + void filterSelected(const QString &filter); + +public: +#ifdef QT3_SUPPORT + typedef FileMode Mode; + inline QT3_SUPPORT void setMode(FileMode m) { setFileMode(m); } + inline QT3_SUPPORT FileMode mode() const { return fileMode(); } + inline QT3_SUPPORT void setDir(const QString &directory) { setDirectory(directory); } + inline QT3_SUPPORT void setDir( const QDir &directory ) { setDirectory(directory); } + QT3_SUPPORT QString selectedFile() const; +#endif + + static QString getOpenFileName(QWidget *parent = 0, + const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = 0, + Options options = 0); + + static QString getSaveFileName(QWidget *parent = 0, + const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = 0, + Options options = 0); + + static QString getExistingDirectory(QWidget *parent = 0, + const QString &caption = QString(), + const QString &dir = QString(), + Options options = ShowDirsOnly); + + static QStringList getOpenFileNames(QWidget *parent = 0, + const QString &caption = QString(), + const QString &dir = QString(), + const QString &filter = QString(), + QString *selectedFilter = 0, + Options options = 0); + +#ifdef QT3_SUPPORT + inline static QString QT3_SUPPORT getOpenFileName(const QString &dir, + const QString &filter = QString(), + QWidget *parent = 0, const char* name = 0, + const QString &caption = QString(), + QString *selectedFilter = 0, + bool resolveSymlinks = true) + { Q_UNUSED(name); + return getOpenFileName(parent, caption, dir, filter, selectedFilter, + resolveSymlinks ? Option(0) : DontResolveSymlinks); } + + inline static QString QT3_SUPPORT getSaveFileName(const QString &dir, + const QString &filter = QString(), + QWidget *parent = 0, const char* name = 0, + const QString &caption = QString(), + QString *selectedFilter = 0, + bool resolveSymlinks = true) + { Q_UNUSED(name); + return getSaveFileName(parent, caption, dir, filter, selectedFilter, + resolveSymlinks ? Option(0) : DontResolveSymlinks); } + + inline static QString QT3_SUPPORT getExistingDirectory(const QString &dir, + QWidget *parent = 0, + const char* name = 0, + const QString &caption = QString(), + bool dirOnly = true, + bool resolveSymlinks = true) + { Q_UNUSED(name); + return getExistingDirectory(parent, caption, dir, + Options((resolveSymlinks ? Option(0) : DontResolveSymlinks) + | (dirOnly ? ShowDirsOnly : Option(0)))); } + + inline static QStringList QT3_SUPPORT getOpenFileNames(const QString &filter, + const QString &dir = QString(), + QWidget *parent = 0, + const char* name = 0, + const QString &caption = QString(), + QString *selectedFilter = 0, + bool resolveSymlinks = true) + { Q_UNUSED(name); + return getOpenFileNames(parent, caption, dir, filter, selectedFilter, + resolveSymlinks ? Option(0) : DontResolveSymlinks); } +#endif // QT3_SUPPORT + +protected: + QFileDialog(const QFileDialogArgs &args); + void done(int result); + void accept(); + void changeEvent(QEvent *e); + +private: + Q_DECLARE_PRIVATE(QFileDialog) + Q_DISABLE_COPY(QFileDialog) + + Q_PRIVATE_SLOT(d_func(), void _q_pathChanged(const QString &)) + + Q_PRIVATE_SLOT(d_func(), void _q_navigateBackward()) + Q_PRIVATE_SLOT(d_func(), void _q_navigateForward()) + Q_PRIVATE_SLOT(d_func(), void _q_navigateToParent()) + Q_PRIVATE_SLOT(d_func(), void _q_createDirectory()) + Q_PRIVATE_SLOT(d_func(), void _q_showListView()) + Q_PRIVATE_SLOT(d_func(), void _q_showDetailsView()) + Q_PRIVATE_SLOT(d_func(), void _q_showContextMenu(const QPoint &)) + Q_PRIVATE_SLOT(d_func(), void _q_renameCurrent()) + Q_PRIVATE_SLOT(d_func(), void _q_deleteCurrent()) + Q_PRIVATE_SLOT(d_func(), void _q_showHidden()) + Q_PRIVATE_SLOT(d_func(), void _q_updateOkButton()) + Q_PRIVATE_SLOT(d_func(), void _q_currentChanged(const QModelIndex &index)) + Q_PRIVATE_SLOT(d_func(), void _q_enterDirectory(const QModelIndex &index)) + Q_PRIVATE_SLOT(d_func(), void _q_goToDirectory(const QString &path)) + Q_PRIVATE_SLOT(d_func(), void _q_useNameFilter(int index)) + Q_PRIVATE_SLOT(d_func(), void _q_selectionChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_goToUrl(const QUrl &url)) + Q_PRIVATE_SLOT(d_func(), void _q_goHome()) + Q_PRIVATE_SLOT(d_func(), void _q_showHeader(QAction *)) + Q_PRIVATE_SLOT(d_func(), void _q_autoCompleteFileName(const QString &text)) + Q_PRIVATE_SLOT(d_func(), void _q_rowsInserted(const QModelIndex & parent)) + Q_PRIVATE_SLOT(d_func(), void _q_fileRenamed(const QString &path, + const QString oldName, const QString newName)) +#if defined(Q_WS_MAC) + Q_PRIVATE_SLOT(d_func(), void _q_macRunNativeAppModalPanel()) +#endif +}; + +inline void QFileDialog::setDirectory(const QDir &adirectory) +{ setDirectory(adirectory.absolutePath()); } + +Q_DECLARE_OPERATORS_FOR_FLAGS(QFileDialog::Options) + +#endif // QT_NO_FILEDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFILEDIALOG_H diff --git a/src/gui/dialogs/qfiledialog.ui b/src/gui/dialogs/qfiledialog.ui new file mode 100644 index 0000000000..3afbd893c7 --- /dev/null +++ b/src/gui/dialogs/qfiledialog.ui @@ -0,0 +1,320 @@ +<ui version="4.0" > + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>QFileDialog</class> + <widget class="QDialog" name="QFileDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>521</width> + <height>316</height> + </rect> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="lookInLabel" > + <property name="text" > + <string>Look in:</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2" > + <layout class="QHBoxLayout" > + <item> + <widget class="QFileDialogComboBox" name="lookInCombo" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Ignored" > + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>50</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="backButton" > + <property name="toolTip" > + <string>Back</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="forwardButton" > + <property name="toolTip" > + <string>Forward</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="toParentButton" > + <property name="toolTip" > + <string>Parent Directory</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="newFolderButton" > + <property name="toolTip" > + <string>Create New Folder</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="listModeButton" > + <property name="toolTip" > + <string>List View</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="detailModeButton" > + <property name="toolTip" > + <string>Detail View</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0" colspan="3" > + <widget class="QSplitter" name="splitter" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <widget class="QSidebar" name="sidebar" /> + <widget class="QFrame" name="frame" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QStackedWidget" name="stackedWidget" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="page" > + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QFileDialogListView" name="listView" /> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_2" > + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QFileDialogTreeView" name="treeView" /> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="fileNameLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QFileDialogLineEdit" name="fileNameEdit" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Expanding" > + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item rowspan="2" row="2" column="2" > + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="fileTypeLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Files of type:</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QComboBox" name="fileTypeCombo" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QFileDialogTreeView</class> + <extends>QTreeView</extends> + <header>qfiledialog_p.h</header> + </customwidget> + <customwidget> + <class>QFileDialogListView</class> + <extends>QListView</extends> + <header>qfiledialog_p.h</header> + </customwidget> + <customwidget> + <class>QSidebar</class> + <extends>QListWidget</extends> + <header>qsidebar_p.h</header> + </customwidget> + <customwidget> + <class>QFileDialogLineEdit</class> + <extends>QLineEdit</extends> + <header>qfiledialog_p.h</header> + </customwidget> + <customwidget> + <class>QFileDialogComboBox</class> + <extends>QComboBox</extends> + <header>qfiledialog_p.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>lookInCombo</tabstop> + <tabstop>backButton</tabstop> + <tabstop>forwardButton</tabstop> + <tabstop>toParentButton</tabstop> + <tabstop>newFolderButton</tabstop> + <tabstop>listModeButton</tabstop> + <tabstop>detailModeButton</tabstop> + <tabstop>sidebar</tabstop> + <tabstop>listView</tabstop> + <tabstop>fileNameEdit</tabstop> + <tabstop>fileTypeCombo</tabstop> + <tabstop>buttonBox</tabstop> + <tabstop>treeView</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/gui/dialogs/qfiledialog_mac.mm b/src/gui/dialogs/qfiledialog_mac.mm new file mode 100644 index 0000000000..4c13d0113c --- /dev/null +++ b/src/gui/dialogs/qfiledialog_mac.mm @@ -0,0 +1,1113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfiledialog.h" + +#ifndef QT_NO_FILEDIALOG + +/***************************************************************************** + QFileDialog debug facilities + *****************************************************************************/ +//#define DEBUG_FILEDIALOG_FILTERS + +#include <qapplication.h> +#include <private/qapplication_p.h> +#include <private/qfiledialog_p.h> +#include <private/qt_mac_p.h> +#include <private/qt_cocoa_helpers_mac_p.h> +#include <qregexp.h> +#include <qbuffer.h> +#include <qdebug.h> +#include <qstringlist.h> +#include <qaction.h> +#include <qtextcodec.h> +#include <qvarlengtharray.h> +#include <qdesktopwidget.h> +#include <stdlib.h> +#include "ui_qfiledialog.h" + +QT_BEGIN_NAMESPACE + +extern QStringList qt_make_filter_list(const QString &filter); // qfiledialog.cpp +extern QStringList qt_clean_filter_list(const QString &filter); // qfiledialog.cpp +extern const char *qt_file_dialog_filter_reg_exp; // qfiledialog.cpp +extern bool qt_mac_is_macsheet(const QWidget *w); // qwidget_mac.mm + +QT_END_NAMESPACE + +QT_FORWARD_DECLARE_CLASS(QFileDialogPrivate) +QT_FORWARD_DECLARE_CLASS(QString) +QT_FORWARD_DECLARE_CLASS(QStringList) +QT_FORWARD_DECLARE_CLASS(QWidget) +QT_FORWARD_DECLARE_CLASS(QAction) +QT_FORWARD_DECLARE_CLASS(QFileInfo) +QT_USE_NAMESPACE + +@class QNSOpenSavePanelDelegate; + +@interface QNSOpenSavePanelDelegate : NSObject { + @public + NSOpenPanel *mOpenPanel; + NSSavePanel *mSavePanel; + NSView *mAccessoryView; + NSPopUpButton *mPopUpButton; + NSTextField *mTextField; + QFileDialogPrivate *mPriv; + NSString *mCurrentDir; + bool mConfirmOverwrite; + int mReturnCode; + + QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode) mAcceptMode; + QT_PREPEND_NAMESPACE(QDir::Filters) *mQDirFilter; + QT_PREPEND_NAMESPACE(QFileDialog::FileMode) mFileMode; + QT_PREPEND_NAMESPACE(QFileDialog::Options) *mFileOptions; + + QString *mLastFilterCheckPath; + QString *mCurrentSelection; + QStringList *mQDirFilterEntryList; + QStringList *mNameFilterDropDownList; + QStringList *mSelectedNameFilter; +} + +- (NSString *)strip:(const QString &)label; +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename; +- (void)filterChanged:(id)sender; +- (void)showModelessPanel; +- (BOOL)runApplicationModalPanel; +- (void)showWindowModalSheet:(QWidget *)docWidget; +- (void)updateProperties; +- (QStringList)acceptableExtensionsForSave; +- (QString)removeExtensions:(const QString &)filter; +- (void)createTextField; +- (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails; +- (void)createAccessory; + +@end + +@implementation QNSOpenSavePanelDelegate + +- (id)initWithAcceptMode:(QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode))acceptMode + title:(const QString &)title + nameFilters:(const QStringList &)nameFilters + selectedNameFilter:(const QString &)selectedNameFilter + hideNameFilterDetails:(bool)hideNameFilterDetails + qDirFilter:(QT_PREPEND_NAMESPACE(QDir::Filters))qDirFilter + fileOptions:(QT_PREPEND_NAMESPACE(QFileDialog::Options))fileOptions + fileMode:(QT_PREPEND_NAMESPACE(QFileDialog::FileMode))fileMode + selectFile:(const QString &)selectFile + confirmOverwrite:(bool)confirm + priv:(QFileDialogPrivate *)priv +{ + self = [super init]; + + mAcceptMode = acceptMode; + if (mAcceptMode == QT_PREPEND_NAMESPACE(QFileDialog::AcceptOpen)){ + mOpenPanel = [NSOpenPanel openPanel]; + mSavePanel = mOpenPanel; + } else { + mSavePanel = [NSSavePanel savePanel]; + mOpenPanel = 0; + } + + [mSavePanel setLevel:NSModalPanelWindowLevel]; + [mSavePanel setDelegate:self]; + mQDirFilter = new QT_PREPEND_NAMESPACE(QDir::Filters)(qDirFilter); + mFileOptions = new QT_PREPEND_NAMESPACE(QFileDialog::Options)(fileOptions); + mFileMode = fileMode; + mConfirmOverwrite = confirm; + mReturnCode = -1; + mPriv = priv; + mLastFilterCheckPath = new QString; + mQDirFilterEntryList = new QStringList; + mNameFilterDropDownList = new QStringList(nameFilters); + mSelectedNameFilter = new QStringList(qt_clean_filter_list(selectedNameFilter)); + QFileInfo sel(selectFile); + if (sel.isDir()){ + mCurrentDir = [qt_mac_QStringToNSString(sel.absoluteFilePath()) retain]; + mCurrentSelection = new QString; + } else { + mCurrentDir = [qt_mac_QStringToNSString(sel.absolutePath()) retain]; + mCurrentSelection = new QString(sel.absoluteFilePath()); + } + [mSavePanel setTitle:qt_mac_QStringToNSString(title)]; + [self createPopUpButton:selectedNameFilter hideDetails:hideNameFilterDetails]; + [self createTextField]; + [self createAccessory]; + [mSavePanel setAccessoryView:mNameFilterDropDownList->size() > 1 ? mAccessoryView : nil]; + + if (mPriv){ + [mSavePanel setPrompt:[self strip:mPriv->acceptLabel]]; + if (mPriv->fileNameLabelExplicitlySat) + [mSavePanel setNameFieldLabel:[self strip:mPriv->qFileDialogUi->fileNameLabel->text()]]; + } + + [self updateProperties]; + [mSavePanel retain]; + return self; +} + +- (void)dealloc +{ + delete mQDirFilter; + delete mFileOptions; + delete mLastFilterCheckPath; + delete mQDirFilterEntryList; + delete mNameFilterDropDownList; + delete mSelectedNameFilter; + delete mCurrentSelection; + + [mSavePanel orderOut:mSavePanel]; + [mSavePanel setAccessoryView:nil]; + [mPopUpButton release]; + [mTextField release]; + [mAccessoryView release]; + [mSavePanel setDelegate:nil]; + [mSavePanel release]; + [mCurrentDir release]; + [super dealloc]; +} + +- (NSString *)strip:(const QString &)label +{ + QAction a(label, 0); + return qt_mac_QStringToNSString(a.iconText()); +} + +- (void)closePanel +{ + *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]); + [mSavePanel close]; +} + +- (void)showModelessPanel +{ + if (mOpenPanel){ + QFileInfo info(*mCurrentSelection); + NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); + NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); + bool selectable = (mAcceptMode == QFileDialog::AcceptSave) + || [self panel:nil shouldShowFilename:filepath]; + [mOpenPanel + beginForDirectory:mCurrentDir + file:selectable ? filename : nil + types:nil + modelessDelegate:self + didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) + contextInfo:nil]; + } +} + +- (BOOL)runApplicationModalPanel +{ + QFileInfo info(*mCurrentSelection); + NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); + NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); + bool selectable = (mAcceptMode == QFileDialog::AcceptSave) + || [self panel:nil shouldShowFilename:filepath]; + mReturnCode = [mSavePanel + runModalForDirectory:mCurrentDir + file:selectable ? filename : @"untitled"]; + return (mReturnCode == NSOKButton); +} + +- (QT_PREPEND_NAMESPACE(QDialog::DialogCode))dialogResultCode +{ + return (mReturnCode == NSOKButton) ? QT_PREPEND_NAMESPACE(QDialog::Accepted) : QT_PREPEND_NAMESPACE(QDialog::Rejected); +} + +- (void)showWindowModalSheet:(QWidget *)docWidget +{ + Q_UNUSED(docWidget); + QFileInfo info(*mCurrentSelection); + NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); + NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); + bool selectable = (mAcceptMode == QFileDialog::AcceptSave) + || [self panel:nil shouldShowFilename:filepath]; + [mSavePanel + beginSheetForDirectory:mCurrentDir + file:selectable ? filename : nil +#ifdef QT_MAC_USE_COCOA + modalForWindow:QT_PREPEND_NAMESPACE(qt_mac_window_for)(docWidget) +#else + modalForWindow:nil +#endif + modalDelegate:self + didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) + contextInfo:nil]; +} + +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename +{ + Q_UNUSED(sender); + QString qtFileName = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename); + QFileInfo info(qtFileName); + QString path = info.absolutePath(); + if (path != *mLastFilterCheckPath){ + *mLastFilterCheckPath = path; + *mQDirFilterEntryList = info.dir().entryList(*mQDirFilter); + } + // Check if the QDir filter accepts the file: + if (!mQDirFilterEntryList->contains(info.fileName())) + return NO; + + // Always accept directories regardless of their names: + BOOL isDir; + if ([[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir] && isDir) + return YES; + + // No filter means accept everything + if (mSelectedNameFilter->isEmpty()) + return YES; + // Check if the current file name filter accepts the file: + for (int i=0; i<mSelectedNameFilter->size(); ++i) { + if (QDir::match(mSelectedNameFilter->at(i), qtFileName)) + return YES; + } + return NO; +} + +- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag +{ + Q_UNUSED(sender); + if (!okFlag) + return filename; + if (mConfirmOverwrite) + return filename; + + // User has clicked save, and no overwrite confirmation should occur. + // To get the latter, we need to change the name we return (hence the prefix): + return [@"___qt_very_unlikely_prefix_" stringByAppendingString:filename]; +} + +- (void)setNameFilters:(const QStringList &)filters hideDetails:(BOOL)hideDetails +{ + [mPopUpButton removeAllItems]; + *mNameFilterDropDownList = filters; + if (filters.size() > 0){ + for (int i=0; i<filters.size(); ++i) { + QString filter = hideDetails ? [self removeExtensions:filters.at(i)] : filters.at(i); + [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)]; + } + [mPopUpButton selectItemAtIndex:0]; + [mSavePanel setAccessoryView:mAccessoryView]; + } else + [mSavePanel setAccessoryView:nil]; + + [self filterChanged:self]; +} + +- (void)filterChanged:(id)sender +{ + // This mDelegate function is called when the _name_ filter changes. + Q_UNUSED(sender); + QString selection = mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]); + *mSelectedNameFilter = QT_PREPEND_NAMESPACE(qt_clean_filter_list)(selection); + [mSavePanel validateVisibleColumns]; + [self updateProperties]; + if (mPriv) + mPriv->QNSOpenSavePanelDelegate_filterSelected([mPopUpButton indexOfSelectedItem]); +} + +- (QString)currentNameFilter +{ + return mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]); +} + +- (QStringList)selectedFiles +{ + if (mOpenPanel) + return QT_PREPEND_NAMESPACE(qt_mac_NSArrayToQStringList)([mOpenPanel filenames]); + else{ + QStringList result; + QString filename = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]); + result << filename.remove(QLatin1String("___qt_very_unlikely_prefix_")); + return result; + } +} + +- (void)updateProperties +{ + // Call this functions if mFileMode, mFileOptions, + // mNameFilterDropDownList or mQDirFilter changes. + // The savepanel does not contain the neccessary functions for this. + bool chooseFilesOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFile) + || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles); + bool chooseDirsOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::Directory) + || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::DirectoryOnly) + || *mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ShowDirsOnly); + + [mOpenPanel setCanChooseFiles:!chooseDirsOnly]; + [mOpenPanel setCanChooseDirectories:!chooseFilesOnly]; + [mSavePanel setCanCreateDirectories:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ReadOnly))]; + [mOpenPanel setAllowsMultipleSelection:(mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles))]; + [mOpenPanel setResolvesAliases:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::DontResolveSymlinks))]; + + QStringList ext = [self acceptableExtensionsForSave]; + if (mPriv && !ext.isEmpty() && !mPriv->defaultSuffix.isEmpty()) + ext.prepend(mPriv->defaultSuffix); + [mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : QT_PREPEND_NAMESPACE(qt_mac_QStringListToNSMutableArray(ext))]; + + if ([mSavePanel isVisible]) + [mOpenPanel validateVisibleColumns]; +} + +- (void)panelSelectionDidChange:(id)sender +{ + Q_UNUSED(sender); + *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString([mSavePanel filename])); + if (mPriv) + mPriv->QNSOpenSavePanelDelegate_selectionChanged(*mCurrentSelection); +} + +- (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + Q_UNUSED(panel); + Q_UNUSED(contextInfo); + mReturnCode = returnCode; + if (mPriv) + mPriv->QNSOpenSavePanelDelegate_panelClosed(returnCode == NSOKButton); +} + +- (void)panel:(id)sender directoryDidChange:(NSString *)path +{ + Q_UNUSED(sender); + if (!mPriv) + return; + if ([path isEqualToString:mCurrentDir]) + return; + + [mCurrentDir release]; + mCurrentDir = [path retain]; + mPriv->QNSOpenSavePanelDelegate_directoryEntered(QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString(mCurrentDir))); +} + +/* + Returns a list of extensions (e.g. "png", "jpg", "gif") + for the current name filter. If a filter do not conform + to the format *.xyz or * or *.*, an empty list + is returned meaning accept everything. +*/ +- (QStringList)acceptableExtensionsForSave +{ + QStringList result; + for (int i=0; i<mSelectedNameFilter->count(); ++i) { + const QString &filter = mSelectedNameFilter->at(i); + if (filter.startsWith(QLatin1String("*.")) + && !filter.contains(QLatin1Char('?')) + && filter.count(QLatin1Char('*')) == 1) { + result += filter.mid(2); + } else { + return QStringList(); // Accept everything + } + } + return result; +} + +- (QString)removeExtensions:(const QString &)filter +{ + QRegExp regExp(QT_PREPEND_NAMESPACE(QString::fromLatin1)(QT_PREPEND_NAMESPACE(qt_file_dialog_filter_reg_exp))); + if (regExp.indexIn(filter) != -1) + return regExp.cap(1).trimmed(); + return filter; +} + +- (void)createTextField +{ + NSRect textRect = { { 0.0, 3.0 }, { 100.0, 25.0 } }; + mTextField = [[NSTextField alloc] initWithFrame:textRect]; + [[mTextField cell] setFont:[NSFont systemFontOfSize: + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; + [mTextField setAlignment:NSRightTextAlignment]; + [mTextField setEditable:false]; + [mTextField setSelectable:false]; + [mTextField setBordered:false]; + [mTextField setDrawsBackground:false]; + if (mPriv){ + [mTextField setStringValue:[self strip:mPriv->qFileDialogUi->fileTypeLabel->text()]]; + } else + [mTextField setStringValue:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(QT_PREPEND_NAMESPACE(QFileDialog::tr)("Files of type:"))]; +} + +- (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails +{ + NSRect popUpRect = { { 100.0, 5.0 }, { 250.0, 25.0 } }; + mPopUpButton = [[NSPopUpButton alloc] initWithFrame:popUpRect pullsDown:NO]; + [mPopUpButton setTarget:self]; + [mPopUpButton setAction:@selector(filterChanged:)]; + + QStringList *filters = mNameFilterDropDownList; + if (filters->size() > 0){ + for (int i=0; i<mNameFilterDropDownList->size(); ++i) { + QString filter = hideDetails ? [self removeExtensions:filters->at(i)] : filters->at(i); + [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)]; + if (filters->at(i) == selectedFilter) + [mPopUpButton selectItemAtIndex:i]; + } + } +} + +- (void)createAccessory +{ + NSRect accessoryRect = { { 0.0, 0.0 }, { 450.0, 33.0 } }; + mAccessoryView = [[NSView alloc] initWithFrame:accessoryRect]; + [mAccessoryView addSubview:mTextField]; + [mAccessoryView addSubview:mPopUpButton]; +} + +@end + +QT_BEGIN_NAMESPACE + +void QFileDialogPrivate::QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath) +{ + emit q_func()->currentChanged(newPath); +} + +void QFileDialogPrivate::QNSOpenSavePanelDelegate_panelClosed(bool accepted) +{ + if (accepted) + q_func()->accept(); + else + q_func()->reject(); +} + +void QFileDialogPrivate::QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir) +{ + setLastVisitedDirectory(newDir); + emit q_func()->directoryEntered(newDir); +} + +void QFileDialogPrivate::QNSOpenSavePanelDelegate_filterSelected(int menuIndex) +{ + emit q_func()->filterSelected(nameFilters.at(menuIndex)); +} + +extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp +extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); // qglobal.cpp + +void QFileDialogPrivate::setDirectory_sys(const QString &directory) +{ +#ifndef QT_MAC_USE_COCOA + if (directory == mCurrentLocation) + return; + mCurrentLocation = directory; + emit q_func()->directoryEntered(mCurrentLocation); + + FSRef fsRef; + if (qt_mac_create_fsref(directory, &fsRef) == noErr) { + AEDesc desc; + if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr) + NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc); + } +#else + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + [delegate->mSavePanel setDirectory:qt_mac_QStringToNSString(directory)]; +#endif +} + +QString QFileDialogPrivate::directory_sys() const +{ +#ifndef QT_MAC_USE_COCOA + return mCurrentLocation; +#else + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + return qt_mac_NSStringToQString([delegate->mSavePanel directory]); +#endif +} + +void QFileDialogPrivate::selectFile_sys(const QString &filename) +{ + QString filePath = filename; + if (QDir::isRelativePath(filePath)) + filePath = QFileInfo(directory_sys(), filePath).filePath(); + +#ifndef QT_MAC_USE_COCOA + // Update the selection list immidiatly, so + // subsequent calls to selectedFiles() gets correct: + mCurrentSelectionList.clear(); + mCurrentSelectionList << filename; + if (mCurrentSelection != filename){ + mCurrentSelection = filename; + emit q_func()->currentChanged(mCurrentSelection); + } + + AEDescList descList; + if (AECreateList(0, 0, false, &descList) != noErr) + return; + + FSRef fsRef; + if (qt_mac_create_fsref(filePath, &fsRef) == noErr) { + AEDesc desc; + if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr){ + if (AEPutDesc(&descList, 0, &desc) == noErr) + NavCustomControl(mDialog, kNavCtlSetSelection, (void*)&descList); + } + } + + // Type the file name into the save dialog's text field: + UInt8 *strBuffer = (UInt8 *)malloc(1024); + qt_mac_to_pascal_string(QFileInfo(filename).fileName(), strBuffer); + NavCustomControl(mDialog, kNavCtlSetEditFileName, strBuffer); + free(strBuffer); +#else + // There seems to no way to select a file once the dialog is running. + // So do the next best thing, set the file's directory: + setDirectory_sys(QFileInfo(filePath).absolutePath()); +#endif +} + +QStringList QFileDialogPrivate::selectedFiles_sys() const +{ +#ifndef QT_MAC_USE_COCOA + if (q_func()->acceptMode() == QFileDialog::AcceptOpen){ + return mCurrentSelectionList; + } else { + return QStringList() << mCurrentLocation + QLatin1Char('/') + + QCFString::toQString(NavDialogGetSaveFileName(mDialog)); + } +#else + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + return [delegate selectedFiles]; +#endif +} + +void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters) +{ +#ifndef QT_MAC_USE_COCOA + Q_UNUSED(filters); +#else + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + bool hideDetails = q_func()->testOption(QFileDialog::HideNameFilterDetails); + [delegate setNameFilters:filters hideDetails:hideDetails]; +#endif +} + +void QFileDialogPrivate::setFilter_sys() +{ +#ifndef QT_MAC_USE_COCOA +#else + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + *(delegate->mQDirFilter) = model->filter(); + [delegate updateProperties]; +#endif +} + +void QFileDialogPrivate::selectNameFilter_sys(const QString &filter) +{ + int index = nameFilters.indexOf(filter); + if (index != -1) { +#ifndef QT_MAC_USE_COCOA + NavMenuItemSpec navSpec; + bzero(&navSpec, sizeof(NavMenuItemSpec)); + navSpec.menuType = index; + NavCustomControl(mDialog, kNavCtlSelectCustomType, &navSpec); +#else + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + [delegate->mPopUpButton selectItemAtIndex:index]; + [delegate filterChanged:nil]; +#endif + } +} + +QString QFileDialogPrivate::selectedNameFilter_sys() const +{ +#ifndef QT_MAC_USE_COCOA + int index = filterInfo.currentSelection; +#else + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + int index = [delegate->mPopUpButton indexOfSelectedItem]; +#endif + return index != -1 ? nameFilters.at(index) : QString(); +} + +void QFileDialogPrivate::deleteNativeDialog_sys() +{ +#ifndef QT_MAC_USE_COCOA + if (mDialog) + NavDialogDispose(mDialog); + mDialog = 0; + mDialogStarted = false; +#else + QMacCocoaAutoReleasePool pool; + [reinterpret_cast<QNSOpenSavePanelDelegate *>(mDelegate) release]; + mDelegate = 0; +#endif + nativeDialogInUse = false; +} + +bool QFileDialogPrivate::setVisible_sys(bool visible) +{ + Q_Q(QFileDialog); + if (!visible == q->isHidden()) + return false; + +#ifndef QT_MAC_USE_COCOA + return visible ? showCarbonNavServicesDialog() : hideCarbonNavServicesDialog(); +#else + return visible ? showCocoaFilePanel() : hideCocoaFilePanel(); +#endif +} + +#ifndef QT_MAC_USE_COCOA +Boolean QFileDialogPrivate::qt_mac_filedialog_filter_proc(AEDesc *theItem, void *info, + void *data, NavFilterModes) +{ + QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data); + + if (!fileDialogPrivate || fileDialogPrivate->filterInfo.filters.isEmpty() + || (fileDialogPrivate->filterInfo.currentSelection < 0 + && fileDialogPrivate->filterInfo.currentSelection + >= fileDialogPrivate->filterInfo.filters.size())) + return true; + + NavFileOrFolderInfo *theInfo = static_cast<NavFileOrFolderInfo *>(info); + QString file; + const QtMacFilterName &fn + = fileDialogPrivate->filterInfo.filters.at(fileDialogPrivate->filterInfo.currentSelection); + if (theItem->descriptorType == typeFSRef) { + FSRef ref; + AEGetDescData(theItem, &ref, sizeof(ref)); + UInt8 str_buffer[1024]; + FSRefMakePath(&ref, str_buffer, 1024); + file = QString::fromUtf8(reinterpret_cast<const char *>(str_buffer)); + int slsh = file.lastIndexOf(QLatin1Char('/')); + if (slsh != -1) + file = file.right(file.length() - slsh - 1); + } + QStringList reg = fn.regexp.split(QLatin1String(";")); + for (QStringList::const_iterator it = reg.constBegin(); it != reg.constEnd(); ++it) { + QRegExp rg(*it, Qt::CaseInsensitive, QRegExp::Wildcard); +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("QFileDialogPrivate::qt_mac_filedialog_filter_proc:%d, asked to filter.. %s (%s)", __LINE__, + qPrintable(file), qPrintable(*it)); +#endif + if (rg.exactMatch(file)) + return true; + } + return (theInfo->isFolder && !file.endsWith(QLatin1String(".app"))); +} + +void QFileDialogPrivate::qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg, + NavCBRecPtr p, NavCallBackUserData data) +{ + QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data); + + switch(msg) { + case kNavCBPopupMenuSelect: { + NavMenuItemSpec *s = static_cast<NavMenuItemSpec *>(p->eventData.eventDataParms.param); + if (int(s->menuType) != fileDialogPrivate->filterInfo.currentSelection) { + fileDialogPrivate->filterInfo.currentSelection = s->menuType; + emit fileDialogPrivate->q_func()->filterSelected(fileDialogPrivate->nameFilters.at(s->menuType)); + } + if (fileDialogPrivate->acceptMode == QFileDialog::AcceptSave) { + QString base = QCFString::toQString(NavDialogGetSaveFileName(p->context)); + QFileInfo fi(base); + base = fi.completeBaseName(); + const QtMacFilterName &fn = fileDialogPrivate->filterInfo.filters.at( + fileDialogPrivate->filterInfo.currentSelection); + QStringList reg = fn.regexp.split(QLatin1String(";"), QString::SkipEmptyParts); + QString r = reg.first(); + r = r.right(r.length()-1); // Strip the * + base += r; //"." + QString::number(s->menuType); + NavDialogSetSaveFileName(p->context, QCFString::toCFStringRef(base)); + } +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("QFileDialogPrivate::qt_mac_filedialog_event_proc:%d - Selected a filter: %ld", __LINE__, s->menuType); +#endif + break; } + case kNavCBStart:{ + fileDialogPrivate->mDialogStarted = true; + // Set selected file: + QModelIndexList indexes = fileDialogPrivate->qFileDialogUi->listView->selectionModel()->selectedRows(); + QString selected; + if (!indexes.isEmpty()) + selected = indexes.at(0).data(QFileSystemModel::FilePathRole).toString(); + else + selected = fileDialogPrivate->typedFiles().value(0); + fileDialogPrivate->selectFile_sys(selected); + fileDialogPrivate->selectNameFilter_sys(fileDialogPrivate->qFileDialogUi->fileTypeCombo->currentText()); + break; } + case kNavCBSelectEntry:{ + // Event: Current selection has changed. + QStringList prevSelectionList = fileDialogPrivate->mCurrentSelectionList; + fileDialogPrivate->mCurrentSelectionList.clear(); + QString fileNameToEmit; + + AEDescList *descList = (AEDescList *)p->eventData.eventDataParms.param; + // Get the number of files selected: + UInt8 strBuffer[1024]; + long count; + OSErr err = AECountItems(descList, &count); + if (err != noErr || !count) + break; + + for (long index=1; index<=count; ++index) { + FSRef ref; + err = AEGetNthPtr(descList, index, typeFSRef, 0, 0, &ref, sizeof(ref), 0); + if (err != noErr) + break; + FSRefMakePath(&ref, strBuffer, 1024); + QString selected = QString::fromUtf8((const char *)strBuffer); + fileDialogPrivate->mCurrentSelectionList << selected; + if (!prevSelectionList.contains(selected)) + fileNameToEmit = selected; + } + + if (!fileNameToEmit.isEmpty() && fileNameToEmit != fileDialogPrivate->mCurrentSelection) + emit fileDialogPrivate->q_func()->currentChanged(fileNameToEmit); + fileDialogPrivate->mCurrentSelection = fileNameToEmit; + break; } + case kNavCBShowDesktop: + case kNavCBNewLocation:{ + // Event: Current directory has changed. + AEDesc *desc = (AEDesc *)p->eventData.eventDataParms.param; + FSRef ref; + AEGetDescData(desc, &ref, sizeof(ref)); + UInt8 *strBuffer = (UInt8 *)malloc(1024); + FSRefMakePath(&ref, strBuffer, 1024); + QString newLocation = QString::fromUtf8((const char *)strBuffer); + free(strBuffer); + if (fileDialogPrivate->mCurrentLocation != newLocation){ + fileDialogPrivate->mCurrentLocation = newLocation; + QFileDialog::FileMode mode = fileDialogPrivate->fileMode; + if (mode == QFileDialog::AnyFile || mode == QFileDialog::ExistingFile + || mode == QFileDialog::ExistingFiles){ + // When changing directory, the current selection is cleared if + // we are supposed to be selecting files only: + fileDialogPrivate->mCurrentSelectionList.clear(); + if (!fileDialogPrivate->mCurrentSelection.isEmpty()){ + fileDialogPrivate->mCurrentSelection.clear(); + emit fileDialogPrivate->q_func()->currentChanged(fileDialogPrivate->mCurrentSelection); + } + } + fileDialogPrivate->setLastVisitedDirectory(newLocation); + emit fileDialogPrivate->q_func()->directoryEntered(newLocation); + } + break; } + case kNavCBAccept: + fileDialogPrivate->mDialogClosed = true; + fileDialogPrivate->q_func()->accept(); + break; + case kNavCBCancel: + fileDialogPrivate->mDialogClosed = true; + fileDialogPrivate->q_func()->reject(); + break; + } +} + +static QFileDialogPrivate::QtMacFilterName qt_mac_extract_filter(const QString &rawFilter, bool showDetails) +{ + QFileDialogPrivate::QtMacFilterName ret; + ret.filter = rawFilter; + QString result = rawFilter; + QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); + int index = r.indexIn(result); + if (index >= 0) + result = r.cap(2); + + if (showDetails) { + ret.description = rawFilter; + } else { + if (index >= 0) + ret.description = r.cap(1).trimmed(); + if (ret.description.isEmpty()) + ret.description = result; + } + ret.regexp = result.replace(QLatin1Char(' '), QLatin1Char(';')); + return ret; +} + +static QList<QFileDialogPrivate::QtMacFilterName> qt_mac_make_filters_list(const QString &filter, bool showDetails) +{ +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("QFileDialog:%d - Got filter (%s)", __LINE__, filter.latin1()); +#endif + + QList<QFileDialogPrivate::QtMacFilterName> ret; + QString f(filter); + if (f.isEmpty()) + f = QFileDialog::tr("All Files (*)"); + if (f.isEmpty()) + return ret; + QStringList filts = qt_make_filter_list(f); + for (QStringList::const_iterator it = filts.constBegin(); it != filts.constEnd(); ++it) { + QFileDialogPrivate::QtMacFilterName filter = qt_mac_extract_filter(*it, showDetails); +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("QFileDialog:%d Split out filter (%d) '%s' '%s' [%s]", __LINE__, ret.count(), + filter->regxp.latin1(), filter->description.latin1(), (*it).latin1()); +#endif + ret.append(filter); + } + return ret; +} + +void QFileDialogPrivate::createNavServicesDialog() +{ + Q_Q(QFileDialog); + if (mDialog) + deleteNativeDialog_sys(); + + NavDialogCreationOptions navOptions; + NavGetDefaultDialogCreationOptions(&navOptions); + + // Translate QFileDialog settings into NavDialog options: + if (qt_mac_is_macsheet(q)) { + navOptions.modality = kWindowModalityWindowModal; + navOptions.parentWindow = qt_mac_window_for(q->parentWidget()); + } else if (q->windowModality() == Qt::ApplicationModal) + navOptions.modality = kWindowModalityAppModal; + else + navOptions.modality = kWindowModalityNone; + navOptions.optionFlags |= kNavSupportPackages; + if (q->testOption(QFileDialog::DontConfirmOverwrite)) + navOptions.optionFlags |= kNavDontConfirmReplacement; + if (fileMode != QFileDialog::ExistingFiles) + navOptions.optionFlags &= ~kNavAllowMultipleFiles; + + navOptions.windowTitle = QCFString::toCFStringRef(q->windowTitle()); + + static const int w = 450, h = 350; + navOptions.location.h = navOptions.location.v = -1; + QWidget *parent = q->parentWidget(); + if (parent && parent->isVisible()) { + WindowClass wclass; + GetWindowClass(qt_mac_window_for(parent), &wclass); + parent = parent->window(); + QString s = parent->windowTitle(); + navOptions.clientName = QCFString::toCFStringRef(s); + navOptions.location.h = (parent->x() + (parent->width() / 2)) - (w / 2); + navOptions.location.v = (parent->y() + (parent->height() / 2)) - (h / 2); + + QRect r = QApplication::desktop()->screenGeometry( + QApplication::desktop()->screenNumber(parent)); + const int border = 10; + if (navOptions.location.h + w > r.right()) + navOptions.location.h -= (navOptions.location.h + w) - r.right() + border; + if (navOptions.location.v + h > r.bottom()) + navOptions.location.v -= (navOptions.location.v + h) - r.bottom() + border; + if (navOptions.location.h < r.left()) + navOptions.location.h = r.left() + border; + if (navOptions.location.v < r.top()) + navOptions.location.v = r.top() + border; + } + + filterInfo.currentSelection = 0; + filterInfo.filters = qt_mac_make_filters_list(nameFilters.join(QLatin1String(";;")), q->isNameFilterDetailsVisible()); + QCFType<CFArrayRef> filterArray; + if (filterInfo.filters.size() > 1) { + int i = 0; + CFStringRef *cfstringArray = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef) + * filterInfo.filters.size())); + for (i = 0; i < filterInfo.filters.size(); ++i) { + cfstringArray[i] = QCFString::toCFStringRef(filterInfo.filters.at(i).description); + } + filterArray = CFArrayCreate(kCFAllocatorDefault, + reinterpret_cast<const void **>(cfstringArray), filterInfo.filters.size(), + &kCFTypeArrayCallBacks); + navOptions.popupExtension = filterArray; + free(cfstringArray); + } + + if (q->acceptMode() == QFileDialog::AcceptSave) { + if (NavCreatePutFileDialog(&navOptions, 'cute', kNavGenericSignature, + QFileDialogPrivate::qt_mac_filedialog_event_proc, this, &mDialog)) { + qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); + return; + } + } else if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) { + if (NavCreateChooseFolderDialog(&navOptions, + QFileDialogPrivate::qt_mac_filedialog_event_proc, 0, this, &mDialog)) { + qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); + return; + } + } else { + if (NavCreateGetFileDialog(&navOptions, 0, + QFileDialogPrivate::qt_mac_filedialog_event_proc, 0, + QFileDialogPrivate::qt_mac_filedialog_filter_proc, this, &mDialog)) { + qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); + return; + } + } + + // Set start-up directory: + if (mCurrentLocation.isEmpty()) + mCurrentLocation = rootPath(); + FSRef fsRef; + if (qt_mac_create_fsref(mCurrentLocation, &fsRef) == noErr) { + AEDesc desc; + if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr) + NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc); + } +} + +bool QFileDialogPrivate::showCarbonNavServicesDialog() +{ + Q_Q(QFileDialog); + if (q->acceptMode() == QFileDialog::AcceptSave && q->windowModality() == Qt::NonModal) + return false; // cannot do native no-modal save dialogs. + createNavServicesDialog(); + mDialogClosed = false; + if (q->windowModality() != Qt::ApplicationModal) + NavDialogRun(mDialog); + return true; +} + +bool QFileDialogPrivate::hideCarbonNavServicesDialog() +{ + if (!mDialogClosed){ + mDialogClosed = true; + NavCustomControl(mDialog, kNavCtlCancel, 0); + } + return true; +} + +#else // Cocoa + +void QFileDialogPrivate::createNSOpenSavePanelDelegate() +{ + Q_Q(QFileDialog); + if (mDelegate) + return; + + bool selectDir = q->selectedFiles().isEmpty(); + QString selection(selectDir ? q->directory().absolutePath() : q->selectedFiles().value(0)); + QNSOpenSavePanelDelegate *delegate = [[QNSOpenSavePanelDelegate alloc] + initWithAcceptMode:acceptMode + title:q->windowTitle() + nameFilters:q->nameFilters() + selectedNameFilter:q->selectedNameFilter() + hideNameFilterDetails:q->testOption(QFileDialog::HideNameFilterDetails) + qDirFilter:model->filter() + fileOptions:opts + fileMode:fileMode + selectFile:selection + confirmOverwrite:!q->testOption(QFileDialog::DontConfirmOverwrite) + priv:this]; + + mDelegate = delegate; +} + +bool QFileDialogPrivate::showCocoaFilePanel() +{ + Q_Q(QFileDialog); + QMacCocoaAutoReleasePool pool; + createNSOpenSavePanelDelegate(); + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + if (qt_mac_is_macsheet(q)) + [delegate showWindowModalSheet:q->parentWidget()]; + else + [delegate showModelessPanel]; + return true; +} + +bool QFileDialogPrivate::hideCocoaFilePanel() +{ + if (!mDelegate){ + // Nothing to do. We return false to leave the question + // open regarding whether or not to go native: + return false; + } else { + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + [delegate closePanel]; + // Even when we hide it, we are still using a + // native dialog, so return true: + return true; + } +} + +#endif + +void QFileDialogPrivate::mac_nativeDialogModalHelp() +{ + // Do a queued meta-call to open the native modal dialog so it opens after the new + // event loop has started to execute (in QDialog::exec). Using a timer rather than + // a queued meta call is intentional to ensure that the call is only delivered when + // [NSApp run] runs (timers are handeled special in cocoa). If NSApp is not + // running (which is the case if e.g a top-most QEventLoop has been + // interrupted, and the second-most event loop has not yet been reactivated (regardless + // if [NSApp run] is still on the stack)), showing a native modal dialog will fail. + if (nativeDialogInUse){ + Q_Q(QFileDialog); + QTimer::singleShot(1, q, SLOT(_q_macRunNativeAppModalPanel())); + } +} + +void QFileDialogPrivate::_q_macRunNativeAppModalPanel() +{ + QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); +#ifndef QT_MAC_USE_COCOA + NavDialogRun(mDialog); +#else + Q_Q(QFileDialog); + QMacCocoaAutoReleasePool pool; + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + [delegate runApplicationModalPanel]; + dialogResultCode_sys() == QDialog::Accepted ? q->accept() : q->reject(); +#endif +} + +QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys() +{ +#ifndef QT_MAC_USE_COCOA + NavUserAction result = NavDialogGetUserAction(mDialog); + if (result == kNavUserActionCancel || result == kNavUserActionNone) + return QDialog::Rejected; + else + return QDialog::Accepted; +#else + QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate); + return [delegate dialogResultCode]; +#endif +} + + +QT_END_NAMESPACE + +#endif // QT_NO_FILEDIALOG + diff --git a/src/gui/dialogs/qfiledialog_p.h b/src/gui/dialogs/qfiledialog_p.h new file mode 100644 index 0000000000..8cb2cb0b0a --- /dev/null +++ b/src/gui/dialogs/qfiledialog_p.h @@ -0,0 +1,458 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILEDIALOG_P_H +#define QFILEDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QT_NO_FILEDIALOG + +#include "qfiledialog.h" +#include "private/qdialog_p.h" +#include "qplatformdefs.h" + +#include "qfilesystemmodel_p.h" +#include <qlistview.h> +#include <qtreeview.h> +#include <qcombobox.h> +#include <qtoolbutton.h> +#include <qlabel.h> +#include <qevent.h> +#include <qlineedit.h> +#include <qurl.h> +#include <qstackedwidget.h> +#include <qdialogbuttonbox.h> +#include <qabstractproxymodel.h> +#include <qcompleter.h> +#include <qpointer.h> +#include <qtimeline.h> +#include <qdebug.h> +#include "qsidebar_p.h" + +#if defined (Q_OS_UNIX) +#include <unistd.h> +#endif + +QT_BEGIN_NAMESPACE + +class QFileDialogListView; +class QFileDialogTreeView; +class QFileDialogLineEdit; +class QGridLayout; +class QCompleter; +class QHBoxLayout; +class Ui_QFileDialog; + +struct QFileDialogArgs +{ + QFileDialogArgs() : parent(0), mode(QFileDialog::AnyFile) {} + + QWidget *parent; + QString caption; + QString directory; + QString selection; + QString filter; + QFileDialog::FileMode mode; + QFileDialog::Options options; +}; + +#define UrlRole (Qt::UserRole + 1) + +class Q_AUTOTEST_EXPORT QFileDialogPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QFileDialog) + +public: + QFileDialogPrivate() : +#ifndef QT_NO_PROXYMODEL + proxyModel(0), +#endif + model(0), + fileMode(QFileDialog::AnyFile), + acceptMode(QFileDialog::AcceptOpen), + currentHistoryLocation(-1), + renameAction(0), + deleteAction(0), + showHiddenAction(0), + useDefaultCaption(true), + defaultFileTypes(true), + fileNameLabelExplicitlySat(false), + nativeDialogInUse(false), +#ifdef Q_WS_MAC + mDelegate(0), +#ifndef QT_MAC_USE_COCOA + mDialog(0), + mDialogStarted(false), + mDialogClosed(true), +#endif +#endif + qFileDialogUi(0) + {} + + void createToolButtons(); + void createMenuActions(); + void createWidgets(); + + void init(const QString &directory = QString(), const QString &nameFilter = QString(), + const QString &caption = QString()); + bool itemViewKeyboardEvent(QKeyEvent *event); + QString getEnvironmentVariable(const QString &string); + static QString workingDirectory(const QString &path); + static QString initialSelection(const QString &path); + QStringList typedFiles() const; + QStringList addDefaultSuffixToFiles(const QStringList filesToFix) const; + bool removeDirectory(const QString &path); + + inline QModelIndex mapToSource(const QModelIndex &index) const; + inline QModelIndex mapFromSource(const QModelIndex &index) const; + inline QModelIndex rootIndex() const; + inline void setRootIndex(const QModelIndex &index) const; + inline QModelIndex select(const QModelIndex &index) const; + inline QString rootPath() const; + + QLineEdit *lineEdit() const; + + int maxNameLength(const QString &path) { +#if defined(Q_OS_UNIX) + return ::pathconf(QFile::encodeName(path).data(), _PC_NAME_MAX); +#elif defined(Q_OS_WIN) +#ifndef Q_OS_WINCE + DWORD maxLength; + QString drive = path.left(3); + if (QT_WA_INLINE(::GetVolumeInformationW(reinterpret_cast<const WCHAR *>(drive.utf16()), NULL, 0, NULL, &maxLength, NULL, NULL, 0), + ::GetVolumeInformationA(drive.toLocal8Bit().constData(), NULL, 0, NULL, &maxLength, NULL, NULL, 0)) == FALSE) + return -1; + return maxLength; +#else + Q_UNUSED(path); + return MAX_PATH; +#endif //Q_OS_WINCE +#else + Q_UNUSED(path); +#endif + return -1; + } + + QString basename(const QString &path) const + { + int separator = QDir::toNativeSeparators(path).lastIndexOf(QDir::separator()); + if (separator != -1) + return path.mid(separator + 1); + return path; + } + + QDir::Filters filterForMode(QDir::Filters filters) const + { + if (fileMode == QFileDialog::DirectoryOnly) { + filters |= QDir::Drives | QDir::AllDirs | QDir::Dirs; + filters &= ~QDir::Files; + } else { + filters |= QDir::Drives | QDir::AllDirs | QDir::Files | QDir::Dirs; + } + return filters; + } + + QAbstractItemView *currentView() const; + + static inline QString toInternal(const QString &path) + { +#if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX) + QString n(path); + for (int i = 0; i < (int)n.length(); ++i) + if (n[i] == QLatin1Char('\\')) n[i] = QLatin1Char('/'); +#if defined(Q_OS_WINCE) + if ((n.size() > 1) && (n.startsWith(QLatin1String("//")))) + n = n.mid(1); +#endif + return n; +#else // the compile should optimize away this + return path; +#endif + } + + void setLastVisitedDirectory(const QString &dir); + void retranslateWindowTitle(); + void retranslateStrings(); + void emitFilesSelected(const QStringList &files); + + void _q_goHome(); + void _q_pathChanged(const QString &); + void _q_navigateBackward(); + void _q_navigateForward(); + void _q_navigateToParent(); + void _q_createDirectory(); + void _q_showListView(); + void _q_showDetailsView(); + void _q_showContextMenu(const QPoint &position); + void _q_renameCurrent(); + void _q_deleteCurrent(); + void _q_showHidden(); + void _q_showHeader(QAction *); + void _q_updateOkButton(); + void _q_currentChanged(const QModelIndex &index); + void _q_enterDirectory(const QModelIndex &index); + void _q_goToDirectory(const QString &); + void _q_useNameFilter(int index); + void _q_selectionChanged(); + void _q_goToUrl(const QUrl &url); + void _q_autoCompleteFileName(const QString &); + void _q_rowsInserted(const QModelIndex & parent); + void _q_fileRenamed(const QString &path, const QString oldName, const QString newName); + + // layout +#ifndef QT_NO_PROXYMODEL + QAbstractProxyModel *proxyModel; +#endif + + // data + QStringList watching; + QFileSystemModel *model; + QCompleter *completer; + + QFileDialog::FileMode fileMode; + QFileDialog::AcceptMode acceptMode; + bool confirmOverwrite; + QString defaultSuffix; + QString setWindowTitle; + + QStringList currentHistory; + int currentHistoryLocation; + + QAction *renameAction; + QAction *deleteAction; + QAction *showHiddenAction; + QAction *newFolderAction; + + bool useDefaultCaption; + bool defaultFileTypes; + bool fileNameLabelExplicitlySat; + QStringList nameFilters; + + // Members for using native dialogs: + bool nativeDialogInUse; + // setVisible_sys returns true if it ends up showing a native + // dialog. Returning false means that a non-native dialog must be + // used instead. + bool canBeNativeDialog(); + bool setVisible_sys(bool visible); + void deleteNativeDialog_sys(); + QDialog::DialogCode dialogResultCode_sys(); + + void setDirectory_sys(const QString &directory); + QString directory_sys() const; + void selectFile_sys(const QString &filename); + QStringList selectedFiles_sys() const; + void setFilter_sys(); + void setNameFilters_sys(const QStringList &filters); + void selectNameFilter_sys(const QString &filter); + QString selectedNameFilter_sys() const; + ////////////////////////////////////////////// + +#if defined(Q_WS_MAC) + void *mDelegate; +#ifndef QT_MAC_USE_COCOA + NavDialogRef mDialog; + bool mDialogStarted; + bool mDialogClosed; + QString mCurrentLocation; + QString mCurrentSelection; + QStringList mCurrentSelectionList; + + struct QtMacFilterName { + QString description; + QString regexp; + QString filter; + }; + struct QtMacNavFilterInfo { + QtMacNavFilterInfo() : currentSelection(-1) {} + int currentSelection; + QList<QtMacFilterName> filters; + } filterInfo; + + static void qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg, NavCBRecPtr p, + NavCallBackUserData data); + static Boolean qt_mac_filedialog_filter_proc(AEDesc *theItem, void *info, void *data, + NavFilterModes); + bool showCarbonNavServicesDialog(); + bool hideCarbonNavServicesDialog(); + void createNavServicesDialog(); +#else + bool showCocoaFilePanel(); + bool hideCocoaFilePanel(); +#endif + void createNSOpenSavePanelDelegate(); + void QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath); + void QNSOpenSavePanelDelegate_panelClosed(bool accepted); + void QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir); + void QNSOpenSavePanelDelegate_filterSelected(int menuIndex); + void _q_macRunNativeAppModalPanel(); + void mac_nativeDialogModalHelp(); +#endif + + Ui_QFileDialog *qFileDialogUi; + + QString acceptLabel; + + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; + QByteArray signalToDisconnectOnClose; + + QFileDialog::Options opts; +}; + +class QFileDialogLineEdit : public QLineEdit +{ +public: + QFileDialogLineEdit(QWidget *parent = 0) : QLineEdit(parent), hideOnEsc(false), d_ptr(0){} + void init(QFileDialogPrivate *d_pointer) {d_ptr = d_pointer; } + void keyPressEvent(QKeyEvent *e); + bool hideOnEsc; +private: + QFileDialogPrivate *d_ptr; +}; + +class QFileDialogComboBox : public QComboBox +{ +public: + QFileDialogComboBox(QWidget *parent = 0) : QComboBox(parent), urlModel(0) {} + void init(QFileDialogPrivate *d_pointer); + void showPopup(); + void setHistory(const QStringList &paths); + QStringList history() const { return m_history; } + void paintEvent(QPaintEvent *); + +private: + QUrlModel *urlModel; + QFileDialogPrivate *d_ptr; + QStringList m_history; +}; + +class QFileDialogListView : public QListView +{ +public: + QFileDialogListView(QWidget *parent = 0); + void init(QFileDialogPrivate *d_pointer); + QSize sizeHint() const; +protected: + void keyPressEvent(QKeyEvent *e); +private: + QFileDialogPrivate *d_ptr; +}; + +class QFileDialogTreeView : public QTreeView +{ +public: + QFileDialogTreeView(QWidget *parent); + void init(QFileDialogPrivate *d_pointer); + QSize sizeHint() const; + +protected: + void keyPressEvent(QKeyEvent *e); +private: + QFileDialogPrivate *d_ptr; +}; + +inline QModelIndex QFileDialogPrivate::mapToSource(const QModelIndex &index) const { +#ifdef QT_NO_PROXYMODEL + return index; +#else + return proxyModel ? proxyModel->mapToSource(index) : index; +#endif +} +inline QModelIndex QFileDialogPrivate::mapFromSource(const QModelIndex &index) const { +#ifdef QT_NO_PROXYMODEL + return index; +#else + return proxyModel ? proxyModel->mapFromSource(index) : index; +#endif +} + +inline QString QFileDialogPrivate::rootPath() const { + return model->rootPath(); +} + +#ifndef Q_WS_MAC + // Dummies for platforms that don't use native dialogs: + inline void QFileDialogPrivate::deleteNativeDialog_sys() {} + inline bool QFileDialogPrivate::setVisible_sys(bool) { return false; } + inline QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys(){ return QDialog::Rejected; } + inline void QFileDialogPrivate::setDirectory_sys(const QString &) {} + inline QString QFileDialogPrivate::directory_sys() const { return QString(); } + inline void QFileDialogPrivate::selectFile_sys(const QString &) {} + inline QStringList QFileDialogPrivate::selectedFiles_sys() const { return QStringList(); } + inline void QFileDialogPrivate::setFilter_sys() {} + inline void QFileDialogPrivate::setNameFilters_sys(const QStringList &) {} + inline void QFileDialogPrivate::selectNameFilter_sys(const QString &) {} + inline QString QFileDialogPrivate::selectedNameFilter_sys() const { return QString(); } +#endif + +#ifndef QT_NO_COMPLETER +/*! + QCompleter that can deal with QFileSystemModel + */ +class QFSCompletor : public QCompleter { +public: + QFSCompletor(QAbstractItemModel *model, QObject *parent = 0) : QCompleter(model, parent) + { +#ifdef Q_OS_WIN + setCaseSensitivity(Qt::CaseInsensitive); +#endif + } + QString pathFromIndex(const QModelIndex &index) const; + QStringList splitPath(const QString& path) const; +}; +#endif // QT_NO_COMPLETER + +QT_END_NAMESPACE + +#endif // QT_NO_FILEDIALOG + +#endif // QFILEDIALOG_P_H diff --git a/src/gui/dialogs/qfiledialog_win.cpp b/src/gui/dialogs/qfiledialog_win.cpp new file mode 100644 index 0000000000..84314882ef --- /dev/null +++ b/src/gui/dialogs/qfiledialog_win.cpp @@ -0,0 +1,821 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfiledialog.h" + +#ifndef QT_NO_FILEDIALOG + +#include <private/qfiledialog_p.h> +#include <qapplication.h> +#include <private/qapplication_p.h> +#include <qt_windows.h> +#include <qglobal.h> +#include <qregexp.h> +#include <qbuffer.h> +#include <qdir.h> +#include <qstringlist.h> +#include <qlibrary.h> + +#ifndef QT_NO_THREAD +# include <private/qmutexpool_p.h> +#endif + +#include <shlobj.h> + +#ifdef Q_OS_WINCE +#include <commdlg.h> +# ifndef BFFM_SETSELECTION +# define BFFM_SETSELECTION (WM_USER + 102) +# endif +// Windows Mobile has a broken definition for BROWSEINFO +// Only compile fix +typedef struct qt_priv_browseinfo { + HWND hwndOwner; + LPCITEMIDLIST pidlRoot; + LPTSTR pszDisplayName; + LPCTSTR lpszTitle; + UINT ulFlags; + BFFCALLBACK lpfn; + LPARAM lParam; + int iImage; +} qt_BROWSEINFO; +bool qt_priv_ptr_valid = false; +#endif + + +// Don't remove the lines below! +// +// resolving the W methods manually is needed, because Windows 95 doesn't include +// these methods in Shell32.lib (not even stubs!), so you'd get an unresolved symbol +// when Qt calls getExistingDirectory(), etc. +typedef LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(BROWSEINFO*); +static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0; +typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(LPITEMIDLIST,LPWSTR); +static PtrSHGetPathFromIDList ptrSHGetPathFromIDList = 0; + +QT_BEGIN_NAMESPACE + +static void qt_win_resolve_libs() +{ + static bool triedResolve = false; + + if (!triedResolve) { +#ifndef QT_NO_THREAD + // protect initialization + QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); + // check triedResolve again, since another thread may have already + // done the initialization + if (triedResolve) { + // another thread did initialize the security function pointers, + // so we shouldn't do it again. + return; + } +#endif + + triedResolve = true; + if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { +#if !defined(Q_OS_WINCE) + QLibrary lib(QLatin1String("shell32")); + ptrSHBrowseForFolder = (PtrSHBrowseForFolder) lib.resolve("SHBrowseForFolderW"); + ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList) lib.resolve("SHGetPathFromIDListW"); +#else + // CE stores them in a different lib and does not use unicode version + HINSTANCE handle = LoadLibraryW(L"Ceshell"); + ptrSHBrowseForFolder = (PtrSHBrowseForFolder)GetProcAddress(handle, L"SHBrowseForFolder"); + ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)GetProcAddress(handle, L"SHGetPathFromIDList"); + if (ptrSHBrowseForFolder && ptrSHGetPathFromIDList) + qt_priv_ptr_valid = true; +#endif + } + } +} + +extern const char* qt_file_dialog_filter_reg_exp; // defined in qfiledialog.cpp +extern QStringList qt_make_filter_list(const QString &filter); + +const int maxNameLen = 1023; +const int maxMultiLen = 65535; + +// Returns the wildcard part of a filter. +static QString qt_win_extract_filter(const QString &rawFilter) +{ + QString result = rawFilter; + QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); + int index = r.indexIn(result); + if (index >= 0) + result = r.cap(2); + QStringList list = result.split(QLatin1Char(' ')); + for(QStringList::iterator it = list.begin(); it < list.end(); ++it) { + if (*it == QLatin1String("*")) { + *it = QLatin1String("*.*"); + break; + } + } + return list.join(QLatin1String(";")); +} + +static QStringList qt_win_make_filters_list(const QString &filter) +{ + QString f(filter); + + if (f.isEmpty()) + f = QFileDialog::tr("All Files (*.*)"); + + return qt_make_filter_list(f); +} + +// Makes a NUL-oriented Windows filter from a Qt filter. +static QString qt_win_filter(const QString &filter) +{ + QStringList filterLst = qt_win_make_filters_list(filter); + QStringList::Iterator it = filterLst.begin(); + QString winfilters; + for (; it != filterLst.end(); ++it) { + QString subfilter = *it; + if (!subfilter.isEmpty()) { + winfilters += subfilter; + winfilters += QChar(); + winfilters += qt_win_extract_filter(subfilter); + winfilters += QChar(); + } + } + winfilters += QChar(); + return winfilters; +} + +static QString qt_win_selected_filter(const QString &filter, DWORD idx) +{ + return qt_win_make_filters_list(filter).at((int)idx - 1); +} + +#ifndef Q_OS_WINCE +// Static vars for OFNA funcs: +static QByteArray aInitDir; +static QByteArray aInitSel; +static QByteArray aTitle; +static QByteArray aFilter; +// Use ANSI strings and API + +// If you change this, then make sure you change qt_win_make_OFN (below) too +static OPENFILENAMEA *qt_win_make_OFNA(QWidget *parent, + const QString &initialSelection, + const QString &initialDirectory, + const QString &title, + const QString &filters, + QFileDialog::FileMode mode, + QFileDialog::Options options) +{ + if (parent) + parent = parent->window(); + else + parent = qApp->activeWindow(); + + aTitle = title.toLocal8Bit(); + aInitDir = QDir::toNativeSeparators(initialDirectory).toLocal8Bit(); + if (initialSelection.isEmpty()) { + aInitSel = ""; + } else { + aInitSel = QDir::toNativeSeparators(initialSelection).toLocal8Bit(); + aInitSel.replace("<", ""); + aInitSel.replace(">", ""); + aInitSel.replace("\"", ""); + aInitSel.replace("|", ""); + } + int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen; + aInitSel.resize(maxLen + 1); // make room for return value + aFilter = filters.toLocal8Bit(); + + OPENFILENAMEA* ofn = new OPENFILENAMEA; + memset(ofn, 0, sizeof(OPENFILENAMEA)); + +#if defined(Q_CC_BOR) && (WINVER >= 0x0500) && (_WIN32_WINNT >= 0x0500) + // according to the MSDN, this should also be necessary for MSVC, but + // OPENFILENAME_SIZE_VERSION_400A is in not Microsoft header, as it seems + if (QApplication::winVersion()==Qt::WV_NT || QApplication::winVersion()&Qt::WV_DOS_based) { + ofn->lStructSize = OPENFILENAME_SIZE_VERSION_400A; + } else { + ofn->lStructSize = sizeof(OPENFILENAMEA); + } +#else + ofn->lStructSize = sizeof(OPENFILENAMEA); +#endif + Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); + ofn->hwndOwner = parent ? parent->winId() : 0; + ofn->lpstrFilter = aFilter; + ofn->lpstrFile = aInitSel.data(); + ofn->nMaxFile = maxLen; + ofn->lpstrInitialDir = aInitDir.data(); + ofn->lpstrTitle = aTitle.data(); + ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER); + + if (mode == QFileDialog::ExistingFile || + mode == QFileDialog::ExistingFiles) + ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); + if (mode == QFileDialog::ExistingFiles) + ofn->Flags |= (OFN_ALLOWMULTISELECT); + if (!(options & QFileDialog::DontConfirmOverwrite)) + ofn->Flags |= OFN_OVERWRITEPROMPT; + + return ofn; +} + +static void qt_win_clean_up_OFNA(OPENFILENAMEA **ofn) +{ + delete *ofn; + *ofn = 0; +} +#endif + +static QString tFilters, tTitle, tInitDir; + +#ifdef UNICODE +// If you change this, then make sure you change qt_win_make_OFNA (above) too +static OPENFILENAME* qt_win_make_OFN(QWidget *parent, + const QString& initialSelection, + const QString& initialDirectory, + const QString& title, + const QString& filters, + QFileDialog::FileMode mode, + QFileDialog::Options options) +{ + if (parent) + parent = parent->window(); + else + parent = qApp->activeWindow(); + + tInitDir = QDir::toNativeSeparators(initialDirectory); + tFilters = filters; + tTitle = title; + QString initSel = QDir::toNativeSeparators(initialSelection); + if (!initSel.isEmpty()) { + initSel.replace(QLatin1String("<"), QLatin1String("")); + initSel.replace(QLatin1String(">"), QLatin1String("")); + initSel.replace(QLatin1String("\""), QLatin1String("")); + initSel.replace(QLatin1String("|"), QLatin1String("")); + } + + int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen; + TCHAR *tInitSel = new TCHAR[maxLen+1]; + if (initSel.length() > 0 && initSel.length() <= maxLen) + memcpy(tInitSel, initSel.utf16(), (initSel.length()+1)*sizeof(QChar)); + else + tInitSel[0] = 0; + + OPENFILENAME* ofn = new OPENFILENAME; + memset(ofn, 0, sizeof(OPENFILENAME)); + +#if defined(Q_CC_BOR) && (WINVER >= 0x0500) && (_WIN32_WINNT >= 0x0500) + // according to the MSDN, this should also be necessary for MSVC, but + // OPENFILENAME_SIZE_VERSION_400 is in not Microsoft header, as it seems + if (QApplication::winVersion()==Qt::WV_NT || QApplication::winVersion()&Qt::WV_DOS_based) { + ofn->lStructSize= OPENFILENAME_SIZE_VERSION_400; + } else { + ofn->lStructSize = sizeof(OPENFILENAME); + } +#else + ofn->lStructSize = sizeof(OPENFILENAME); +#endif + Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); + ofn->hwndOwner = parent ? parent->winId() : 0; + ofn->lpstrFilter = (TCHAR *)tFilters.utf16(); + ofn->lpstrFile = tInitSel; + ofn->nMaxFile = maxLen; + ofn->lpstrInitialDir = (TCHAR *)tInitDir.utf16(); + ofn->lpstrTitle = (TCHAR *)tTitle.utf16(); + ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER); + if (mode == QFileDialog::ExistingFile || + mode == QFileDialog::ExistingFiles) + ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); + if (mode == QFileDialog::ExistingFiles) + ofn->Flags |= (OFN_ALLOWMULTISELECT); + if (!(options & QFileDialog::DontConfirmOverwrite)) + ofn->Flags |= OFN_OVERWRITEPROMPT; + + return ofn; +} + + +static void qt_win_clean_up_OFN(OPENFILENAME **ofn) +{ + delete [] (*ofn)->lpstrFile; + delete *ofn; + *ofn = 0; +} + +#endif // UNICODE + +extern void qt_win_eatMouseMove(); + +QString qt_win_get_open_file_name(const QFileDialogArgs &args, + QString *initialDirectory, + QString *selectedFilter) +{ + QString result; + + QString isel = args.selection; + + if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:")) + initialDirectory->remove(0, 5); + QFileInfo fi(*initialDirectory); + + if (initialDirectory && !fi.isDir()) { + *initialDirectory = fi.absolutePath(); + if (isel.isEmpty()) + isel = fi.fileName(); + } + + if (!fi.exists()) + *initialDirectory = QDir::homePath(); + + DWORD selFilIdx = 0; + + int idx = 0; + if (selectedFilter) { + QStringList filterLst = qt_win_make_filters_list(args.filter); + idx = filterLst.indexOf(*selectedFilter); + } + + QDialog modal_widget; + modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); + modal_widget.setParent(args.parent, Qt::Window); + QApplicationPrivate::enterModal(&modal_widget); + QT_WA({ + // Use Unicode strings and API + OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection, + args.directory, args.caption, + qt_win_filter(args.filter), + QFileDialog::ExistingFile, + args.options); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetOpenFileName(ofn)) { + result = QString::fromUtf16((ushort*)ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + qt_win_clean_up_OFN(&ofn); + } , { + // Use ANSI strings and API + OPENFILENAMEA* ofn = qt_win_make_OFNA(args.parent, args.selection, + args.directory, args.caption, + qt_win_filter(args.filter), + QFileDialog::ExistingFile, + args.options); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetOpenFileNameA(ofn)) { + result = QString::fromLocal8Bit(ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + qt_win_clean_up_OFNA(&ofn); + }); + QApplicationPrivate::leaveModal(&modal_widget); + + qt_win_eatMouseMove(); + + if (result.isEmpty()) + return result; + + fi = result; + *initialDirectory = fi.path(); + if (selectedFilter) + *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx); + return fi.absoluteFilePath(); +} + +QString qt_win_get_save_file_name(const QFileDialogArgs &args, + QString *initialDirectory, + QString *selectedFilter) +{ + QString result; + + QString isel = args.selection; + if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:")) + initialDirectory->remove(0, 5); + QFileInfo fi(*initialDirectory); + + if (initialDirectory && !fi.isDir()) { + *initialDirectory = fi.absolutePath(); + if (isel.isEmpty()) + isel = fi.fileName(); + } + + if (!fi.exists()) + *initialDirectory = QDir::homePath(); + + DWORD selFilIdx = 0; + + int idx = 0; + if (selectedFilter) { + QStringList filterLst = qt_win_make_filters_list(args.filter); + idx = filterLst.indexOf(*selectedFilter); + } + + QDialog modal_widget; + modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); + modal_widget.setParent(args.parent, Qt::Window); + QApplicationPrivate::enterModal(&modal_widget); + + // This block is used below for the lpstrDefExt member. + // Note that the current MSDN docs document this member wrong. + // It should rather be documented as "the default extension if no extension was given and if the + // current filter does not have a extension (e.g (*)). If the current filter have an extension, use + // the extension of the current filter" + QString defaultSaveExt; + if (selectedFilter && !selectedFilter->isEmpty()) { + defaultSaveExt = qt_win_extract_filter(*selectedFilter); + // make sure we only have the extension + int firstDot = defaultSaveExt.indexOf(QLatin1Char('.')); + if (firstDot != -1) { + defaultSaveExt.remove(0, firstDot + 1); + } else { + defaultSaveExt.clear(); + } + } + + QT_WA({ + // Use Unicode strings and API + OPENFILENAME *ofn = qt_win_make_OFN(args.parent, args.selection, + args.directory, args.caption, + qt_win_filter(args.filter), + QFileDialog::AnyFile, + args.options); + + ofn->lpstrDefExt = (TCHAR *)defaultSaveExt.utf16(); + + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetSaveFileName(ofn)) { + result = QString::fromUtf16((ushort*)ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + qt_win_clean_up_OFN(&ofn); + } , { + // Use ANSI strings and API + OPENFILENAMEA *ofn = qt_win_make_OFNA(args.parent, args.selection, + args.directory, args.caption, + qt_win_filter(args.filter), + QFileDialog::AnyFile, + args.options); + QByteArray asciiExt = defaultSaveExt.toAscii(); + ofn->lpstrDefExt = asciiExt.data(); + + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetSaveFileNameA(ofn)) { + result = QString::fromLocal8Bit(ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + qt_win_clean_up_OFNA(&ofn); + }); +#if defined(Q_OS_WINCE) + int semIndex = result.indexOf(QLatin1Char(';')); + if (semIndex >= 0) + result = result.left(semIndex); +#endif + + QApplicationPrivate::leaveModal(&modal_widget); + + qt_win_eatMouseMove(); + + if (result.isEmpty()) + return result; + + fi = result; + *initialDirectory = fi.path(); + if (selectedFilter) + *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx); + return fi.absoluteFilePath(); +} + +QStringList qt_win_get_open_file_names(const QFileDialogArgs &args, + QString *initialDirectory, + QString *selectedFilter) +{ + QStringList result; + QFileInfo fi; + QDir dir; + QString isel; + + if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:")) + initialDirectory->remove(0, 5); + fi = QFileInfo(*initialDirectory); + + if (initialDirectory && !fi.isDir()) { + *initialDirectory = fi.absolutePath(); + isel = fi.fileName(); + } + + if (!fi.exists()) + *initialDirectory = QDir::homePath(); + + DWORD selFilIdx = 0; + + int idx = 0; + if (selectedFilter) { + QStringList filterLst = qt_win_make_filters_list(args.filter); + idx = filterLst.indexOf(*selectedFilter); + } + + QDialog modal_widget; + modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); + modal_widget.setParent(args.parent, Qt::Window); + QApplicationPrivate::enterModal(&modal_widget); + QT_WA({ + OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection, + args.directory, args.caption, + qt_win_filter(args.filter), + QFileDialog::ExistingFiles, + args.options); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetOpenFileName(ofn)) { + QString fileOrDir = QString::fromUtf16((ushort*)ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + int offset = fileOrDir.length() + 1; + if (ofn->lpstrFile[offset] == 0) { + // Only one file selected; has full path + fi.setFile(fileOrDir); + QString res = fi.absoluteFilePath(); + if (!res.isEmpty()) + result.append(res); + } + else { + // Several files selected; first string is path + dir.setPath(fileOrDir); + QString f; + while(!(f = QString::fromUtf16((ushort*)ofn->lpstrFile+offset)).isEmpty()) { + fi.setFile(dir, f); + QString res = fi.absoluteFilePath(); + if (!res.isEmpty()) + result.append(res); + offset += f.length() + 1; + } + } + } + qt_win_clean_up_OFN(&ofn); + } , { + OPENFILENAMEA* ofn = qt_win_make_OFNA(args.parent, args.selection, + args.directory, args.caption, + qt_win_filter(args.filter), + QFileDialog::ExistingFiles, + args.options); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetOpenFileNameA(ofn)) { + QByteArray fileOrDir(ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + int offset = fileOrDir.length() + 1; + if (ofn->lpstrFile[offset] == '\0') { + // Only one file selected; has full path + fi.setFile(QString::fromLocal8Bit(fileOrDir)); + QString res = fi.absoluteFilePath(); + if (!res.isEmpty()) + result.append(res); + } + else { + // Several files selected; first string is path + dir.setPath(QString::fromLocal8Bit(fileOrDir)); + QByteArray f; + while (!(f = QByteArray(ofn->lpstrFile + offset)).isEmpty()) { + fi.setFile(dir, QString::fromLocal8Bit(f)); + QString res = fi.absoluteFilePath(); + if (!res.isEmpty()) + result.append(res); + offset += f.length() + 1; + } + } + qt_win_clean_up_OFNA(&ofn); + } + }); + QApplicationPrivate::leaveModal(&modal_widget); + + qt_win_eatMouseMove(); + + if (!result.isEmpty()) { + *initialDirectory = fi.path(); // only save the path if there is a result + if (selectedFilter) + *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx); + } + return result; +} + +// MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers) + +static int __stdcall winGetExistDirCallbackProc(HWND hwnd, + UINT uMsg, + LPARAM lParam, + LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED && lpData != 0) { + QString *initDir = (QString *)(lpData); + if (!initDir->isEmpty()) { + // ### Lars asks: is this correct for the A version???? + QT_WA({ + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initDir->utf16())); + } , { + SendMessageA(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initDir->utf16())); + }); + } + } else if (uMsg == BFFM_SELCHANGED) { + QT_WA({ + qt_win_resolve_libs(); + TCHAR path[MAX_PATH]; + ptrSHGetPathFromIDList(LPITEMIDLIST(lParam), path); + QString tmpStr = QString::fromUtf16((ushort*)path); + if (!tmpStr.isEmpty()) + SendMessage(hwnd, BFFM_ENABLEOK, 1, 1); + else + SendMessage(hwnd, BFFM_ENABLEOK, 0, 0); + SendMessage(hwnd, BFFM_SETSTATUSTEXT, 1, LPARAM(path)); + } , { + char path[MAX_PATH]; + SHGetPathFromIDListA(LPITEMIDLIST(lParam), path); + QString tmpStr = QString::fromLocal8Bit(path); + if (!tmpStr.isEmpty()) + SendMessageA(hwnd, BFFM_ENABLEOK, 1, 1); + else + SendMessageA(hwnd, BFFM_ENABLEOK, 0, 0); + SendMessageA(hwnd, BFFM_SETSTATUSTEXT, 1, LPARAM(path)); + }); + } + return 0; +} + +#ifndef BIF_NEWDIALOGSTYLE +#define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize +#endif + + +QString qt_win_get_existing_directory(const QFileDialogArgs &args) +{ + QString currentDir = QDir::currentPath(); + QString result; + QWidget *parent = args.parent; + if (parent) + parent = parent->window(); + else + parent = qApp->activeWindow(); + if (parent) + parent->createWinId(); + + QDialog modal_widget; + modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); + modal_widget.setParent(parent, Qt::Window); + QApplicationPrivate::enterModal(&modal_widget); +#if !defined(Q_OS_WINCE) + QT_WA({ + qt_win_resolve_libs(); + QString initDir = QDir::toNativeSeparators(args.directory); + TCHAR path[MAX_PATH]; + TCHAR initPath[MAX_PATH]; + initPath[0] = 0; + path[0] = 0; + tTitle = args.caption; + BROWSEINFO bi; + Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); + bi.hwndOwner = (parent ? parent->winId() : 0); + bi.pidlRoot = NULL; + //### This does not seem to be respected? - the dialog always displays "Browse for folder" + bi.lpszTitle = (TCHAR*)tTitle.utf16(); + bi.pszDisplayName = initPath; + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE; + bi.lpfn = winGetExistDirCallbackProc; + bi.lParam = LPARAM(&initDir); + if (ptrSHBrowseForFolder) { + LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder(&bi); + if (pItemIDList && ptrSHGetPathFromIDList) { + ptrSHGetPathFromIDList(pItemIDList, path); + IMalloc *pMalloc; + if (SHGetMalloc(&pMalloc) != NOERROR) + result = QString(); + else { + pMalloc->Free(pItemIDList); + pMalloc->Release(); + result = QString::fromUtf16((ushort*)path); + } + } else + result = QString(); + } + tTitle = QString(); + } , { + QString initDir = QDir::toNativeSeparators(args.directory); + char path[MAX_PATH]; + char initPath[MAX_PATH]; + QByteArray ctitle = args.caption.toLocal8Bit(); + initPath[0]=0; + path[0]=0; + BROWSEINFOA bi; + Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); + bi.hwndOwner = (parent ? parent->winId() : 0); + bi.pidlRoot = NULL; + bi.lpszTitle = ctitle; + bi.pszDisplayName = initPath; + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE; + bi.lpfn = winGetExistDirCallbackProc; + bi.lParam = LPARAM(&initDir); + LPITEMIDLIST pItemIDList = SHBrowseForFolderA(&bi); + if (pItemIDList) { + SHGetPathFromIDListA(pItemIDList, path); + IMalloc *pMalloc; + if (SHGetMalloc(&pMalloc) != NOERROR) + result = QString(); + else { + pMalloc->Free(pItemIDList); + pMalloc->Release(); + result = QString::fromLocal8Bit(path); + } + } else + result = QString(); + }); +#else + qt_win_resolve_libs(); + QString initDir = QDir::toNativeSeparators(args.directory); + TCHAR path[MAX_PATH]; + TCHAR initPath[MAX_PATH]; + memset(initPath, 0 , MAX_PATH*sizeof(TCHAR)); + memset(path, 0, MAX_PATH*sizeof(TCHAR)); + tTitle = args.caption; + qt_BROWSEINFO bi; + Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); + bi.hwndOwner = (parent ? parent->winId() : 0); + bi.pidlRoot = NULL; + bi.lpszTitle = (TCHAR*)tTitle.utf16(); + bi.pszDisplayName = initPath; + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE; + bi.lpfn = winGetExistDirCallbackProc; + bi.lParam = LPARAM(&initDir); + if (ptrSHBrowseForFolder) { + LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder((BROWSEINFO*)&bi); + if (pItemIDList && ptrSHGetPathFromIDList) { + ptrSHGetPathFromIDList(pItemIDList, path); + IMalloc *pMalloc; + if (SHGetMalloc(&pMalloc) != NOERROR) + result = QString(); + else { + pMalloc->Free(pItemIDList); + pMalloc->Release(); + result = QString::fromUtf16((ushort*)path); + } + } else + result = QString(); + } + tTitle = QString(); + +#endif + QApplicationPrivate::leaveModal(&modal_widget); + + qt_win_eatMouseMove(); + + // Due to a bug on Windows Me, we need to reset the current + // directory + if ((QSysInfo::WindowsVersion == QSysInfo::WV_98 || QSysInfo::WindowsVersion == QSysInfo::WV_Me) + && QDir::currentPath() != currentDir) + QDir::setCurrent(currentDir); + + if (!result.isEmpty()) + result.replace(QLatin1String("\\"), QLatin1String("/")); + return result; +} + + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/dialogs/qfiledialog_wince.ui b/src/gui/dialogs/qfiledialog_wince.ui new file mode 100644 index 0000000000..8aa9e1b30f --- /dev/null +++ b/src/gui/dialogs/qfiledialog_wince.ui @@ -0,0 +1,342 @@ +<ui version="4.0" > + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +*********************************************************************</comment> + <class>QFileDialog</class> + <widget class="QDialog" name="QFileDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>240</width> + <height>320</height> + </rect> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QFileDialogComboBox" name="lookInCombo" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Expanding" > + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QToolButton" name="backButton" > + <property name="toolTip" > + <string>Back</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="forwardButton" > + <property name="toolTip" > + <string>Forward</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="toParentButton" > + <property name="toolTip" > + <string>Parent Directory</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="newFolderButton" > + <property name="toolTip" > + <string>Create New Folder</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="listModeButton" > + <property name="toolTip" > + <string>List View</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="detailModeButton" > + <property name="toolTip" > + <string>Detail View</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSplitter" name="splitter" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <widget class="QSidebar" name="sidebar" /> + <widget class="QFrame" name="frame" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QStackedWidget" name="stackedWidget" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="page" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>108</width> + <height>164</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QFileDialogListView" name="listView" /> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_2" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>100</width> + <height>30</height> + </rect> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QFileDialogTreeView" name="treeView" /> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QFileDialogLineEdit" name="fileNameEdit" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Expanding" > + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item rowspan="2" row="0" column="1" > + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QComboBox" name="fileTypeCombo" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="fileNameLabel" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="maximumSize" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="fileTypeLabel" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text" > + <string>Files of type:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lookInLabel" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text" > + <string>Look in:</string> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QFileDialogTreeView</class> + <extends>QTreeView</extends> + <header>qfiledialog_p.h</header> + </customwidget> + <customwidget> + <class>QFileDialogListView</class> + <extends>QListView</extends> + <header>qfiledialog_p.h</header> + </customwidget> + <customwidget> + <class>QSidebar</class> + <extends>QListWidget</extends> + <header>qsidebar_p.h</header> + </customwidget> + <customwidget> + <class>QFileDialogLineEdit</class> + <extends>QLineEdit</extends> + <header>qfiledialog_p.h</header> + </customwidget> + <customwidget> + <class>QFileDialogComboBox</class> + <extends>QComboBox</extends> + <header>qfiledialog_p.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>lookInCombo</tabstop> + <tabstop>backButton</tabstop> + <tabstop>forwardButton</tabstop> + <tabstop>toParentButton</tabstop> + <tabstop>newFolderButton</tabstop> + <tabstop>listModeButton</tabstop> + <tabstop>detailModeButton</tabstop> + <tabstop>sidebar</tabstop> + <tabstop>listView</tabstop> + <tabstop>fileNameEdit</tabstop> + <tabstop>fileTypeCombo</tabstop> + <tabstop>buttonBox</tabstop> + <tabstop>treeView</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/gui/dialogs/qfileinfogatherer.cpp b/src/gui/dialogs/qfileinfogatherer.cpp new file mode 100644 index 0000000000..3fe64ff7a2 --- /dev/null +++ b/src/gui/dialogs/qfileinfogatherer.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfileinfogatherer_p.h" +#include <qdebug.h> +#include <qfsfileengine.h> +#include <qdiriterator.h> +#ifndef Q_OS_WIN +#include <unistd.h> +#include <sys/types.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_FILESYSTEMMODEL + +bool QFileInfoGatherer::fetchedRoot = false; + +/*! + Creates thread +*/ +QFileInfoGatherer::QFileInfoGatherer(QObject *parent) + : QThread(parent), abort(false), +#ifndef QT_NO_FILESYSTEMWATCHER + watcher(0), +#endif + m_resolveSymlinks(false), m_iconProvider(&defaultProvider) +{ +#ifndef Q_OS_WIN + userId = getuid(); + groupId = getgid(); +#else + m_resolveSymlinks = true; +#endif +#ifndef QT_NO_FILESYSTEMWATCHER + watcher = new QFileSystemWatcher(this); + connect(watcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(list(const QString &))); + connect(watcher, SIGNAL(fileChanged(const QString &)), this, SLOT(updateFile(const QString &))); +#endif + start(LowPriority); +} + +/*! + Destroys thread +*/ +QFileInfoGatherer::~QFileInfoGatherer() +{ + mutex.lock(); + abort = true; + condition.wakeOne(); + mutex.unlock(); + wait(); +} + +void QFileInfoGatherer::setResolveSymlinks(bool enable) +{ + Q_UNUSED(enable); +#ifdef Q_OS_WIN + mutex.lock(); + m_resolveSymlinks = enable; + mutex.unlock(); +#endif +} + +bool QFileInfoGatherer::resolveSymlinks() const +{ + return m_resolveSymlinks; +} + +void QFileInfoGatherer::setIconProvider(QFileIconProvider *provider) +{ + mutex.lock(); + m_iconProvider = provider; + mutex.unlock(); +} + +QFileIconProvider *QFileInfoGatherer::iconProvider() const +{ + return m_iconProvider; +} + +/*! + Fetch extended information for all \a files in \a path + + \sa updateFile(), update(), resolvedName() +*/ +void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStringList &files) +{ + mutex.lock(); + // See if we already have this dir/file in our que + int loc = this->path.lastIndexOf(path); + while (loc > 0) { + if (this->files.at(loc) == files) { + mutex.unlock(); + return; + } + loc = this->path.lastIndexOf(path, loc - 1); + } + this->path.push(path); + this->files.push(files); + condition.wakeAll(); + mutex.unlock(); +} + +/*! + Fetch extended information for all \a filePath + + \sa fetchExtendedInformation() +*/ +void QFileInfoGatherer::updateFile(const QString &filePath) +{ + QString dir = filePath.mid(0, filePath.lastIndexOf(QDir::separator())); + QString fileName = filePath.mid(dir.length() + 1); + fetchExtendedInformation(dir, QStringList(fileName)); +} + +/* + List all files in \a directoryPath + + \sa listed() +*/ +void QFileInfoGatherer::clear() +{ +#ifndef QT_NO_FILESYSTEMWATCHER + mutex.lock(); + watcher->removePaths(watcher->files()); + watcher->removePaths(watcher->directories()); + mutex.unlock(); +#endif +} + +/* + List all files in \a directoryPath + + \sa listed() +*/ +void QFileInfoGatherer::list(const QString &directoryPath) +{ + fetchExtendedInformation(directoryPath, QStringList()); +} + +/* + Until aborted wait to fetch a directory or files +*/ +void QFileInfoGatherer::run() +{ + forever { + bool updateFiles = false; + mutex.lock(); + if (abort) { + mutex.unlock(); + return; + } + if (this->path.isEmpty()) + condition.wait(&mutex); + QString path; + QStringList list; + if (!this->path.isEmpty()) { + path = this->path.first(); + list = this->files.first(); + this->path.pop_front(); + this->files.pop_front(); + updateFiles = true; + } + mutex.unlock(); + if (updateFiles) getFileInfos(path, list); + } +} + +/* + QFileInfo::permissions is different depending upon your platform. + + "normalize this" so they can mean the same to us. +*/ +QFile::Permissions QFileInfoGatherer::translatePermissions(const QFileInfo &fileInfo) const { + QFile::Permissions permissions = fileInfo.permissions(); +#ifdef Q_OS_WIN + return permissions; +#else + QFile::Permissions p = permissions; + p &= ~(QFile::ReadUser|QFile::WriteUser|QFile::ExeUser); + if ( permissions & QFile::ReadOther + || (fileInfo.ownerId() == userId && permissions & QFile::ReadOwner) + || (fileInfo.groupId() == groupId && permissions & QFile::ReadGroup)) + p |= QFile::ReadUser; + + if ( permissions & QFile::WriteOther + || (fileInfo.ownerId() == userId && permissions & QFile::WriteOwner) + || (fileInfo.groupId() == groupId && permissions & QFile::WriteGroup)) + p |= QFile::WriteUser; + + if ( permissions & QFile::ExeOther + || (fileInfo.ownerId() == userId && permissions & QFile::ExeOwner) + || (fileInfo.groupId() == groupId && permissions & QFile::ExeGroup)) + p |= QFile::ExeUser; + return p; +#endif +} + +QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const +{ + QExtendedInformation info(fileInfo); + info.icon = m_iconProvider->icon(fileInfo); + info.setPermissions(translatePermissions(fileInfo)); + info.displayType = m_iconProvider->type(fileInfo); +#ifndef QT_NO_FILESYSTEMWATCHER + // ### Not ready to listen all modifications + #if 0 + // Enable the next two commented out lines to get updates when the file sizes change... + if (!fileInfo.exists() && !fileInfo.isSymLink()) { + info.size = -1; + //watcher->removePath(fileInfo.absoluteFilePath()); + } else { + if (!fileInfo.absoluteFilePath().isEmpty() && fileInfo.exists() && fileInfo.isReadable() + && !watcher->files().contains(fileInfo.absoluteFilePath())) { + //watcher->addPath(fileInfo.absoluteFilePath()); + } + } + #endif +#endif + + if (fileInfo.isSymLink() && m_resolveSymlinks) { + QFileInfo resolvedInfo(fileInfo.symLinkTarget()); + resolvedInfo = resolvedInfo.canonicalFilePath(); + if (resolvedInfo.exists()) { + emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName()); + } + } + return info; +} + +QString QFileInfoGatherer::translateDriveName(const QFileInfo &drive) const +{ + QString driveName = drive.absoluteFilePath(); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + if (driveName.startsWith(QLatin1Char('/'))) // UNC host + return drive.fileName(); + if (driveName.endsWith(QLatin1Char('/'))) + driveName.chop(1); +#endif + return driveName; +} + +/* + Get specific file info's, batch the files so update when we have 100 + items and every 200ms after that + */ +void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files) +{ +#ifndef QT_NO_FILESYSTEMWATCHER + //### We test here if the path still exist before adding it in the watcher + //### because sometime the file is deleted just before enter here so QStringList files is not up to date + //### It is not a proper fix, perhaps in 4.6 we should have a better way to avoid that + //### to ensure the gatherer have fresh information + QFileInfo info(path); + if (files.isEmpty() + && !watcher->directories().contains(path) + && !path.isEmpty() + && info.exists() + && !path.startsWith(QLatin1String("//")) /*don't watch UNC path*/) { + watcher->addPath(path); + } +#endif + + // List drives + if (path.isEmpty()) { +#if defined Q_AUTOTEST_EXPORT + fetchedRoot = true; +#endif + QFileInfoList infoList; + if (files.isEmpty()) { + infoList = QDir::drives(); + } else { + for (int i = 0; i < files.count(); ++i) + infoList << QFileInfo(files.at(i)); + } + for (int i = infoList.count() - 1; i >= 0; --i) { + QString driveName = translateDriveName(infoList.at(i)); + QList<QPair<QString,QFileInfo> > updatedFiles; + updatedFiles.append(QPair<QString,QFileInfo>(driveName, infoList.at(i))); + emit updates(path, updatedFiles); + } + return; + } + + QTime base = QTime::currentTime(); + QFileInfo fileInfo; + bool firstTime = true; + QList<QPair<QString, QFileInfo> > updatedFiles; + QStringList filesToCheck = files; + + QString itPath = QDir::fromNativeSeparators(files.isEmpty() ? path : QLatin1String("")); + QDirIterator dirIt(itPath, QDir::AllEntries | QDir::System | QDir::Hidden); + QStringList allFiles; + while(!abort && dirIt.hasNext()) { + dirIt.next(); + fileInfo = dirIt.fileInfo(); + allFiles.append(fileInfo.fileName()); + fetch(fileInfo, base, firstTime, updatedFiles, path); + } + if (!allFiles.isEmpty()) + emit newListOfFiles(path, allFiles); + + QStringList::const_iterator filesIt = filesToCheck.constBegin(); + while(!abort && filesIt != filesToCheck.constEnd()) { + fileInfo.setFile(path + QDir::separator() + *filesIt); + ++filesIt; + fetch(fileInfo, base, firstTime, updatedFiles, path); + } + if (!updatedFiles.isEmpty()) + emit updates(path, updatedFiles); +} + +void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QTime &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path) { + updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo)); + QTime current = QTime::currentTime(); + if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) { + emit updates(path, updatedFiles); + updatedFiles.clear(); + base = current; + firstTime = false; + } +} + +#endif // QT_NO_FILESYSTEMMODEL + +QT_END_NAMESPACE diff --git a/src/gui/dialogs/qfileinfogatherer_p.h b/src/gui/dialogs/qfileinfogatherer_p.h new file mode 100644 index 0000000000..eac0d462e2 --- /dev/null +++ b/src/gui/dialogs/qfileinfogatherer_p.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILEINFOGATHERER_H +#define QFILEINFOGATHERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qthread.h> +#include <qmutex.h> +#include <qwaitcondition.h> +#include <qfilesystemwatcher.h> +#include <qfileiconprovider.h> +#include <qfsfileengine.h> +#include <qpair.h> +#include <qdatetime.h> +#include <qstack.h> +#include <qdir.h> + +QT_BEGIN_NAMESPACE + +class QExtendedInformation { +public: + enum Type { Dir, File, System }; + + QExtendedInformation() {} + QExtendedInformation(const QFileInfo &info) : mFileInfo(info) {} + + inline bool isDir() { return type() == Dir; } + inline bool isFile() { return type() == File; } + inline bool isSystem() { return type() == System; } + + bool operator ==(const QExtendedInformation &fileInfo) const { + return mFileInfo == fileInfo.mFileInfo + && displayType == fileInfo.displayType + && permissions() == fileInfo.permissions(); + } + + bool isCaseSensitive() const { + QFSFileEngine fe(mFileInfo.absoluteFilePath()); + return fe.caseSensitive(); + } + QFile::Permissions permissions() const { + return mPermissions; + } + + void setPermissions (QFile::Permissions permissions) { + mPermissions = permissions; + } + + Type type() const { + if (mFileInfo.isDir()) { + return QExtendedInformation::Dir; + } + if (mFileInfo.isFile()) { + return QExtendedInformation::File; + } + if (!mFileInfo.exists() && mFileInfo.isSymLink()) { + return QExtendedInformation::System; + } + return QExtendedInformation::System; + } + + bool isSymLink() const { + return mFileInfo.isSymLink(); + } + + bool isHidden() const { + return mFileInfo.isHidden(); + } + + QFileInfo fileInfo() const { + return mFileInfo; + } + + QDateTime lastModified() const { + return mFileInfo.lastModified(); + } + + qint64 size() const { + qint64 size = -1; + if (type() == QExtendedInformation::Dir) + size = 0; + if (type() == QExtendedInformation::File) + size = mFileInfo.size(); + if (!mFileInfo.exists() && !mFileInfo.isSymLink()) + size = -1; + return size; + } + + QString displayType; + QIcon icon; + +private : + QFileInfo mFileInfo; + QFile::Permissions mPermissions; +}; + +class QFileIconProvider; + +#ifndef QT_NO_FILESYSTEMMODEL + +class Q_AUTOTEST_EXPORT QFileInfoGatherer : public QThread +{ +Q_OBJECT + +Q_SIGNALS: + void updates(const QString &directory, const QList<QPair<QString, QFileInfo> > &updates); + void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const; + void nameResolved(const QString &fileName, const QString &resolvedName) const; + +public: + QFileInfoGatherer(QObject *parent = 0); + ~QFileInfoGatherer(); + + void clear(); + QExtendedInformation getInfo(const QFileInfo &info) const; + +public Q_SLOTS: + void list(const QString &directoryPath); + void fetchExtendedInformation(const QString &path, const QStringList &files); + void updateFile(const QString &path); + void setResolveSymlinks(bool enable); + bool resolveSymlinks() const; + void setIconProvider(QFileIconProvider *provider); + QFileIconProvider *iconProvider() const; + +protected: + void run(); + void getFileInfos(const QString &path, const QStringList &files); + +private: + void fetch(const QFileInfo &info, QTime &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path); + QString translateDriveName(const QFileInfo &drive) const; + QFile::Permissions translatePermissions(const QFileInfo &fileInfo) const; + + QMutex mutex; + QWaitCondition condition; + bool abort; + + QStack<QString> path; + QStack<QStringList> files; + +#ifndef QT_NO_FILESYSTEMWATCHER + QFileSystemWatcher *watcher; +#endif + bool m_resolveSymlinks; + QFileIconProvider *m_iconProvider; + QFileIconProvider defaultProvider; +#ifndef Q_OS_WIN + uint userId; + uint groupId; +#endif +public : + //for testing purpose + static bool fetchedRoot; +}; +#endif // QT_NO_FILESYSTEMMODEL + + +QT_END_NAMESPACE +#endif // QFILEINFOGATHERER_H + diff --git a/src/gui/dialogs/qfilesystemmodel.cpp b/src/gui/dialogs/qfilesystemmodel.cpp new file mode 100644 index 0000000000..51d33146f1 --- /dev/null +++ b/src/gui/dialogs/qfilesystemmodel.cpp @@ -0,0 +1,1911 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilesystemmodel_p.h" +#include "qfilesystemmodel.h" +#include <qlocale.h> +#include <qmime.h> +#include <qurl.h> +#include <qdebug.h> +#include <qmessagebox.h> +#include <qapplication.h> + +#ifdef Q_OS_WIN +#include <windows.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_FILESYSTEMMODEL + +/*! + \enum QFileSystemModel::Roles + \value FileIconRole + \value FilePathRole + \value FileNameRole + \value FilePermissions +*/ + +/*! + \class QFileSystemModel + \since 4.4 + + \brief The QFileSystemModel class provides a data model for the local filesystem. + + \ingroup model-view + + This class provides access to the local filesystem, providing functions + for renaming and removing files and directories, and for creating new + directories. In the simplest case, it can be used with a suitable display + widget as part of a browser or filter. + + QFileSystemModel will not fetch any files or directories until setRootPath + is called. This will prevent any unnecessary querying on the file system + until that point such as listing the drives on Windows. + + Unlike the QDirModel, QFileSystemModel uses a separate thread to populate + itself so it will not cause the main thread to hang as the file system + is being queried. Calls to rowCount() will return 0 until the model + populates a directory. + + QFileSystemModel keeps a cache with file information. The cache is + automatically kept up to date using the QFileSystemWatcher. + + QFileSystemModel can be accessed using the standard interface provided by + QAbstractItemModel, but it also provides some convenience functions that are + specific to a directory model. + The fileInfo(), isDir(), name(), and path() functions provide information + about the underlying files and directories related to items in the model. + Directories can be created and removed using mkdir(), rmdir(). + + \note QFileSystemModel requires an instance of a GUI application. + + \sa {Model Classes} +*/ + +/*! + \fn bool QFileSystemModel::rmdir(const QModelIndex &index) const + + Removes the directory corresponding to the model item \a index in the + file system model and \bold{deletes the corresponding directory from the + file system}, returning true if successful. If the directory cannot be + removed, false is returned. + + \warning This function deletes directories from the file system; it does + \bold{not} move them to a location where they can be recovered. + + \sa remove() +*/ + +/*! + \fn QIcon QFileSystemModel::fileName(const QModelIndex &index) const + + Returns the file name for the item stored in the model under the given + \a index. +*/ + +/*! + \fn QIcon QFileSystemModel::fileIcon(const QModelIndex &index) const + + Returns the icon for the item stored in the model under the given + \a index. +*/ + +/*! + \fn QFileInfo QFileSystemModel::fileInfo(const QModelIndex &index) const + + Returns the QFileInfo for the item stored in the model under the given + \a index. +*/ + +/*! + \fn void QFileSystemModel::rootPathChanged(const QString &newPath); + + This signal is emitted whenever the root path has been changed to a \a newPath. +*/ + +/*! + \fn void QFileSystemModel::fileRenamed(const QString &path, const QString &oldName, const QString &newName) + + This signal is emitted whenever a file with the \a oldName is successfully + renamed to \a newName. The file is located in in the directory \a path. +*/ + +/*! + \fn bool QFileSystemModel::remove(const QModelIndex &index) const + + Removes the model item \a index from the file system model and \bold{deletes the + corresponding file from the file system}, returning true if successful. If the + item cannot be removed, false is returned. + + \warning This function deletes files from the file system; it does \bold{not} + move them to a location where they can be recovered. + + \sa rmdir() +*/ + +bool QFileSystemModel::remove(const QModelIndex &aindex) const +{ + //### TODO optim + QString path = filePath(aindex); + QDirIterator it(path, + QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories); + QStringList children; + while (it.hasNext()) + children.prepend(it.next()); + children.append(path); + + bool error = false; + for (int i = 0; i < children.count(); ++i) { + QFileInfo info(children.at(i)); + QModelIndex modelIndex = index(children.at(i)); + if (info.isDir()) { + QDir dir; + if (children.at(i) != path) + error |= remove(modelIndex); + error |= rmdir(modelIndex); + } else { + error |= QFile::remove(filePath(modelIndex)); + } + } + return error; +} + +/*! + Constructs a file system model with the given \a parent. +*/ +QFileSystemModel::QFileSystemModel(QObject *parent) + : QAbstractItemModel(*new QFileSystemModelPrivate, parent) +{ + Q_D(QFileSystemModel); + d->init(); +} + +/*! + \internal +*/ +QFileSystemModel::QFileSystemModel(QFileSystemModelPrivate &dd, QObject *parent) + : QAbstractItemModel(dd, parent) +{ + Q_D(QFileSystemModel); + d->init(); +} + +/*! + Destroys this file system model. +*/ +QFileSystemModel::~QFileSystemModel() +{ +} + +/*! + \reimp +*/ +QModelIndex QFileSystemModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_D(const QFileSystemModel); + if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent)) + return QModelIndex(); + + // get the parent node + QFileSystemModelPrivate::QFileSystemNode *parentNode = (d->indexValid(parent) ? d->node(parent) : + const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&d->root)); + Q_ASSERT(parentNode); + + // now get the internal pointer for the index + QString childName = parentNode->visibleChildren[d->translateVisibleLocation(parentNode, row)]; + const QFileSystemModelPrivate::QFileSystemNode *indexNode = parentNode->children.value(childName); + Q_ASSERT(indexNode); + + return createIndex(row, column, const_cast<QFileSystemModelPrivate::QFileSystemNode*>(indexNode)); +} + +/*! + \overload + + Returns the model item index for the given \a path and \a column. +*/ +QModelIndex QFileSystemModel::index(const QString &path, int column) const +{ + Q_D(const QFileSystemModel); + QFileSystemModelPrivate::QFileSystemNode *node = d->node(path, false); + QModelIndex idx = d->index(node); + if (idx.column() != column) + idx = idx.sibling(idx.row(), column); + return idx; +} + +/*! + \internal + + Return the QFileSystemNode that goes to index. + */ +QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QModelIndex &index) const +{ + if (!index.isValid()) + return const_cast<QFileSystemNode*>(&root); + QFileSystemModelPrivate::QFileSystemNode *indexNode = static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer()); + Q_ASSERT(indexNode); + return indexNode; +} + +#ifdef Q_OS_WIN +static QString qt_GetLongPathName(const QString &strShortPath) +{ + QString longPath; + int i = 0; + if (strShortPath == QLatin1String(".") + || (strShortPath.startsWith(QLatin1String("//"))) + || (strShortPath.startsWith(QLatin1String("\\\\")))) // unc + return strShortPath; + QString::const_iterator it = strShortPath.constBegin(); + QString::const_iterator constEnd = strShortPath.constEnd(); + do { + bool isSep = (*it == QLatin1Char('\\') || *it == QLatin1Char('/')); + if (isSep || it == constEnd) { + QString section = (it == constEnd ? strShortPath : strShortPath.left(i)); + // FindFirstFile does not handle volumes ("C:"), so we have to catch that ourselves. + if (section.endsWith(QLatin1Char(':'))) { + longPath.append(section.toUpper()); + } else { + HANDLE h; +#ifndef Q_OS_WINCE + //We add the extend length prefix to handle long path + QString longSection = QLatin1String("\\\\?\\")+QDir::toNativeSeparators(section); +#else + QString longSection = QDir::toNativeSeparators(section); +#endif + QT_WA({ + WIN32_FIND_DATAW findData; + h = ::FindFirstFileW((wchar_t *)longSection.utf16(), &findData); + if (h != INVALID_HANDLE_VALUE) + longPath.append(QString::fromUtf16((ushort*)findData.cFileName)); + } , { + WIN32_FIND_DATAA findData; + h = ::FindFirstFileA(section.toLocal8Bit(), &findData); + if (h != INVALID_HANDLE_VALUE) + longPath.append(QString::fromLocal8Bit(findData.cFileName)); + }); + if (h == INVALID_HANDLE_VALUE) { + longPath.append(section); + break; + } else { + ::FindClose(h); + } + } + if (it != constEnd) + longPath.append(*it); + else + break; + } + ++it; + if (isSep && it == constEnd) // break out if the last character is a separator + break; + ++i; + } while (true); + return longPath; +} +#endif + +/*! + \internal + + Given a path return the matching QFileSystemNode or &root if invalid +*/ +QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QString &path, bool fetch) const +{ + Q_Q(const QFileSystemModel); + Q_UNUSED(q); + if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1String(":"))) + return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); + + // Construct the nodes up to the new root path if they need to be built + QString absolutePath; +#ifdef Q_OS_WIN + QString longPath = qt_GetLongPathName(path); +#else + QString longPath = path; +#endif + if (longPath == rootDir.path()) + absolutePath = rootDir.absolutePath(); + else + absolutePath = QDir(longPath).absolutePath(); + + // ### TODO can we use bool QAbstractFileEngine::caseSensitive() const? + QStringList pathElements = absolutePath.split(QLatin1Char('/'), QString::SkipEmptyParts); + if ((pathElements.isEmpty()) +#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE) + && QDir::fromNativeSeparators(longPath) != QLatin1String("/") +#endif + ) + return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); + QModelIndex index = QModelIndex(); // start with "My Computer" +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + if (absolutePath.startsWith(QLatin1String("//"))) { // UNC path + QString host = QLatin1String("\\\\") + pathElements.first(); + if (absolutePath == QDir::fromNativeSeparators(host)) + absolutePath.append(QLatin1Char('/')); + if (longPath.endsWith(QLatin1Char('/')) && !absolutePath.endsWith(QLatin1Char('/'))) + absolutePath.append(QLatin1Char('/')); + int r = 0; + QFileSystemModelPrivate::QFileSystemNode *rootNode = const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); + if (!root.children.contains(host.toLower())) { + if (pathElements.count() == 1 && !absolutePath.endsWith(QLatin1Char('/'))) + return rootNode; + QFileInfo info(host); + if (!info.exists()) + return rootNode; + QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); + p->addNode(rootNode, host); + p->addVisibleFiles(rootNode, QStringList(host)); + } + r = rootNode->visibleLocation(host); + r = translateVisibleLocation(rootNode, r); + index = q->index(r, 0, QModelIndex()); + pathElements.pop_front(); + } else { + if (!pathElements.at(0).contains(QLatin1String(":"))) + pathElements.prepend(QDir(longPath).rootPath()); + if (pathElements.at(0).endsWith(QLatin1Char('/'))) + pathElements[0].chop(1); + } +#else + // add the "/" item, since it is a valid path element on Unix + if (absolutePath[0] == QLatin1Char('/')) + pathElements.prepend(QLatin1String("/")); +#endif + + QFileSystemModelPrivate::QFileSystemNode *parent = node(index); + for (int i = 0; i < pathElements.count(); ++i) { + QString element = pathElements.at(i); +#ifdef Q_OS_WIN + // On Windows, "filename......." and "filename" are equivalent Task #133928 + while (element.endsWith(QLatin1Char('.'))) + element.chop(1); +#endif + bool alreadyExisted = parent->children.contains(element); + + // we couldn't find the path element, we create a new node since we + // _know_ that the path is valid + if (alreadyExisted) { + if ((parent->children.count() == 0) + || (parent->caseSensitive() + && parent->children.value(element)->fileName != element) + || (!parent->caseSensitive() + && parent->children.value(element)->fileName.toLower() != element.toLower())) + alreadyExisted = false; + } + + QFileSystemModelPrivate::QFileSystemNode *node; + if (!alreadyExisted) { + // Someone might call ::index("file://cookie/monster/doesn't/like/veggies"), + // a path that doesn't exists, I.E. don't blindly create directories. + QFileInfo info(absolutePath); + if (!info.exists()) + return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); + QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); + node = p->addNode(parent, element); +#ifndef QT_NO_FILESYSTEMWATCHER + node->populate(fileInfoGatherer.getInfo(info)); +#endif + } else { + node = parent->children.value(element); + } + + Q_ASSERT(node); + if (!node->isVisible) { + // It has been filtered out + if (alreadyExisted && node->hasInformation() && !fetch) + return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); + + QFileSystemModelPrivate *p = const_cast<QFileSystemModelPrivate*>(this); + p->addVisibleFiles(parent, QStringList(element)); + if (!p->bypassFilters.contains(node)) + p->bypassFilters[node] = 1; + QString dir = q->filePath(this->index(parent)); + if (!node->hasInformation() && fetch) { + Fetching f; + f.dir = dir; + f.file = element; + f.node = node; + p->toFetch.append(f); + p->fetchingTimer.start(0, const_cast<QFileSystemModel*>(q)); + } + } + parent = node; + } + + return parent; +} + +/*! + \reimp +*/ +void QFileSystemModel::timerEvent(QTimerEvent *event) +{ + Q_D(QFileSystemModel); + if (event->timerId() == d->fetchingTimer.timerId()) { + d->fetchingTimer.stop(); +#ifndef QT_NO_FILESYSTEMWATCHER + for (int i = 0; i < d->toFetch.count(); ++i) { + const QFileSystemModelPrivate::QFileSystemNode *node = d->toFetch.at(i).node; + if (!node->hasInformation()) { + d->fileInfoGatherer.fetchExtendedInformation(d->toFetch.at(i).dir, + QStringList(d->toFetch.at(i).file)); + } else { + // qDebug() << "yah!, you saved a little gerbil soul"; + } + } +#endif + d->toFetch.clear(); + } +} + +/*! + Returns true if the model item \a index represents a directory; + otherwise returns false. +*/ +bool QFileSystemModel::isDir(const QModelIndex &index) const +{ + // This function is for public usage only because it could create a file info + Q_D(const QFileSystemModel); + if (!index.isValid()) + return true; + QFileSystemModelPrivate::QFileSystemNode *n = d->node(index); + if (n->hasInformation()) + return n->isDir(); + return fileInfo(index).isDir(); +} + +/*! + Returns the size in bytes of \a index. If the file does not exist, 0 is returned. + */ +qint64 QFileSystemModel::size(const QModelIndex &index) const +{ + Q_D(const QFileSystemModel); + if (!index.isValid()) + return 0; + return d->node(index)->size(); +} + +/*! + Returns the type of file \a index such as "Directory" or "JPEG file". + */ +QString QFileSystemModel::type(const QModelIndex &index) const +{ + Q_D(const QFileSystemModel); + if (!index.isValid()) + return QString(); + return d->node(index)->type(); +} + +/*! + Returns the date and time when \a index was last modified. + */ +QDateTime QFileSystemModel::lastModified(const QModelIndex &index) const +{ + Q_D(const QFileSystemModel); + if (!index.isValid()) + return QDateTime(); + return d->node(index)->lastModified(); +} + +/*! + \reimp +*/ +QModelIndex QFileSystemModel::parent(const QModelIndex &index) const +{ + Q_D(const QFileSystemModel); + if (!d->indexValid(index)) + return QModelIndex(); + + QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index); + Q_ASSERT(indexNode != 0); + QFileSystemModelPrivate::QFileSystemNode *parentNode = (indexNode ? indexNode->parent : 0); + if (parentNode == 0 || parentNode == &d->root) + return QModelIndex(); + + // get the parent's row + QFileSystemModelPrivate::QFileSystemNode *grandParentNode = parentNode->parent; + Q_ASSERT(grandParentNode->children.contains(parentNode->fileName)); + int visualRow = d->translateVisibleLocation(grandParentNode, grandParentNode->visibleLocation(grandParentNode->children.value(parentNode->fileName)->fileName)); + if (visualRow == -1) + return QModelIndex(); + return createIndex(visualRow, 0, parentNode); +} + +/* + \internal + + return the index for node +*/ +QModelIndex QFileSystemModelPrivate::index(const QFileSystemModelPrivate::QFileSystemNode *node) const +{ + Q_Q(const QFileSystemModel); + QFileSystemModelPrivate::QFileSystemNode *parentNode = (node ? node->parent : 0); + if (node == &root || !parentNode) + return QModelIndex(); + + // get the parent's row + Q_ASSERT(node); + if (!node->isVisible) + return QModelIndex(); + + int visualRow = translateVisibleLocation(parentNode, parentNode->visibleLocation(node->fileName)); + return q->createIndex(visualRow, 0, const_cast<QFileSystemNode*>(node)); +} + +/*! + \reimp +*/ +bool QFileSystemModel::hasChildren(const QModelIndex &parent) const +{ + Q_D(const QFileSystemModel); + if (parent.column() > 0) + return false; + + if (!parent.isValid()) // drives + return true; + + const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); + Q_ASSERT(indexNode); + return (indexNode->isDir()); +} + +/*! + \reimp + */ +bool QFileSystemModel::canFetchMore(const QModelIndex &parent) const +{ + Q_D(const QFileSystemModel); + const QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); + return (!indexNode->populatedChildren); +} + +/*! + \reimp + */ +void QFileSystemModel::fetchMore(const QModelIndex &parent) +{ + Q_D(QFileSystemModel); + if (!d->setRootPath) + return; + QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(parent); + if (indexNode->populatedChildren) + return; + indexNode->populatedChildren = true; + d->fileInfoGatherer.list(filePath(parent)); +} + +/*! + \reimp +*/ +int QFileSystemModel::rowCount(const QModelIndex &parent) const +{ + Q_D(const QFileSystemModel); + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + return d->root.visibleChildren.count(); + + const QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); + return parentNode->visibleChildren.count(); +} + +/*! + \reimp +*/ +int QFileSystemModel::columnCount(const QModelIndex &parent) const +{ + return (parent.column() > 0) ? 0 : 4; +} + +/*! + Returns the data stored under the given \a role for the item "My Computer". + + \sa Qt::ItemDataRole + */ +QVariant QFileSystemModel::myComputer(int role) const +{ + Q_D(const QFileSystemModel); + switch (role) { + case Qt::DisplayRole: + return d->myComputer(); + case Qt::DecorationRole: + return d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Computer); + } + return QVariant(); +} + +/*! + \reimp +*/ +QVariant QFileSystemModel::data(const QModelIndex &index, int role) const +{ + Q_D(const QFileSystemModel); + if (!index.isValid() || index.model() != this) + return QVariant(); + + switch (role) { + case Qt::EditRole: + case Qt::DisplayRole: + switch (index.column()) { + case 0: return d->name(index); + case 1: return d->size(index); + case 2: return d->type(index); + case 3: return d->time(index); + default: + qWarning("data: invalid display value column %d", index.column()); + break; + } + break; + case FilePathRole: + return filePath(index); + case FileNameRole: + return d->name(index); + case Qt::DecorationRole: + if (index.column() == 0) { + QIcon icon = d->icon(index); + if (icon.isNull()) { + if (d->node(index)->isDir()) + icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Folder); + else + icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::File); + } + return icon; + } + break; + case Qt::TextAlignmentRole: + if (index.column() == 1) + return Qt::AlignRight; + break; + case FilePermissions: + int p = permissions(index); + return p; + } + + return QVariant(); +} + +/*! + \internal +*/ +QString QFileSystemModelPrivate::size(const QModelIndex &index) const +{ + if (!index.isValid()) + return QString(); + const QFileSystemNode *n = node(index); + if (n->isDir()) { +#ifdef Q_OS_MAC + return QLatin1String("--"); +#else + return QLatin1String(""); +#endif + // Windows - "" + // OS X - "--" + // Konqueror - "4 KB" + // Nautilus - "9 items" (the number of children) + } + return size(n->size()); +} + +QString QFileSystemModelPrivate::size(qint64 bytes) +{ + // According to the Si standard KB is 1000 bytes, KiB is 1024 + // but on windows sizes are calculated by dividing by 1024 so we do what they do. + const qint64 kb = 1024; + const qint64 mb = 1024 * kb; + const qint64 gb = 1024 * mb; + const qint64 tb = 1024 * gb; + if (bytes >= tb) + return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3)); + if (bytes >= gb) + return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2)); + if (bytes >= mb) + return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1)); + if (bytes >= kb) + return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb)); + return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes)); +} + +/*! + \internal +*/ +QString QFileSystemModelPrivate::time(const QModelIndex &index) const +{ + if (!index.isValid()) + return QString(); +#ifndef QT_NO_DATESTRING + return node(index)->lastModified().toString(Qt::SystemLocaleDate); +#else + Q_UNUSED(index); + return QString(); +#endif +} + +/* + \internal +*/ +QString QFileSystemModelPrivate::type(const QModelIndex &index) const +{ + if (!index.isValid()) + return QString(); + return node(index)->type(); +} + +/*! + \internal +*/ +QString QFileSystemModelPrivate::name(const QModelIndex &index) const +{ + if (!index.isValid()) + return QString(); + QFileSystemNode *dirNode = node(index); + if (dirNode->isSymLink() && fileInfoGatherer.resolveSymlinks()) { + QString fullPath = QDir::fromNativeSeparators(filePath(index)); + if (resolvedSymLinks.contains(fullPath)) + return resolvedSymLinks[fullPath]; + } + // ### TODO it would be nice to grab the volume name if dirNode->parent == root + return dirNode->fileName; +} + +/*! + \internal +*/ +QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const +{ + if (!index.isValid()) + return QIcon(); + return node(index)->icon(); +} + +/*! + \reimp +*/ +bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, int role) +{ + Q_D(QFileSystemModel); + if (!idx.isValid() + || idx.column() != 0 + || role != Qt::EditRole + || (flags(idx) & Qt::ItemIsEditable) == 0) { + return false; + } + + QString newName = value.toString(); + QString oldName = idx.data().toString(); + if (newName == idx.data().toString()) + return true; + + if (newName.isEmpty() + || newName.contains(QDir::separator()) + || !QDir(filePath(parent(idx))).rename(oldName, newName)) { +#ifndef QT_NO_MESSAGEBOX + QMessageBox::information(0, QFileSystemModel::tr("Invalid filename"), + QFileSystemModel::tr("<b>The name \"%1\" can not be used.</b><p>Try using another name, with fewer characters or no punctuations marks.") + .arg(newName), + QMessageBox::Ok); +#endif // QT_NO_MESSAGEBOX + return false; + } else { + /* + *After re-naming something we don't want the selection to change* + - can't remove rows and later insert + - can't quickly remove and insert + - index pointer can't change because treeview doesn't use persistant index's + + - if this get any more complicated think of changing it to just + use layoutChanged + */ + + QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(idx); + QFileSystemModelPrivate::QFileSystemNode *parentNode = indexNode->parent; + int visibleLocation = parentNode->visibleLocation(parentNode->children.value(indexNode->fileName)->fileName); + + d->addNode(parentNode, newName); + parentNode->visibleChildren.removeAt(visibleLocation); + QFileSystemModelPrivate::QFileSystemNode * oldValue = parentNode->children.value(oldName); + parentNode->children[newName] = oldValue; + QFileInfo info(d->rootDir, newName); + oldValue->fileName = newName; + oldValue->parent = parentNode; + oldValue->populate(d->fileInfoGatherer.getInfo(info)); + oldValue->isVisible = true; + + parentNode->children.remove(oldName); + parentNode->visibleChildren.insert(visibleLocation, newName); + + d->delayedSort(); + emit fileRenamed(filePath(idx.parent()), oldName, newName); + } + return true; +} + +/*! + \reimp +*/ +QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) { + case Qt::DecorationRole: + if (section == 0) { + // ### TODO oh man this is ugly and doesn't even work all the way! + // it is still 2 pixels off + QImage pixmap(16, 1, QImage::Format_Mono); + pixmap.fill(0); + pixmap.setAlphaChannel(pixmap.createAlphaMask()); + return pixmap; + } + case Qt::TextAlignmentRole: + return Qt::AlignLeft; + } + + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QAbstractItemModel::headerData(section, orientation, role); + + QString returnValue; + switch (section) { + case 0: returnValue = tr("Name"); + break; + case 1: returnValue = tr("Size"); + break; + case 2: returnValue = +#ifdef Q_OS_MAC + tr("Kind", "Match OS X Finder"); +#else + tr("Type", "All other platforms"); +#endif + break; + // Windows - Type + // OS X - Kind + // Konqueror - File Type + // Nautilus - Type + case 3: returnValue = tr("Date Modified"); + break; + default: return QVariant(); + } + return returnValue; +} + +/*! + \reimp +*/ +Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const +{ + Q_D(const QFileSystemModel); + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + if (!index.isValid()) + return flags; + + QFileSystemModelPrivate::QFileSystemNode *indexNode = d->node(index); + if (d->nameFilterDisables && !d->passNameFilters(indexNode)) { + flags &= ~Qt::ItemIsEnabled; + // ### TODO you shouldn't be able to set this as the current item, task 119433 + return flags; + } + + flags |= Qt::ItemIsDragEnabled; + if (d->readOnly) + return flags; + if ((index.column() == 0) && indexNode->permissions() & QFile::WriteUser) { + flags |= Qt::ItemIsEditable; + if (indexNode->isDir()) + flags |= Qt::ItemIsDropEnabled; + } + return flags; +} + +/*! + \internal +*/ +void QFileSystemModelPrivate::_q_performDelayedSort() +{ + Q_Q(QFileSystemModel); + q->sort(sortColumn, sortOrder); +} + +static inline QChar getNextChar(const QString &s, int location) +{ + return (location < s.length()) ? s.at(location) : QChar(); +} + +/*! + Natural number sort, skips spaces. + + Examples: + 1, 2, 10, 55, 100 + 01.jpg, 2.jpg, 10.jpg + + Note on the algorithm: + Only as many characters as necessary are looked at and at most they all + are looked at once. + + Slower then QString::compare() (of course) + */ +int QFileSystemModelPrivate::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) +{ + for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) { + // skip spaces, tabs and 0's + QChar c1 = getNextChar(s1, l1); + while (c1.isSpace()) + c1 = getNextChar(s1, ++l1); + QChar c2 = getNextChar(s2, l2); + while (c2.isSpace()) + c2 = getNextChar(s2, ++l2); + + if (c1.isDigit() && c2.isDigit()) { + while (c1.digitValue() == 0) + c1 = getNextChar(s1, ++l1); + while (c2.digitValue() == 0) + c2 = getNextChar(s2, ++l2); + + int lookAheadLocation1 = l1; + int lookAheadLocation2 = l2; + int currentReturnValue = 0; + // find the last digit, setting currentReturnValue as we go if it isn't equal + for ( + QChar lookAhead1 = c1, lookAhead2 = c2; + (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); + lookAhead1 = getNextChar(s1, ++lookAheadLocation1), + lookAhead2 = getNextChar(s2, ++lookAheadLocation2) + ) { + bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); + bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); + if (!is1ADigit && !is2ADigit) + break; + if (!is1ADigit) + return -1; + if (!is2ADigit) + return 1; + if (currentReturnValue == 0) { + if (lookAhead1 < lookAhead2) { + currentReturnValue = -1; + } else if (lookAhead1 > lookAhead2) { + currentReturnValue = 1; + } + } + } + if (currentReturnValue != 0) + return currentReturnValue; + } + + if (cs == Qt::CaseInsensitive) { + if (!c1.isLower()) c1 = c1.toLower(); + if (!c2.isLower()) c2 = c2.toLower(); + } + int r = QString::localeAwareCompare(c1, c2); + if (r < 0) + return -1; + if (r > 0) + return 1; + } + // The two strings are the same (02 == 2) so fall back to the normal sort + return QString::compare(s1, s2, cs); +} + +/* + \internal + Helper functor used by sort() +*/ +class QFileSystemModelSorter +{ +public: + inline QFileSystemModelSorter(int column) : sortColumn(column) {} + + bool compareNodes(const QFileSystemModelPrivate::QFileSystemNode *l, + const QFileSystemModelPrivate::QFileSystemNode *r) const + { + switch (sortColumn) { + case 0: { +#ifndef Q_OS_MAC + // place directories before files + bool left = l->isDir(); + bool right = r->isDir(); + if (left ^ right) + return left; +#endif + return QFileSystemModelPrivate::naturalCompare(l->fileName, + r->fileName, Qt::CaseInsensitive) < 0; + } + case 1: + // Directories go first + if (l->isDir() && !r->isDir()) + return true; + return l->size() < r->size(); + case 2: + return l->type() < r->type(); + case 3: + return l->lastModified() < r->lastModified(); + } + Q_ASSERT(false); + return false; + } + + bool operator()(const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &l, + const QPair<QFileSystemModelPrivate::QFileSystemNode*, int> &r) const + { + return compareNodes(l.first, r.first); + } + + +private: + int sortColumn; +}; + +/* + \internal + + Sort all of the children of parent +*/ +void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent) +{ + QFileSystemModelPrivate::QFileSystemNode *indexNode = node(parent); + if (indexNode->children.count() == 0) + return; + + QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > values; + QHash<QString, QFileSystemNode *>::const_iterator iterator; + int i = 0; + for(iterator = indexNode->children.begin() ; iterator != indexNode->children.end() ; ++iterator) { + if (filtersAcceptsNode(iterator.value())) { + values.append(QPair<QFileSystemModelPrivate::QFileSystemNode*, int>((iterator.value()), i)); + } else { + iterator.value()->isVisible = false; + } + i++; + } + QFileSystemModelSorter ms(column); + qStableSort(values.begin(), values.end(), ms); + // First update the new visible list + indexNode->visibleChildren.clear(); + for (int i = 0; i < values.count(); ++i) { + indexNode->visibleChildren.append(values.at(i).first->fileName); + values.at(i).first->isVisible = true; + } +} + +/*! + \reimp +*/ +void QFileSystemModel::sort(int column, Qt::SortOrder order) +{ + Q_D(QFileSystemModel); + if (d->sortOrder == order && d->sortColumn == column && !d->forceSort) + return; + + emit layoutAboutToBeChanged(); + QModelIndexList oldList = persistentIndexList(); + QList<QPair<QFileSystemModelPrivate::QFileSystemNode*, int> > oldNodes; + for (int i = 0; i < oldList.count(); ++i) { + QPair<QFileSystemModelPrivate::QFileSystemNode*, int> pair(d->node(oldList.at(i)), oldList.at(i).column()); + oldNodes.append(pair); + } + + if (!(d->sortColumn == column && d->sortOrder != order && !d->forceSort)) { + //we sort only from where we are, don't need to sort all the model + d->sortChildren(column, index(rootPath())); + d->sortColumn = column; + d->forceSort = false; + } + d->sortOrder = order; + + QModelIndexList newList; + for (int i = 0; i < oldNodes.count(); ++i) { + QModelIndex idx = d->index(oldNodes.at(i).first); + idx = idx.sibling(idx.row(), oldNodes.at(i).second); + newList.append(idx); + } + changePersistentIndexList(oldList, newList); + emit layoutChanged(); +} + +/*! + Returns a list of MIME types that can be used to describe a list of items + in the model. +*/ +QStringList QFileSystemModel::mimeTypes() const +{ + return QStringList(QLatin1String("text/uri-list")); +} + +/*! + Returns an object that contains a serialized description of the specified + \a indexes. The format used to describe the items corresponding to the + indexes is obtained from the mimeTypes() function. + + If the list of indexes is empty, 0 is returned rather than a serialized + empty list. +*/ +QMimeData *QFileSystemModel::mimeData(const QModelIndexList &indexes) const +{ + QList<QUrl> urls; + QList<QModelIndex>::const_iterator it = indexes.begin(); + for (; it != indexes.end(); ++it) + if ((*it).column() == 0) + urls << QUrl::fromLocalFile(filePath(*it)); + QMimeData *data = new QMimeData(); + data->setUrls(urls); + return data; +} + +/*! + Handles the \a data supplied by a drag and drop operation that ended with + the given \a action over the row in the model specified by the \a row and + \a column and by the \a parent index. + + \sa supportedDropActions() +*/ +bool QFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + Q_UNUSED(row); + Q_UNUSED(column); + if (!parent.isValid() || isReadOnly()) + return false; + + bool success = true; + QString to = filePath(parent) + QDir::separator(); + + QList<QUrl> urls = data->urls(); + QList<QUrl>::const_iterator it = urls.constBegin(); + + switch (action) { + case Qt::CopyAction: + for (; it != urls.constEnd(); ++it) { + QString path = (*it).toLocalFile(); + success = QFile::copy(path, to + QFileInfo(path).fileName()) && success; + } + break; + case Qt::LinkAction: + for (; it != urls.constEnd(); ++it) { + QString path = (*it).toLocalFile(); + success = QFile::link(path, to + QFileInfo(path).fileName()) && success; + } + break; + case Qt::MoveAction: + for (; it != urls.constEnd(); ++it) { + QString path = (*it).toLocalFile(); + success = QFile::copy(path, to + QFileInfo(path).fileName()) + && QFile::remove(path) && success; + } + break; + default: + return false; + } + + return success; +} + +/*! + \reimp +*/ +Qt::DropActions QFileSystemModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; +} + +/*! + Returns the path of the item stored in the model under the + \a index given. +*/ +QString QFileSystemModel::filePath(const QModelIndex &index) const +{ + Q_D(const QFileSystemModel); + QString fullPath = d->filePath(index); + QFileSystemModelPrivate::QFileSystemNode *dirNode = d->node(index); + if (dirNode->isSymLink() && d->fileInfoGatherer.resolveSymlinks() + && d->resolvedSymLinks.contains(fullPath) + && dirNode->isDir()) { + QFileInfo resolvedInfo(fullPath); + resolvedInfo = resolvedInfo.canonicalFilePath(); + if (resolvedInfo.exists()) + return resolvedInfo.filePath(); + } + return fullPath; +} + +QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const +{ + Q_Q(const QFileSystemModel); + Q_UNUSED(q); + if (!index.isValid()) + return QString(); + Q_ASSERT(index.model() == q); + + QStringList path; + QModelIndex idx = index; + while (idx.isValid()) { + QFileSystemModelPrivate::QFileSystemNode *dirNode = node(idx); + if (dirNode) + path.prepend(dirNode->fileName); + idx = idx.parent(); + } + QString fullPath = QDir::fromNativeSeparators(path.join(QDir::separator())); +#if !defined(Q_OS_WIN) || defined(Q_OS_WINCE) + if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/')) + fullPath = fullPath.mid(1); +#endif + return fullPath; +} + +/*! + Create a directory with the \a name in the \a parent model index. +*/ +QModelIndex QFileSystemModel::mkdir(const QModelIndex &parent, const QString &name) +{ + Q_D(QFileSystemModel); + if (!parent.isValid()) + return parent; + + QDir dir(filePath(parent)); + if (!dir.mkdir(name)) + return QModelIndex(); + QFileSystemModelPrivate::QFileSystemNode *parentNode = d->node(parent); + d->addNode(parentNode, name); + Q_ASSERT(parentNode->children.contains(name)); + QFileSystemModelPrivate::QFileSystemNode *node = parentNode->children[name]; + node->populate(d->fileInfoGatherer.getInfo(QFileInfo(dir.absolutePath() + QDir::separator() + name))); + d->addVisibleFiles(parentNode, QStringList(name)); + return d->index(node); +} + +/*! + Returns the complete OR-ed together combination of QFile::Permission for the \a index. + */ +QFile::Permissions QFileSystemModel::permissions(const QModelIndex &index) const +{ + Q_D(const QFileSystemModel); + QFile::Permissions p = d->node(index)->permissions(); + if (d->readOnly) { + p ^= (QFile::WriteOwner | QFile::WriteUser + | QFile::WriteGroup | QFile::WriteOther); + } + return p; +} + +/*! + Sets the directory that is being watched by the model to \a newPath by + installing a \l{QFileSystemWatcher}{file system watcher} on it. Any + changes to files and directories within this directory will be + reflected in the model. + + If the path is changed, the rootPathChanged() signal will be emitted. + + \note This function does not change the structure of the model or + modify the data available to views. In other words, the "root" of + the model is \e not changed to include only files and directories + within the directory specified by \a newPath in the file system. + */ +QModelIndex QFileSystemModel::setRootPath(const QString &newPath) +{ + Q_D(QFileSystemModel); +#ifdef Q_OS_WIN + QString longNewPath = QDir::fromNativeSeparators(qt_GetLongPathName(newPath)); +#else + QString longNewPath = newPath; +#endif + QDir newPathDir(longNewPath); + //we remove .. and . from the given path if exist + if (!newPath.isEmpty()) { + longNewPath = QDir::cleanPath(longNewPath); + newPathDir.setPath(longNewPath); + } + + d->setRootPath = true; + + //user don't ask for the root path ("") but the conversion failed + if (!newPath.isEmpty() && longNewPath.isEmpty()) + return d->index(rootPath()); + + if (d->rootDir.path() == longNewPath) + return d->index(rootPath()); + + bool showDrives = (longNewPath.isEmpty() || longNewPath == d->myComputer()); + if (!showDrives && !newPathDir.exists()) + return d->index(rootPath()); + + // We have a new valid root path + d->rootDir = newPathDir; + QModelIndex newRootIndex; + if (showDrives) { + // otherwise dir will become '.' + d->rootDir.setPath(QLatin1String("")); + } else { + newRootIndex = d->index(newPathDir.path()); + } + fetchMore(newRootIndex); + emit rootPathChanged(longNewPath); + d->forceSort = true; + d->delayedSort(); + return newRootIndex; +} + +/*! + The currently set root path + + \sa rootDirectory() +*/ +QString QFileSystemModel::rootPath() const +{ + Q_D(const QFileSystemModel); + return d->rootDir.path(); +} + +/*! + The currently set directory + + \sa rootPath() +*/ +QDir QFileSystemModel::rootDirectory() const +{ + Q_D(const QFileSystemModel); + QDir dir(d->rootDir); + dir.setNameFilters(nameFilters()); + dir.setFilter(filter()); + return dir; +} + +/*! + Sets the \a provider of file icons for the directory model. +*/ +void QFileSystemModel::setIconProvider(QFileIconProvider *provider) +{ + Q_D(QFileSystemModel); + d->fileInfoGatherer.setIconProvider(provider); + qApp->processEvents(); + d->root.updateIcon(provider, QString()); +} + +/*! + Returns the file icon provider for this directory model. +*/ +QFileIconProvider *QFileSystemModel::iconProvider() const +{ + Q_D(const QFileSystemModel); + return d->fileInfoGatherer.iconProvider(); +} + +/*! + Sets the directory model's filter to that specified by \a filters. + + Note that the filter you set should always include the QDir::AllDirs enum value, + otherwise QFileSystemModel won't be able to read the directory structure. + + \sa QDir::Filters +*/ +void QFileSystemModel::setFilter(QDir::Filters filters) +{ + Q_D(QFileSystemModel); + if (d->filters == filters) + return; + d->filters = filters; + // CaseSensitivity might have changed + setNameFilters(nameFilters()); + d->forceSort = true; + d->delayedSort(); +} + +/*! + Returns the filter specification for the directory model. + + \sa QDir::Filters +*/ +QDir::Filters QFileSystemModel::filter() const +{ + Q_D(const QFileSystemModel); + return d->filters; +} + +/*! + \property QFileSystemModel::resolveSymlinks + \brief Whether the directory model should resolve symbolic links + + This is only relevant on operating systems that support symbolic links. + + By default, this property is false. +*/ +void QFileSystemModel::setResolveSymlinks(bool enable) +{ + Q_D(QFileSystemModel); + d->fileInfoGatherer.setResolveSymlinks(enable); +} + +bool QFileSystemModel::resolveSymlinks() const +{ + Q_D(const QFileSystemModel); + return d->fileInfoGatherer.resolveSymlinks(); +} + +/*! + \property QFileSystemModel::readOnly + \brief Whether the directory model allows writing to the file system + + If this property is set to false, the directory model will allow renaming, copying + and deleting of files and directories. + + This property is true by default +*/ +void QFileSystemModel::setReadOnly(bool enable) +{ + Q_D(QFileSystemModel); + d->readOnly = enable; +} + +bool QFileSystemModel::isReadOnly() const +{ + Q_D(const QFileSystemModel); + return d->readOnly; +} + +/*! + \property QFileSystemModel::nameFilterDisables + \brief Whether files that don't pass the name filter are hidden or disabled + + This property is true by default +*/ +void QFileSystemModel::setNameFilterDisables(bool enable) +{ + Q_D(QFileSystemModel); + if (d->nameFilterDisables == enable) + return; + d->nameFilterDisables = enable; + d->forceSort = true; + d->delayedSort(); +} + +bool QFileSystemModel::nameFilterDisables() const +{ + Q_D(const QFileSystemModel); + return d->nameFilterDisables; +} + +/*! + Sets the name \a filters to apply against the existing files. +*/ +void QFileSystemModel::setNameFilters(const QStringList &filters) +{ + // Prep the regexp's ahead of time +#ifndef QT_NO_REGEXP + Q_D(QFileSystemModel); + + if (!d->bypassFilters.isEmpty()) { + // update the bypass filter to only bypass the stuff that must be kept around + d->bypassFilters.clear(); + // We guarantee that rootPath will stick around + QPersistentModelIndex root(index(rootPath())); + QModelIndexList persistantList = persistentIndexList(); + for (int i = 0; i < persistantList.count(); ++i) { + QFileSystemModelPrivate::QFileSystemNode *node; + node = d->node(persistantList.at(i)); + while (node) { + if (d->bypassFilters.contains(node)) + break; + if (node->isDir()) + d->bypassFilters[node] = true; + node = node->parent; + } + } + } + + d->nameFilters.clear(); + const Qt::CaseSensitivity caseSensitive = + (filter() & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive; + for (int i = 0; i < filters.size(); ++i) { + d->nameFilters << QRegExp(filters.at(i), caseSensitive, QRegExp::Wildcard); + } + d->forceSort = true; + d->delayedSort(); +#endif +} + +/*! + Returns a list of filters applied to the names in the model. +*/ +QStringList QFileSystemModel::nameFilters() const +{ + Q_D(const QFileSystemModel); + QStringList filters; +#ifndef QT_NO_REGEXP + for (int i = 0; i < d->nameFilters.size(); ++i) { + filters << d->nameFilters.at(i).pattern(); + } +#endif + return filters; +} + +/*! + \reimp +*/ +bool QFileSystemModel::event(QEvent *event) +{ + Q_D(QFileSystemModel); + if (event->type() == QEvent::LanguageChange) { + d->root.retranslateStrings(d->fileInfoGatherer.iconProvider(), QString()); + return true; + } + return QAbstractItemModel::event(event); +} + +/*! + \internal + + Performed quick listing and see if any files have been added or removed, + then fetch more information on visible files. + */ +void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, const QStringList &files) +{ + QFileSystemModelPrivate::QFileSystemNode *parentNode = node(directory, false); + if (parentNode->children.count() == 0) + return; + QStringList toRemove; + QStringList newFiles = files; + qSort(newFiles.begin(), newFiles.end()); + QHash<QString, QFileSystemNode*>::const_iterator i = parentNode->children.constBegin(); + while (i != parentNode->children.constEnd()) { + QStringList::iterator iterator; + iterator = qBinaryFind(newFiles.begin(), newFiles.end(), i.value()->fileName); + if (iterator == newFiles.end()) { + toRemove.append(i.value()->fileName); + } + ++i; + } + for (int i = 0 ; i < toRemove.count() ; ++i ) + removeNode(parentNode, toRemove[i]); +} + +/*! + \internal + + Adds a new file to the children of parentNode + + *WARNING* this will change the count of children +*/ +QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName) +{ + // In the common case, itemLocation == count() so check there first + QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode); + parentNode->children.insert(fileName, node); + return node; +} + +/*! + \internal + + File at parentNode->children(itemLocation) has been removed, remove from the lists + and emit signals if necessary + + *WARNING* this will change the count of children and could change visibleChildren + */ +void QFileSystemModelPrivate::removeNode(QFileSystemModelPrivate::QFileSystemNode *parentNode, const QString& name) +{ + Q_Q(QFileSystemModel); + QModelIndex parent = index(parentNode); + bool indexHidden = isHiddenByFilter(parentNode, parent); + + int vLocation = parentNode->visibleLocation(name); + if (vLocation >= 0 && !indexHidden) + q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), + translateVisibleLocation(parentNode, vLocation)); + QFileSystemNode * node = parentNode->children.take(name); + delete node; + // cleanup sort files after removing rather then re-sorting which is O(n) + if (vLocation >= 0) + parentNode->visibleChildren.removeAt(vLocation); + if (vLocation >= 0 && !indexHidden) + q->endRemoveRows(); +} + +/* + \internal + Helper functor used by addVisibleFiles() +*/ +class QFileSystemModelVisibleFinder +{ +public: + inline QFileSystemModelVisibleFinder(QFileSystemModelPrivate::QFileSystemNode *node, QFileSystemModelSorter *sorter) : parentNode(node), sorter(sorter) {} + + bool operator()(const QString &, QString r) const + { + return sorter->compareNodes(parentNode->children.value(name), parentNode->children.value(r)); + } + + QString name; +private: + QFileSystemModelPrivate::QFileSystemNode *parentNode; + QFileSystemModelSorter *sorter; +}; + +/*! + \internal + + File at parentNode->children(itemLocation) was not visible before, but now should be + and emit signals if necessary. + + *WARNING* this will change the visible count + */ +void QFileSystemModelPrivate::addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles) +{ + Q_Q(QFileSystemModel); + QModelIndex parent = index(parentNode); + bool indexHidden = isHiddenByFilter(parentNode, parent); + if (!indexHidden) { + q->beginInsertRows(parent, parentNode->visibleChildren.count() , parentNode->visibleChildren.count() + newFiles.count() - 1); + } + for (int i = 0; i < newFiles.count(); ++i) { + parentNode->visibleChildren.append(newFiles.at(i)); + parentNode->children[newFiles.at(i)]->isVisible = true; + } + if (!indexHidden) + q->endInsertRows(); +} + +/*! + \internal + + File was visible before, but now should NOT be + + *WARNING* this will change the visible count + */ +void QFileSystemModelPrivate::removeVisibleFile(QFileSystemNode *parentNode, int vLocation) +{ + Q_Q(QFileSystemModel); + if (vLocation == -1) + return; + QModelIndex parent = index(parentNode); + bool indexHidden = isHiddenByFilter(parentNode, parent); + if (!indexHidden) + q->beginRemoveRows(parent, translateVisibleLocation(parentNode, vLocation), + translateVisibleLocation(parentNode, vLocation)); + parentNode->children[parentNode->visibleChildren.at(vLocation)]->isVisible = false; + parentNode->visibleChildren.removeAt(vLocation); + if (!indexHidden) + q->endRemoveRows(); +} + +/*! + \internal + + The thread has received new information about files, + update and emit dataChanged if it has actually changed. + */ +void QFileSystemModelPrivate::_q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &updates) +{ + Q_Q(QFileSystemModel); + QVector<QString> rowsToUpdate; + QStringList newFiles; + QFileSystemModelPrivate::QFileSystemNode *parentNode = node(path, false); + QModelIndex parentIndex = index(parentNode); + for (int i = 0; i < updates.count(); ++i) { + QString fileName = updates.at(i).first; + Q_ASSERT(!fileName.isEmpty()); + QExtendedInformation info = fileInfoGatherer.getInfo(updates.at(i).second); + bool previouslyHere = parentNode->children.contains(fileName); + if (!previouslyHere) { + addNode(parentNode, fileName); + } + QFileSystemModelPrivate::QFileSystemNode * node = parentNode->children.value(fileName); + bool isCaseSensitive = parentNode->caseSensitive(); + if (isCaseSensitive) { + if (node->fileName != fileName) + continue; + } else { + if (QString::compare(node->fileName,fileName,Qt::CaseInsensitive) != 0) + continue; + } + if (isCaseSensitive) { + Q_ASSERT(node->fileName == fileName); + } else { + node->fileName = fileName; + } + + if (info.size() == -1) { + removeNode(parentNode, fileName); + continue; + } + if (*node != info ) { + node->populate(info); + bypassFilters.remove(node); + // brand new information. + if (filtersAcceptsNode(node)) { + if (!node->isVisible) { + newFiles.append(fileName); + } else { + rowsToUpdate.append(fileName); + } + } else { + if (node->isVisible) { + int visibleLocation = parentNode->visibleLocation(fileName); + removeVisibleFile(parentNode, visibleLocation); + } else { + // The file is not visible, don't do anything + } + } + } + } + + // bundle up all of the changed signals into as few as possible. + qSort(rowsToUpdate.begin(), rowsToUpdate.end()); + QString min; + QString max; + for (int i = 0; i < rowsToUpdate.count(); ++i) { + QString value = rowsToUpdate.at(i); + //##TODO is there a way to bundle signals with QString as the content of the list? + /*if (min.isEmpty()) { + min = value; + if (i != rowsToUpdate.count() - 1) + continue; + } + if (i != rowsToUpdate.count() - 1) { + if ((value == min + 1 && max.isEmpty()) || value == max + 1) { + max = value; + continue; + } + }*/ + max = value; + min = value; + int visibleMin = parentNode->visibleLocation(min); + int visibleMax = parentNode->visibleLocation(max); + if (visibleMin >= 0 + && visibleMin < parentNode->visibleChildren.count() + && parentNode->visibleChildren.at(visibleMin) == min + && visibleMax >= 0) { + QModelIndex bottom = q->index(translateVisibleLocation(parentNode, visibleMin), 0, parentIndex); + QModelIndex top = q->index(translateVisibleLocation(parentNode, visibleMax), 3, parentIndex); + emit q->dataChanged(bottom, top); + } + + /*min = QString(); + max = QString();*/ + } + + if (newFiles.count() > 0) { + addVisibleFiles(parentNode, newFiles); + } + + if (newFiles.count() > 0 || (sortColumn != 0 && rowsToUpdate.count() > 0)) { + forceSort = true; + delayedSort(); + } +} + +/*! + \internal +*/ +void QFileSystemModelPrivate::_q_resolvedName(const QString &fileName, const QString &resolvedName) +{ + resolvedSymLinks[fileName] = resolvedName; +} + +/*! + \internal +*/ +void QFileSystemModelPrivate::init() +{ + Q_Q(QFileSystemModel); + qRegisterMetaType<QList<QPair<QString,QFileInfo> > >("QList<QPair<QString,QFileInfo> >"); + q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(const QString &, const QStringList &)), + q, SLOT(_q_directoryChanged(const QString &, const QStringList &))); + q->connect(&fileInfoGatherer, SIGNAL(updates(const QString &, const QList<QPair<QString, QFileInfo> > &)), + q, SLOT(_q_fileSystemChanged(const QString &, const QList<QPair<QString, QFileInfo> > &))); + q->connect(&fileInfoGatherer, SIGNAL(nameResolved(const QString &, const QString &)), + q, SLOT(_q_resolvedName(const QString &, const QString &))); + q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection); +} + +/*! + \internal + + Returns false if node doesn't pass the filters otherwise true + + QDir::Modified is not supported + QDir::Drives is not supported +*/ +bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) const +{ + // always accept drives + if (node->parent == &root || bypassFilters.contains(node)) + return true; + + // If we don't know anything yet don't accept it + if (!node->hasInformation()) + return false; + + const bool filterPermissions = ((filters & QDir::PermissionMask) + && (filters & QDir::PermissionMask) != QDir::PermissionMask); + const bool hideDirs = !(filters & (QDir::Dirs | QDir::AllDirs)); + const bool hideFiles = !(filters & QDir::Files); + const bool hideReadable = !(!filterPermissions || (filters & QDir::Readable)); + const bool hideWritable = !(!filterPermissions || (filters & QDir::Writable)); + const bool hideExecutable = !(!filterPermissions || (filters & QDir::Executable)); + const bool hideHidden = !(filters & QDir::Hidden); + const bool hideSystem = !(filters & QDir::System); + const bool hideSymlinks = (filters & QDir::NoSymLinks); + const bool hideDotAndDotDot = (filters & QDir::NoDotAndDotDot); + + // Note that we match the behavior of entryList and not QFileInfo on this and this + // incompatibility won't be fixed until Qt 5 at least + bool isDotOrDot = ( (node->fileName == QLatin1String(".") + || node->fileName == QLatin1String(".."))); + if ( (hideHidden && (!isDotOrDot && node->isHidden())) + || (hideSystem && node->isSystem()) + || (hideDirs && node->isDir()) + || (hideFiles && node->isFile()) + || (hideSymlinks && node->isSymLink()) + || (hideReadable && node->isReadable()) + || (hideWritable && node->isWritable()) + || (hideExecutable && node->isExecutable()) + || (hideDotAndDotDot && isDotOrDot)) + return false; + + return nameFilterDisables || passNameFilters(node); +} + +/* + \internal + + Returns true if node passes the name filters and should be visible. + */ +bool QFileSystemModelPrivate::passNameFilters(const QFileSystemNode *node) const +{ +#ifndef QT_NO_REGEXP + if (nameFilters.isEmpty()) + return true; + + // Check the name regularexpression filters + if (!(node->isDir() && (filters & QDir::AllDirs))) { + for (int i = 0; i < nameFilters.size(); ++i) { + if (nameFilters.at(i).exactMatch(node->fileName)) + return true; + } + return false; + } +#endif + return true; +} + +#include "moc_qfilesystemmodel.cpp" + +#endif // QT_NO_FILESYSTEMMODEL + +QT_END_NAMESPACE diff --git a/src/gui/dialogs/qfilesystemmodel.h b/src/gui/dialogs/qfilesystemmodel.h new file mode 100644 index 0000000000..52ecaf9181 --- /dev/null +++ b/src/gui/dialogs/qfilesystemmodel.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMMODEL_H +#define QFILESYSTEMMODEL_H + +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/qpair.h> +#include <QtCore/qdir.h> +#include <QtGui/qicon.h> +#include <QtCore/qdiriterator.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_FILESYSTEMMODEL + +class ExtendedInformation; +class QFileSystemModelPrivate; +class QFileIconProvider; + +class Q_GUI_EXPORT QFileSystemModel : public QAbstractItemModel +{ + Q_OBJECT + Q_PROPERTY(bool resolveSymlinks READ resolveSymlinks WRITE setResolveSymlinks) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + Q_PROPERTY(bool nameFilterDisables READ nameFilterDisables WRITE setNameFilterDisables) + +Q_SIGNALS: + void rootPathChanged(const QString &newPath); + void fileRenamed(const QString &path, const QString &oldName, const QString &newName); + +public: + enum Roles { + FileIconRole = Qt::DecorationRole, + FilePathRole = Qt::UserRole + 1, + FileNameRole = Qt::UserRole + 2, + FilePermissions = Qt::UserRole + 3 + }; + + explicit QFileSystemModel(QObject *parent = 0); + ~QFileSystemModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex index(const QString &path, int column = 0) const; + QModelIndex parent(const QModelIndex &child) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant myComputer(int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + Qt::ItemFlags flags(const QModelIndex &index) const; + + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent); + Qt::DropActions supportedDropActions() const; + + // QFileSystemModel specific API + QModelIndex setRootPath(const QString &path); + QString rootPath() const; + QDir rootDirectory() const; + + void setIconProvider(QFileIconProvider *provider); + QFileIconProvider *iconProvider() const; + + void setFilter(QDir::Filters filters); + QDir::Filters filter() const; + + void setResolveSymlinks(bool enable); + bool resolveSymlinks() const; + + void setReadOnly(bool enable); + bool isReadOnly() const; + + void setNameFilterDisables(bool enable); + bool nameFilterDisables() const; + + void setNameFilters(const QStringList &filters); + QStringList nameFilters() const; + + QString filePath(const QModelIndex &index) const; + bool isDir(const QModelIndex &index) const; + qint64 size(const QModelIndex &index) const; + QString type(const QModelIndex &index) const; + QDateTime lastModified(const QModelIndex &index) const; + + QModelIndex mkdir(const QModelIndex &parent, const QString &name); + inline bool rmdir(const QModelIndex &index) const; + inline QString fileName(const QModelIndex &index) const; + inline QIcon fileIcon(const QModelIndex &index) const; + QFile::Permissions permissions(const QModelIndex &index) const; + inline QFileInfo fileInfo(const QModelIndex &index) const; + bool remove(const QModelIndex &index) const; + +protected: + QFileSystemModel(QFileSystemModelPrivate &, QObject *parent = 0); + void timerEvent(QTimerEvent *event); + bool event(QEvent *event); + +private: + Q_DECLARE_PRIVATE(QFileSystemModel) + Q_DISABLE_COPY(QFileSystemModel) + + Q_PRIVATE_SLOT(d_func(), void _q_directoryChanged(const QString &directory, const QStringList &list)) + Q_PRIVATE_SLOT(d_func(), void _q_performDelayedSort()) + Q_PRIVATE_SLOT(d_func(), void _q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &)) + Q_PRIVATE_SLOT(d_func(), void _q_resolvedName(const QString &fileName, const QString &resolvedName)) +}; + +inline bool QFileSystemModel::rmdir(const QModelIndex &aindex) const +{ QDir dir; return dir.rmdir(filePath(aindex)); } +inline QString QFileSystemModel::fileName(const QModelIndex &aindex) const +{ return aindex.data(Qt::DisplayRole).toString(); } +inline QIcon QFileSystemModel::fileIcon(const QModelIndex &aindex) const +{ return qvariant_cast<QIcon>(aindex.data(Qt::DecorationRole)); } +inline QFileInfo QFileSystemModel::fileInfo(const QModelIndex &aindex) const +{ return QFileInfo(filePath(aindex)); } + +#endif // QT_NO_FILESYSTEMMODEL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFILESYSTEMMODEL_H + diff --git a/src/gui/dialogs/qfilesystemmodel_p.h b/src/gui/dialogs/qfilesystemmodel_p.h new file mode 100644 index 0000000000..f1e798b52e --- /dev/null +++ b/src/gui/dialogs/qfilesystemmodel_p.h @@ -0,0 +1,305 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILESYSTEMMODEL_P_H +#define QFILESYSTEMMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qfilesystemmodel.h" + +#ifndef QT_NO_FILESYSTEMMODEL + +#include <private/qabstractitemmodel_p.h> +#include <qabstractitemmodel.h> +#include "qfileinfogatherer_p.h" +#include <qpair.h> +#include <qdir.h> +#include <qicon.h> +#include <qdir.h> +#include <qicon.h> +#include <qfileinfo.h> +#include <qtimer.h> +#include <qhash.h> + +QT_BEGIN_NAMESPACE + +class ExtendedInformation; +class QFileSystemModelPrivate; +class QFileIconProvider; + +class Q_AUTOTEST_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate +{ + Q_DECLARE_PUBLIC(QFileSystemModel) + +public: + class QFileSystemNode + { + public: + QFileSystemNode(const QString &filename = QString(), QFileSystemNode *p = 0) + : fileName(filename), populatedChildren(false), isVisible(false), parent(p), info(0) {} + ~QFileSystemNode() { + QHash<QString, QFileSystemNode*>::const_iterator i = children.constBegin(); + while (i != children.constEnd()) { + delete i.value(); + ++i; + } + delete info; + info = 0; + parent = 0; + } + + QString fileName; + + inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; } + inline QString type() const { if (info) return info->displayType; return QLatin1String(""); } + inline QDateTime lastModified() const { if (info) return info->lastModified(); return QDateTime(); } + inline QFile::Permissions permissions() const { if (info) return info->permissions(); return 0; } + inline bool isReadable() const { return ((permissions() & QFile::ReadUser) != 0); } + inline bool isWritable() const { return ((permissions() & QFile::WriteUser) != 0); } + inline bool isExecutable() const { return ((permissions() & QFile::ExeUser) != 0); } + inline bool isDir() const { + if (info) + return info->isDir(); + if (children.count() > 0) + return true; + return false; + } + inline bool isFile() const { if (info) return info->isFile(); return true; } + inline bool isSystem() const { if (info) return info->isSystem(); return true; } + inline bool isHidden() const { if (info) return info->isHidden(); return false; } + inline bool isSymLink() const { if (info) return info->isSymLink(); return false; } + inline bool caseSensitive() const { if (info) return info->isCaseSensitive(); return false; } + inline QIcon icon() const { if (info) return info->icon; return QIcon(); } + + inline bool operator <(const QFileSystemNode &node) const { + if (caseSensitive() || node.caseSensitive()) + return fileName < node.fileName; + return QString::compare(fileName, node.fileName, Qt::CaseInsensitive) < 0; + } + inline bool operator >(const QString &name) const { + if (caseSensitive()) + return fileName > name; + return QString::compare(fileName, name, Qt::CaseInsensitive) > 0; + } + inline bool operator <(const QString &name) const { + if (caseSensitive()) + return fileName < name; + return QString::compare(fileName, name, Qt::CaseInsensitive) < 0; + } + inline bool operator !=(const QExtendedInformation &fileInfo) const { + return !operator==(fileInfo); + } + bool operator ==(const QString &name) const { + if (caseSensitive()) + return fileName == name; + return QString::compare(fileName, name, Qt::CaseInsensitive) == 0; + } + bool operator ==(const QExtendedInformation &fileInfo) const { + return info && (*info == fileInfo); + } + + inline bool hasInformation() const { return info != 0; } + + void populate(const QExtendedInformation &fileInfo) { + if (!info) + info = new QExtendedInformation(fileInfo.fileInfo()); + (*info) = fileInfo; + } + + // children shouldn't normally be accessed directly, use node() + inline int visibleLocation(QString childName) { + return visibleChildren.indexOf(childName); + } + void updateIcon(QFileIconProvider *iconProvider, const QString &path) { + if (info) + info->icon = iconProvider->icon(QFileInfo(path)); + QHash<QString, QFileSystemNode *>::const_iterator iterator; + for(iterator = children.constBegin() ; iterator != children.constEnd() ; ++iterator) { + iterator.value()->updateIcon(iconProvider, path + QLatin1Char('/') + iterator.value()->fileName); + } + } + + void retranslateStrings(QFileIconProvider *iconProvider, const QString &path) { + if (info) + info->displayType = iconProvider->type(QFileInfo(path)); + QHash<QString, QFileSystemNode *>::const_iterator iterator; + for(iterator = children.constBegin() ; iterator != children.constEnd() ; ++iterator) { + iterator.value()->retranslateStrings(iconProvider, path + QLatin1Char('/') + iterator.value()->fileName); + } + } + + bool populatedChildren; + bool isVisible; + QHash<QString,QFileSystemNode *> children; + QList<QString> visibleChildren; + QFileSystemNode *parent; + + private: + QExtendedInformation *info; + + }; + + QFileSystemModelPrivate() : + forceSort(true), + sortColumn(0), + sortOrder(Qt::AscendingOrder), + readOnly(true), + setRootPath(false), + filters(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs), + nameFilterDisables(true) // false on windows, true on mac and unix + { + delayedSortTimer.setSingleShot(true); + } + + void init(); + /* + \internal + + Return true if index which is owned by node is hidden by the filter. + */ + inline bool isHiddenByFilter(QFileSystemNode *indexNode, const QModelIndex &index) const + { + return (indexNode != &root && !index.isValid()); + } + QFileSystemNode *node(const QModelIndex &index) const; + QFileSystemNode *node(const QString &path, bool fetch = true) const; + inline QModelIndex index(const QString &path) { return index(node(path)); } + QModelIndex index(const QFileSystemNode *node) const; + bool filtersAcceptsNode(const QFileSystemNode *node) const; + bool passNameFilters(const QFileSystemNode *node) const; + void removeNode(QFileSystemNode *parentNode, const QString &name); + QFileSystemNode* addNode(QFileSystemNode *parentNode, const QString &fileName); + void addVisibleFiles(QFileSystemNode *parentNode, const QStringList &newFiles); + void removeVisibleFile(QFileSystemNode *parentNode, int visibleLocation); + void sortChildren(int column, const QModelIndex &parent); + + inline int translateVisibleLocation(QFileSystemNode *parent, int row) const { + return (sortOrder == Qt::AscendingOrder) ? row : parent->visibleChildren.count() - row - 1; + } + + inline static QString myComputer() { + // ### TODO We should query the system to find out what the string should be + // XP == "My Computer", + // Vista == "Computer", + // OS X == "Computer" (sometime user generated) "Benjamin's PowerBook G4" +#ifdef Q_OS_WIN + return QFileSystemModel::tr("My Computer"); +#else + return QFileSystemModel::tr("Computer"); +#endif + } + + inline void delayedSort() { + if (!delayedSortTimer.isActive()) + delayedSortTimer.start(0); + } + + static bool caseInsensitiveLessThan(const QString &s1, const QString &s2) + { + return QString::compare(s1, s2, Qt::CaseInsensitive) < 0; + } + + static bool nodeCaseInsensitiveLessThan(const QFileSystemModelPrivate::QFileSystemNode &s1, const QFileSystemModelPrivate::QFileSystemNode &s2) + { + return QString::compare(s1.fileName, s2.fileName, Qt::CaseInsensitive) < 0; + } + + QIcon icon(const QModelIndex &index) const; + QString name(const QModelIndex &index) const; + QString filePath(const QModelIndex &index) const; + QString size(const QModelIndex &index) const; + static QString size(qint64 bytes); + QString type(const QModelIndex &index) const; + QString time(const QModelIndex &index) const; + + void _q_directoryChanged(const QString &directory, const QStringList &list); + void _q_performDelayedSort(); + void _q_fileSystemChanged(const QString &path, const QList<QPair<QString, QFileInfo> > &); + void _q_resolvedName(const QString &fileName, const QString &resolvedName); + + static int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs); + + QDir rootDir; +#ifndef QT_NO_FILESYSTEMWATCHER + QFileInfoGatherer fileInfoGatherer; +#endif + QTimer delayedSortTimer; + bool forceSort; + int sortColumn; + Qt::SortOrder sortOrder; + bool readOnly; + bool setRootPath; + QDir::Filters filters; + QHash<const QFileSystemNode*, bool> bypassFilters; + bool nameFilterDisables; +#ifndef QT_NO_REGEXP + QList<QRegExp> nameFilters; +#endif + // ### Qt 5: resolvedSymLinks goes away + QHash<QString, QString> resolvedSymLinks; + + QFileSystemNode root; + + QBasicTimer fetchingTimer; + struct Fetching { + QString dir; + QString file; + const QFileSystemNode *node; + }; + QList<Fetching> toFetch; + +}; +#endif // QT_NO_FILESYSTEMMODEL + +QT_END_NAMESPACE + +#endif + diff --git a/src/gui/dialogs/qfontdialog.cpp b/src/gui/dialogs/qfontdialog.cpp new file mode 100644 index 0000000000..4c5bf4fbaa --- /dev/null +++ b/src/gui/dialogs/qfontdialog.cpp @@ -0,0 +1,1070 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowdefs.h" + +#ifndef QT_NO_FONTDIALOG + +#include "qfontdialog.h" +#include "qfontdialog_p.h" + +#include <qapplication.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qevent.h> +#include <qfontdatabase.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qstyle.h> +#include <qdialogbuttonbox.h> +#include <qheaderview.h> +#include <qlistview.h> +#include <qstringlistmodel.h> +#include <qvalidator.h> +#include <private/qdialog_p.h> +#include <private/qfont_p.h> + +QT_BEGIN_NAMESPACE + +class QFontListView : public QListView +{ + Q_OBJECT +public: + QFontListView(QWidget *parent); + inline QStringListModel *model() const { + return static_cast<QStringListModel *>(QListView::model()); + } + inline void setCurrentItem(int item) { + QListView::setCurrentIndex(static_cast<QAbstractListModel*>(model())->index(item)); + } + inline int currentItem() const { + return QListView::currentIndex().row(); + } + inline int count() const { + return model()->rowCount(); + } + inline QString currentText() const { + int row = QListView::currentIndex().row(); + return row < 0 ? QString() : model()->stringList().at(row); + } + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + QListView::currentChanged(current, previous); + if (current.isValid()) + emit highlighted(current.row()); + } + QString text(int i) const { + return model()->stringList().at(i); + } +signals: + void highlighted(int); +}; + +QFontListView::QFontListView(QWidget *parent) + : QListView(parent) +{ + setModel(new QStringListModel(parent)); + setEditTriggers(NoEditTriggers); +} + +static const Qt::WindowFlags DefaultWindowFlags = + Qt::Dialog | Qt::WindowSystemMenuHint; + +/*! + \class QFontDialog + \ingroup dialogs + \ingroup text + \mainclass + \brief The QFontDialog class provides a dialog widget for selecting a font. + + A font dialog is created through one of the static getFont() + functions. + + Examples: + + \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 0 + + The dialog can also be used to set a widget's font directly: + \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 1 + If the user clicks OK the font they chose will be used for myWidget, + and if they click Cancel the original font is used. + + \image plastique-fontdialog.png A font dialog in the Plastique widget style. + + \sa QFont, QFontInfo, QFontMetrics, QColorDialog, QFileDialog, QPrintDialog, + {Standard Dialogs Example} +*/ + +/*! + \since 4.5 + + Constructs a standard font dialog. + + Use setCurrentFont() to set the initial font attributes. + + The \a parent parameter is passed to the QDialog constructor. + + \sa getFont() +*/ +QFontDialog::QFontDialog(QWidget *parent) + : QDialog(*new QFontDialogPrivate, parent, DefaultWindowFlags) +{ + Q_D(QFontDialog); + d->init(); +} + +/*! + \since 4.5 + + Constructs a standard font dialog with the given \a parent and specified + \a initial color. +*/ +QFontDialog::QFontDialog(const QFont &initial, QWidget *parent) + : QDialog(*new QFontDialogPrivate, parent, DefaultWindowFlags) +{ + Q_D(QFontDialog); + d->init(); + setCurrentFont(initial); +} + +void QFontDialogPrivate::init() +{ + Q_Q(QFontDialog); + + q->setSizeGripEnabled(true); + q->setWindowTitle(QFontDialog::tr("Select Font")); + + // grid + familyEdit = new QLineEdit(q); + familyEdit->setReadOnly(true); + familyList = new QFontListView(q); + familyEdit->setFocusProxy(familyList); + + familyAccel = new QLabel(q); +#ifndef QT_NO_SHORTCUT + familyAccel->setBuddy(familyList); +#endif + familyAccel->setIndent(2); + + styleEdit = new QLineEdit(q); + styleEdit->setReadOnly(true); + styleList = new QFontListView(q); + styleEdit->setFocusProxy(styleList); + + styleAccel = new QLabel(q); +#ifndef QT_NO_SHORTCUT + styleAccel->setBuddy(styleList); +#endif + styleAccel->setIndent(2); + + sizeEdit = new QLineEdit(q); + sizeEdit->setFocusPolicy(Qt::ClickFocus); + QIntValidator *validator = new QIntValidator(1, 512, q); + sizeEdit->setValidator(validator); + sizeList = new QFontListView(q); + + sizeAccel = new QLabel(q); +#ifndef QT_NO_SHORTCUT + sizeAccel->setBuddy(sizeEdit); +#endif + sizeAccel->setIndent(2); + + // effects box + effects = new QGroupBox(q); + QVBoxLayout *vbox = new QVBoxLayout(effects); + strikeout = new QCheckBox(effects); + vbox->addWidget(strikeout); + underline = new QCheckBox(effects); + vbox->addWidget(underline); + + sample = new QGroupBox(q); + QHBoxLayout *hbox = new QHBoxLayout(sample); + sampleEdit = new QLineEdit(sample); + sampleEdit->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)); + sampleEdit->setAlignment(Qt::AlignCenter); + // Note that the sample text is *not* translated with tr(), as the + // characters used depend on the charset encoding. + sampleEdit->setText(QLatin1String("AaBbYyZz")); + hbox->addWidget(sampleEdit); + + writingSystemCombo = new QComboBox(q); + + writingSystemAccel = new QLabel(q); +#ifndef QT_NO_SHORTCUT + writingSystemAccel->setBuddy(writingSystemCombo); +#endif + writingSystemAccel->setIndent(2); + + size = 0; + smoothScalable = false; + + QObject::connect(writingSystemCombo, SIGNAL(activated(int)), q, SLOT(_q_writingSystemHighlighted(int))); + QObject::connect(familyList, SIGNAL(highlighted(int)), q, SLOT(_q_familyHighlighted(int))); + QObject::connect(styleList, SIGNAL(highlighted(int)), q, SLOT(_q_styleHighlighted(int))); + QObject::connect(sizeList, SIGNAL(highlighted(int)), q, SLOT(_q_sizeHighlighted(int))); + QObject::connect(sizeEdit, SIGNAL(textChanged(QString)), q, SLOT(_q_sizeChanged(QString))); + + QObject::connect(strikeout, SIGNAL(clicked()), q, SLOT(_q_updateSample())); + QObject::connect(underline, SIGNAL(clicked()), q, SLOT(_q_updateSample())); + + for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) { + QFontDatabase::WritingSystem ws = QFontDatabase::WritingSystem(i); + QString writingSystemName = QFontDatabase::writingSystemName(ws); + if (writingSystemName.isEmpty()) + break; + writingSystemCombo->addItem(writingSystemName); + } + + updateFamilies(); + if (familyList->count() != 0) + familyList->setCurrentItem(0); + + // grid layout + QGridLayout *mainGrid = new QGridLayout(q); + + int spacing = mainGrid->spacing(); + if (spacing >= 0) { // uniform spacing + mainGrid->setSpacing(0); + + mainGrid->setColumnMinimumWidth(1, spacing); + mainGrid->setColumnMinimumWidth(3, spacing); + + int margin = 0; + mainGrid->getContentsMargins(0, 0, 0, &margin); + + mainGrid->setRowMinimumHeight(3, margin); + mainGrid->setRowMinimumHeight(6, 2); + mainGrid->setRowMinimumHeight(8, margin); + } + + mainGrid->addWidget(familyAccel, 0, 0); + mainGrid->addWidget(familyEdit, 1, 0); + mainGrid->addWidget(familyList, 2, 0); + + mainGrid->addWidget(styleAccel, 0, 2); + mainGrid->addWidget(styleEdit, 1, 2); + mainGrid->addWidget(styleList, 2, 2); + + mainGrid->addWidget(sizeAccel, 0, 4); + mainGrid->addWidget(sizeEdit, 1, 4); + mainGrid->addWidget(sizeList, 2, 4); + + mainGrid->setColumnStretch(0, 38); + mainGrid->setColumnStretch(2, 24); + mainGrid->setColumnStretch(4, 10); + + mainGrid->addWidget(effects, 4, 0); + + mainGrid->addWidget(sample, 4, 2, 4, 3); + + mainGrid->addWidget(writingSystemAccel, 5, 0); + mainGrid->addWidget(writingSystemCombo, 7, 0); + + buttonBox = new QDialogButtonBox(q); + mainGrid->addWidget(buttonBox, 9, 0, 1, 5); + + QPushButton *button + = static_cast<QPushButton *>(buttonBox->addButton(QDialogButtonBox::Ok)); + QObject::connect(buttonBox, SIGNAL(accepted()), q, SLOT(accept())); + button->setDefault(true); + + buttonBox->addButton(QDialogButtonBox::Cancel); + QObject::connect(buttonBox, SIGNAL(rejected()), q, SLOT(reject())); + +#if defined(Q_OS_WINCE) + q->resize(180, 120); +#else + q->resize(500, 360); +#endif // Q_OS_WINCE + + sizeEdit->installEventFilter(q); + familyList->installEventFilter(q); + styleList->installEventFilter(q); + sizeList->installEventFilter(q); + + familyList->setFocus(); + retranslateStrings(); + +#ifdef Q_WS_MAC + delegate = 0; +#endif +} + +/*! + \internal + Destroys the font dialog and frees up its storage. +*/ + +QFontDialog::~QFontDialog() +{ +} + +/*! + Executes a modal font dialog and returns a font. + + If the user clicks \gui OK, the selected font is returned. If the user + clicks \gui Cancel, the \a initial font is returned. + + The dialog is constructed with the given \a parent and the options specified + in \a options. \a title is shown as the window title of the dialog and \a + initial is the initially selected font. If the \a ok parameter is not-null, + the value it refers to is set to true if the user clicks \gui OK, and set to + false if the user clicks \gui Cancel. + + Examples: + \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 2 + + The dialog can also be used to set a widget's font directly: + \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 3 + In this example, if the user clicks OK the font they chose will be + used, and if they click Cancel the original font is used. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFontDialog constructors. +*/ +QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title, + FontDialogOptions options) +{ + return QFontDialogPrivate::getFont(ok, initial, parent, title, options); +} + +/*! + \overload + \since 4.5 +*/ +QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title) +{ + return QFontDialogPrivate::getFont(ok, initial, parent, title, 0); +} + +/*! + \overload +*/ +QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget *parent) +{ + return QFontDialogPrivate::getFont(ok, initial, parent, QString(), 0); +} + +/*! + \overload + + Executes a modal font dialog and returns a font. + + If the user clicks \gui OK, the selected font is returned. If the user + clicks \gui Cancel, the Qt default font is returned. + + The dialog is constructed with the given \a parent. + If the \a ok parameter is not-null, the value it refers to is set + to true if the user clicks \gui OK, and false if the user clicks + \gui Cancel. + + Example: + \snippet doc/src/snippets/code/src_gui_dialogs_qfontdialog.cpp 4 + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFontDialog constructors. +*/ +QFont QFontDialog::getFont(bool *ok, QWidget *parent) +{ + QFont initial; + return QFontDialogPrivate::getFont(ok, initial, parent, QString(), 0); +} + +QFont QFontDialogPrivate::getFont(bool *ok, const QFont &initial, QWidget *parent, + const QString &title, QFontDialog::FontDialogOptions options) +{ +#ifdef Q_WS_MAC + if (!(options & QFontDialog::DontUseNativeDialog) + && QFontDialogPrivate::sharedFontPanelAvailable) { + return QFontDialogPrivate::execCocoaFontPanel(ok, initial, parent, + title.isEmpty() ? QFontDialog::tr("Select Font") : title, options); + } +#endif + + QFontDialog dlg(parent); + dlg.setOptions(options); + dlg.setCurrentFont(initial); + if (!title.isEmpty()) + dlg.setWindowTitle(title); + + int ret = (dlg.exec() || (options & QFontDialog::NoButtons)); + if (ok) + *ok = !!ret; + if (ret) { + return dlg.selectedFont(); + } else { + return initial; + } +} + +/*! + \internal + An event filter to make the Up, Down, PageUp and PageDown keys work + correctly in the line edits. The source of the event is the object + \a o and the event is \a e. +*/ + +bool QFontDialog::eventFilter(QObject *o , QEvent *e) +{ + Q_D(QFontDialog); + if (e->type() == QEvent::KeyPress) { + QKeyEvent *k = (QKeyEvent *)e; + if (o == d->sizeEdit && + (k->key() == Qt::Key_Up || + k->key() == Qt::Key_Down || + k->key() == Qt::Key_PageUp || + k->key() == Qt::Key_PageDown)) { + + int ci = d->sizeList->currentItem(); + (void)QApplication::sendEvent(d->sizeList, k); + + if (ci != d->sizeList->currentItem() + && style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, this)) + d->sizeEdit->selectAll(); + return true; + } else if ((o == d->familyList || o == d->styleList) && + (k->key() == Qt::Key_Return || k->key() == Qt::Key_Enter)) { + k->accept(); + accept(); + return true; + } + } else if (e->type() == QEvent::FocusIn + && style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, this)) { + if (o == d->familyList) + d->familyEdit->selectAll(); + else if (o == d->styleList) + d->styleEdit->selectAll(); + else if (o == d->sizeList) + d->sizeEdit->selectAll(); + } else if (e->type() == QEvent::MouseButtonPress && o == d->sizeList) { + d->sizeEdit->setFocus(); + } + return QDialog::eventFilter(o, e); +} + +/* + Updates the contents of the "font family" list box. This + function can be reimplemented if you have special requirements. +*/ + +void QFontDialogPrivate::updateFamilies() +{ + Q_Q(QFontDialog); + + familyList->blockSignals(true); + + enum match_t { MATCH_NONE = 0, MATCH_LAST_RESORT = 1, MATCH_APP = 2, MATCH_FAMILY = 3 }; + + QStringList familyNames = fdb.families(writingSystem); + + familyList->model()->setStringList(familyNames); + + QString foundryName1, familyName1, foundryName2, familyName2; + int bestFamilyMatch = -1; + match_t bestFamilyType = MATCH_NONE; + + QFont f; + + // ##### do the right thing for a list of family names in the font. + QFontDatabase::parseFontName(family, foundryName1, familyName1); + + QStringList::const_iterator it = familyNames.constBegin(); + int i = 0; + for(; it != familyNames.constEnd(); ++it, ++i) { + QFontDatabase::parseFontName(*it, foundryName2, familyName2); + + //try to match... + if (familyName1 == familyName2) { + bestFamilyType = MATCH_FAMILY; + if (foundryName1 == foundryName2) { + bestFamilyMatch = i; + break; + } + if (bestFamilyMatch < MATCH_FAMILY) + bestFamilyMatch = i; + } + + //and try some fall backs + match_t type = MATCH_NONE; + if (bestFamilyType <= MATCH_NONE && familyName2 == f.lastResortFamily()) + type = MATCH_LAST_RESORT; + if (bestFamilyType <= MATCH_LAST_RESORT && familyName2 == f.family()) + type = MATCH_APP; + // ### add fallback for writingSystem + if (type != MATCH_NONE) { + bestFamilyType = type; + bestFamilyMatch = i; + } + } + + if (i != -1 && bestFamilyType != MATCH_NONE) + familyList->setCurrentItem(bestFamilyMatch); + else + familyList->setCurrentItem(0); + familyEdit->setText(familyList->currentText()); + if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q) + && familyList->hasFocus()) + familyEdit->selectAll(); + + familyList->blockSignals(false); + updateStyles(); +} + +/* + Updates the contents of the "font style" list box. This + function can be reimplemented if you have special requirements. +*/ +void QFontDialogPrivate::updateStyles() +{ + Q_Q(QFontDialog); + + styleList->blockSignals(true); + + QStringList styles = fdb.styles(familyList->currentText()); + styleList->model()->setStringList(styles); + + if (styles.isEmpty()) { + styleEdit->clear(); + smoothScalable = false; + } else { + if (!style.isEmpty()) { + bool found = false; + bool first = true; + QString cstyle = style; + + redo: + for (int i = 0; i < (int)styleList->count(); i++) { + if (cstyle == styleList->text(i)) { + styleList->setCurrentItem(i); + found = true; + break; + } + } + if (!found && first) { + if (cstyle.contains(QLatin1String("Italic"))) { + cstyle.replace(QLatin1String("Italic"), QLatin1String("Oblique")); + first = false; + goto redo; + } else if (cstyle.contains(QLatin1String("Oblique"))) { + cstyle.replace(QLatin1String("Oblique"), QLatin1String("Italic")); + first = false; + goto redo; + } + } + if (!found) + styleList->setCurrentItem(0); + } + + styleEdit->setText(styleList->currentText()); + if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q) + && styleList->hasFocus()) + styleEdit->selectAll(); + + smoothScalable = fdb.isSmoothlyScalable(familyList->currentText(), styleList->currentText()); + } + + styleList->blockSignals(false); + + updateSizes(); +} + +/*! + \internal + Updates the contents of the "font size" list box. This + function can be reimplemented if you have special requirements. +*/ + +void QFontDialogPrivate::updateSizes() +{ + Q_Q(QFontDialog); + + sizeList->blockSignals(true); + + if (!familyList->currentText().isEmpty()) { + QList<int> sizes = fdb.pointSizes(familyList->currentText(), styleList->currentText()); + + int i = 0; + int current = -1; + QStringList str_sizes; + for(QList<int>::const_iterator it = sizes.constBegin(); it != sizes.constEnd(); ++it) { + str_sizes.append(QString::number(*it)); + if (current == -1 && *it >= size) + current = i; + ++i; + } + sizeList->model()->setStringList(str_sizes); + if (current == -1) { + // we request a size bigger than the ones in the list, select the biggest one + current = sizeList->count() - 1; + } + sizeList->setCurrentItem(current); + + sizeEdit->blockSignals(true); + sizeEdit->setText((smoothScalable ? QString::number(size) : sizeList->currentText())); + if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q) + && sizeList->hasFocus()) + sizeEdit->selectAll(); + sizeEdit->blockSignals(false); + } else { + sizeEdit->clear(); + } + + sizeList->blockSignals(false); + _q_updateSample(); +} + +void QFontDialogPrivate::_q_updateSample() +{ + // compute new font + int pSize = sizeEdit->text().toInt(); + QFont newFont(fdb.font(familyList->currentText(), style, pSize)); + newFont.setStrikeOut(strikeout->isChecked()); + newFont.setUnderline(underline->isChecked()); + + if (familyList->currentText().isEmpty()) + sampleEdit->clear(); + + updateSampleFont(newFont); +} + +void QFontDialogPrivate::updateSampleFont(const QFont &newFont) +{ + Q_Q(QFontDialog); + if (newFont != sampleEdit->font()) { + sampleEdit->setFont(newFont); + emit q->currentFontChanged(newFont); + } +} + +/*! + \internal +*/ +void QFontDialogPrivate::_q_writingSystemHighlighted(int index) +{ + writingSystem = QFontDatabase::WritingSystem(index); + sampleEdit->setText(fdb.writingSystemSample(writingSystem)); + updateFamilies(); +} + +/*! + \internal +*/ +void QFontDialogPrivate::_q_familyHighlighted(int i) +{ + Q_Q(QFontDialog); + family = familyList->text(i); + familyEdit->setText(family); + if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q) + && familyList->hasFocus()) + familyEdit->selectAll(); + + updateStyles(); +} + + +/*! + \internal +*/ + +void QFontDialogPrivate::_q_styleHighlighted(int index) +{ + Q_Q(QFontDialog); + QString s = styleList->text(index); + styleEdit->setText(s); + if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q) + && styleList->hasFocus()) + styleEdit->selectAll(); + + style = s; + + updateSizes(); +} + + +/*! + \internal +*/ + +void QFontDialogPrivate::_q_sizeHighlighted(int index) +{ + Q_Q(QFontDialog); + QString s = sizeList->text(index); + sizeEdit->setText(s); + if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, 0, q) + && sizeEdit->hasFocus()) + sizeEdit->selectAll(); + + size = s.toInt(); + _q_updateSample(); +} + +/*! + \internal + This slot is called if the user changes the font size. + The size is passed in the \a s argument as a \e string. +*/ + +void QFontDialogPrivate::_q_sizeChanged(const QString &s) +{ + // no need to check if the conversion is valid, since we have an QIntValidator in the size edit + int size = s.toInt(); + if (this->size == size) + return; + + this->size = size; + if (sizeList->count() != 0) { + int i; + for (i = 0; i < sizeList->count() - 1; i++) { + if (sizeList->text(i).toInt() >= this->size) + break; + } + sizeList->blockSignals(true); + sizeList->setCurrentItem(i); + sizeList->blockSignals(false); + } + _q_updateSample(); +} + +void QFontDialogPrivate::retranslateStrings() +{ + familyAccel->setText(QFontDialog::tr("&Font")); + styleAccel->setText(QFontDialog::tr("Font st&yle")); + sizeAccel->setText(QFontDialog::tr("&Size")); + effects->setTitle(QFontDialog::tr("Effects")); + strikeout->setText(QFontDialog::tr("Stri&keout")); + underline->setText(QFontDialog::tr("&Underline")); + sample->setTitle(QFontDialog::tr("Sample")); + writingSystemAccel->setText(QFontDialog::tr("Wr&iting System")); +} + +/*! + \reimp +*/ +void QFontDialog::changeEvent(QEvent *e) +{ + Q_D(QFontDialog); + if (e->type() == QEvent::LanguageChange) { + d->retranslateStrings(); + } + QDialog::changeEvent(e); +} + +/*! + \since 4.5 + + \property QFontDialog::currentFont + \brief the current font of the dialog. +*/ + +/*! + \since 4.5 + + Sets the font highlighted in the QFontDialog to the given \a font. + + \sa selectedFont() +*/ +void QFontDialog::setCurrentFont(const QFont &font) +{ + Q_D(QFontDialog); + d->family = font.family(); + d->style = d->fdb.styleString(font); + d->size = font.pointSize(); + if (d->size == -1) { + QFontInfo fi(font); + d->size = fi.pointSize(); + } + d->strikeout->setChecked(font.strikeOut()); + d->underline->setChecked(font.underline()); + d->updateFamilies(); + +#ifdef Q_WS_MAC + if (d->delegate) + QFontDialogPrivate::setFont(d->delegate, font); +#endif +} + +/*! + \since 4.5 + + Returns the current font. + + \sa selectedFont() +*/ +QFont QFontDialog::currentFont() const +{ + Q_D(const QFontDialog); + return d->sampleEdit->font(); +} + +/*! + Returns the font that the user selected by clicking the \gui{OK} + or equivalent button. + + \note This font is not always the same as the font held by the + \l currentFont property since the user can choose different fonts + before finally selecting the one to use. +*/ +QFont QFontDialog::selectedFont() const +{ + Q_D(const QFontDialog); + return d->selectedFont; +} + +/*! + \enum QFontDialog::FontDialogOption + \since 4.5 + + This enum specifies various options that affect the look and feel + of a font dialog. + + \value NoButtons Don't display \gui{OK} and \gui{Cancel} buttons. (Useful for "live dialogs".) + \value DontUseNativeDialog Use Qt's standard font dialog on the Mac instead of Apple's + native font panel. (Currently, the native dialog is never used, + but this is likely to change in future Qt releases.) + + \sa options, setOption(), testOption() +*/ + +/*! + Sets the given \a option to be enabled if \a on is true; + otherwise, clears the given \a option. + + \sa options, testOption() +*/ +void QFontDialog::setOption(FontDialogOption option, bool on) +{ + Q_D(QFontDialog); + if (!(d->opts & option) != !on) + setOptions(d->opts ^ option); +} + +/*! + Returns true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption() +*/ +bool QFontDialog::testOption(FontDialogOption option) const +{ + Q_D(const QFontDialog); + return (d->opts & option) != 0; +} + +/*! + \property QFontDialog::options + \brief the various options that affect the look and feel of the dialog + \since 4.5 + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the + dialog is visible is not guaranteed to have an immediate effect on the + dialog (depending on the option and on the platform). + + \sa setOption(), testOption() +*/ +void QFontDialog::setOptions(FontDialogOptions options) +{ + Q_D(QFontDialog); + + FontDialogOptions changed = (options ^ d->opts); + if (!changed) + return; + + d->opts = options; + d->buttonBox->setVisible(!(options & NoButtons)); +} + +QFontDialog::FontDialogOptions QFontDialog::options() const +{ + Q_D(const QFontDialog); + return d->opts; +} + +#ifdef Q_WS_MAC +// can only have one Cocoa font panel active +bool QFontDialogPrivate::sharedFontPanelAvailable = true; +#endif + +/*! + \since 4.5 + \overload + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QFontDialog::open(QObject *receiver, const char *member) +{ + Q_D(QFontDialog); + connect(this, SIGNAL(fontSelected(const QFont&)), receiver, member); + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +/*! + \since 4.5 + + \fn void QFontDialog::currentFontChanged(const QFont &font) + + This signal is emitted when the current font is changed. The new font is + specified in \a font. + + The signal is emitted while a user is selecting a font. Ultimately, the + chosen font may differ from the font currently selected. + + \sa currentFont, fontSelected(), selectedFont() +*/ + +/*! + \since 4.5 + + \fn void QFontDialog::fontSelected(const QFont &font) + + This signal is emitted when a font has been selected. The selected font is + specified in \a font. + + The signal is only emitted when a user has chosen the final font to be + used. It is not emitted while the user is changing the current font in the + font dialog. + + \sa selectedFont(), currentFontChanged(), currentFont +*/ + +/*! + \reimp +*/ +void QFontDialog::setVisible(bool visible) +{ + Q_D(QFontDialog); + if (visible) + d->selectedFont = QFont(); + +#if defined(Q_WS_MAC) + bool isCurrentlyVisible = (isVisible() || d->delegate); + + if (!visible == !isCurrentlyVisible) + return; + + if (visible) { + if (!(d->opts & DontUseNativeDialog) && QFontDialogPrivate::sharedFontPanelAvailable) { + d->delegate = QFontDialogPrivate::openCocoaFontPanel( + currentFont(), parentWidget(), windowTitle(), options(), d); + QFontDialogPrivate::sharedFontPanelAvailable = false; + return; + } + + setWindowFlags(windowModality() == Qt::WindowModal ? Qt::Sheet : DefaultWindowFlags); + } else { + if (d->delegate) { + QFontDialogPrivate::closeCocoaFontPanel(d->delegate); + d->delegate = 0; + QFontDialogPrivate::sharedFontPanelAvailable = true; + return; + } + } +#endif + + QDialog::setVisible(visible); +} + +/*! + Closes the dialog and sets its result code to \a result. If this dialog + is shown with exec(), done() causes the local event loop to finish, + and exec() to return \a result. + + \sa QDialog::done() +*/ +void QFontDialog::done(int result) +{ + Q_D(QFontDialog); + QDialog::done(result); + if (result == Accepted) { + d->selectedFont = currentFont(); + emit fontSelected(d->selectedFont); + } else { + d->selectedFont = QFont(); + } + if (d->receiverToDisconnectOnClose) { + disconnect(this, SIGNAL(fontSelected(const QFont&)), + d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); + d->receiverToDisconnectOnClose = 0; + } + d->memberToDisconnectOnClose.clear(); +} + +/*! + \fn QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget* parent, const char* name) + \since 4.5 + + Call getFont(\a ok, \a initial, \a parent) instead. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFontDialog constructors. + + The \a name parameter is ignored. +*/ + +/*! + \fn QFont QFontDialog::getFont(bool *ok, QWidget* parent, const char* name) + + Call getFont(\a ok, \a parent) instead. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QFontDialog constructors. + + The \a name parameter is ignored. +*/ + +QT_END_NAMESPACE + +#include "qfontdialog.moc" +#include "moc_qfontdialog.cpp" + +#endif // QT_NO_FONTDIALOG diff --git a/src/gui/dialogs/qfontdialog.h b/src/gui/dialogs/qfontdialog.h new file mode 100644 index 0000000000..303241c664 --- /dev/null +++ b/src/gui/dialogs/qfontdialog.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTDIALOG_H +#define QFONTDIALOG_H + +#include <QtGui/qwindowdefs.h> +#include <QtGui/qdialog.h> +#include <QtGui/qfont.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_FONTDIALOG + +class QFontDialogPrivate; + +class Q_GUI_EXPORT QFontDialog : public QDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QFontDialog) + Q_ENUMS(FontDialogOption) + Q_PROPERTY(QFont currentFont READ currentFont WRITE setCurrentFont NOTIFY currentFontChanged) + Q_PROPERTY(FontDialogOptions options READ options WRITE setOptions) + +public: + enum FontDialogOption { + NoButtons = 0x00000001, + DontUseNativeDialog = 0x00000002 + }; + + Q_DECLARE_FLAGS(FontDialogOptions, FontDialogOption) + + explicit QFontDialog(QWidget *parent = 0); + explicit QFontDialog(const QFont &initial, QWidget *parent = 0); + ~QFontDialog(); + + void setCurrentFont(const QFont &font); + QFont currentFont() const; + + QFont selectedFont() const; + + void setOption(FontDialogOption option, bool on = true); + bool testOption(FontDialogOption option) const; + void setOptions(FontDialogOptions options); + FontDialogOptions options() const; + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + + void setVisible(bool visible); + + // ### Qt 5: merge overloads + static QFont getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title, + FontDialogOptions options); + static QFont getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title); + static QFont getFont(bool *ok, const QFont &initial, QWidget *parent = 0); + static QFont getFont(bool *ok, QWidget *parent = 0); + +#ifdef QT3_SUPPORT + static QFont getFont(bool *ok, const QFont &initial, QWidget *parent, const char *name) + { Q_UNUSED(name); return getFont(ok, initial, parent); } + static QFont getFont(bool *ok, QWidget *parent, const char *name) + { Q_UNUSED(name); return getFont(ok, parent); } +#endif + +Q_SIGNALS: + void currentFontChanged(const QFont &font); + void fontSelected(const QFont &font); + +protected: + void changeEvent(QEvent *event); + void done(int result); + +private: + // ### Qt 5: make protected + bool eventFilter(QObject *object, QEvent *event); + + Q_DISABLE_COPY(QFontDialog) + + Q_PRIVATE_SLOT(d_func(), void _q_sizeChanged(const QString &)) + Q_PRIVATE_SLOT(d_func(), void _q_familyHighlighted(int)) + Q_PRIVATE_SLOT(d_func(), void _q_writingSystemHighlighted(int)) + Q_PRIVATE_SLOT(d_func(), void _q_styleHighlighted(int)) + Q_PRIVATE_SLOT(d_func(), void _q_sizeHighlighted(int)) + Q_PRIVATE_SLOT(d_func(), void _q_updateSample()) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QFontDialog::FontDialogOptions) + +#endif // QT_NO_FONTDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFONTDIALOG_H diff --git a/src/gui/dialogs/qfontdialog_mac.mm b/src/gui/dialogs/qfontdialog_mac.mm new file mode 100644 index 0000000000..d6ddfa3567 --- /dev/null +++ b/src/gui/dialogs/qfontdialog_mac.mm @@ -0,0 +1,625 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfontdialog_p.h" +#if !defined(QT_NO_FONTDIALOG) && defined(Q_WS_MAC) +#include <qapplication.h> +#include <qdialogbuttonbox.h> +#include <qlineedit.h> +#include <private/qapplication_p.h> +#include <private/qfont_p.h> +#include <private/qfontengine_p.h> +#include <private/qt_mac_p.h> +#include <qdebug.h> +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> + +#if !CGFLOAT_DEFINED +typedef float CGFloat; // Should only not be defined on 32-bit platforms +#endif + +QT_USE_NAMESPACE + +// should a priori be kept in sync with qcolordialog_mac.mm +const CGFloat ButtonMinWidth = 78.0; +const CGFloat ButtonMinHeight = 32.0; +const CGFloat ButtonSpacing = 0.0; +const CGFloat ButtonTopMargin = 0.0; +const CGFloat ButtonBottomMargin = 7.0; +const CGFloat ButtonSideMargin = 9.0; + +// looks better with some margins +const CGFloat DialogTopMargin = 7.0; +const CGFloat DialogSideMargin = 9.0; + +const int StyleMask = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask; + +@class QCocoaFontPanelDelegate; + + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 + +@protocol NSWindowDelegate <NSObject> @end + +#endif + +@interface QCocoaFontPanelDelegate : NSObject <NSWindowDelegate> { + NSFontPanel *mFontPanel; + NSView *mStolenContentView; + NSButton *mOkButton; + NSButton *mCancelButton; + QFontDialogPrivate *mPriv; + NSFont *mCocoaFont; + QFont *mQtFont; + BOOL mPanelHackedWithButtons; + CGFloat mDialogExtraWidth; + CGFloat mDialogExtraHeight; + NSModalSession mModalSession; +} +- (id)initWithFontPanel:(NSFontPanel *)panel + stolenContentView:(NSView *)stolenContentView + okButton:(NSButton *)okButton + cancelButton:(NSButton *)cancelButton + priv:(QFontDialogPrivate *)priv + extraWidth:(CGFloat)extraWidth + extraHeight:(CGFloat)extraHeight; +- (void)changeFont:(id)sender; +- (void)changeAttributes:(id)sender; +- (void)setModalSession:(NSModalSession)session; +- (BOOL)windowShouldClose:(id)window; +- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize; +- (void)relayout; +- (void)relayoutToContentSize:(NSSize)frameSize; +- (void)onOkClicked; +- (void)onCancelClicked; +- (NSFontPanel *)fontPanel; +- (NSWindow *)actualPanel; +- (NSSize)dialogExtraSize; +- (void)setQtFont:(const QFont &)newFont; +- (QFont)qtFont; +- (void)finishOffWithCode:(NSInteger)result; +- (void)cleanUpAfterMyself; +@end + +@implementation QCocoaFontPanelDelegate +- (id)initWithFontPanel:(NSFontPanel *)panel + stolenContentView:(NSView *)stolenContentView + okButton:(NSButton *)okButton + cancelButton:(NSButton *)cancelButton + priv:(QFontDialogPrivate *)priv + extraWidth:(CGFloat)extraWidth + extraHeight:(CGFloat)extraHeight +{ + self = [super init]; + mFontPanel = panel; + mStolenContentView = stolenContentView; + mOkButton = okButton; + mCancelButton = cancelButton; + mPriv = priv; + mCocoaFont = 0; + mPanelHackedWithButtons = (okButton != 0); + mDialogExtraWidth = extraWidth; + mDialogExtraHeight = extraHeight; + mModalSession = 0; + + if (mPanelHackedWithButtons) { + [self relayout]; + + [okButton setAction:@selector(onOkClicked)]; + [okButton setTarget:self]; + + [cancelButton setAction:@selector(onCancelClicked)]; + [cancelButton setTarget:self]; + } + mQtFont = new QFont(); + return self; +} + +- (void)dealloc +{ + if (mCocoaFont) + [mCocoaFont release]; + delete mQtFont; + [super dealloc]; +} + +- (void)changeFont:(id)sender +{ + Q_UNUSED(sender); + + QFont newFont; + + if (mCocoaFont) + [mCocoaFont autorelease]; + NSFont *dummyFont = [NSFont userFontOfSize:12.0]; + mCocoaFont = [sender convertFont:dummyFont]; + if (mCocoaFont) { + [mCocoaFont retain]; + + int pSize = qRound([mCocoaFont pointSize]); + QString family(QCFString::toQString(reinterpret_cast<CFStringRef>([mCocoaFont familyName]))); + QString typeface(QCFString::toQString(reinterpret_cast<CFStringRef>([mCocoaFont fontName]))); +// qDebug() << "original family" << family << "typeface" << typeface << "psize" << pSize; + int hyphenPos = typeface.indexOf(QLatin1Char('-')); + if (hyphenPos != -1) { + typeface.remove(0, hyphenPos + 1); + } else { + typeface = QLatin1String("Normal"); + } +// qDebug() << " massaged family" << family << "typeface" << typeface << "psize" << pSize; + newFont = QFontDatabase().font(family, typeface, pSize); + newFont.setUnderline(mQtFont->underline()); + newFont.setStrikeOut(mQtFont->strikeOut()); + } + + [self setQtFont:newFont]; + if (mPriv) + mPriv->updateSampleFont(*mQtFont); +} + +- (void)changeAttributes:(id)sender +{ + NSDictionary *dummyAttribs = [NSDictionary dictionary]; + NSDictionary *attribs = [sender convertAttributes:dummyAttribs]; + +#ifdef QT_MAC_USE_COCOA + for (id key in attribs) { +#else + NSEnumerator *enumerator = [attribs keyEnumerator]; + id key; + while((key = [enumerator nextObject])) { +#endif + NSNumber *number = static_cast<NSNumber *>([attribs objectForKey:key]); + if ([key isEqual:NSUnderlineStyleAttributeName]) { + mQtFont->setUnderline([number intValue] != NSUnderlineStyleNone); + } else if ([key isEqual:NSStrikethroughStyleAttributeName]) { + mQtFont->setStrikeOut([number intValue] != NSUnderlineStyleNone); + } + } + + if (mPriv) + mPriv->updateSampleFont(*mQtFont); +} + +- (void)setModalSession:(NSModalSession)session +{ + Q_ASSERT(!mModalSession); + mModalSession = session; +} + +- (BOOL)windowShouldClose:(id)window +{ + Q_UNUSED(window); + if (mPanelHackedWithButtons) { + [self onCancelClicked]; + } else { + [self finishOffWithCode:NSCancelButton]; + } + return true; +} + +- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize +{ + if (mFontPanel == window) { + proposedFrameSize = [static_cast<id <NSWindowDelegate> >(mFontPanel) windowWillResize:mFontPanel toSize:proposedFrameSize]; + } else { + /* + Ugly hack: NSFontPanel rearranges the layout of its main + component in windowWillResize:toSize:. So we temporarily + restore the stolen content view to its rightful owner, + call windowWillResize:toSize:, and steal the content view + again. + */ + [mStolenContentView removeFromSuperview]; + [mFontPanel setContentView:mStolenContentView]; + NSSize extraSize = [self dialogExtraSize]; + proposedFrameSize.width -= extraSize.width; + proposedFrameSize.height -= extraSize.height; + proposedFrameSize = [static_cast<id <NSWindowDelegate> >(mFontPanel) windowWillResize:mFontPanel toSize:proposedFrameSize]; + NSRect frameRect = { { 0.0, 0.0 }, proposedFrameSize }; + [mFontPanel setFrame:frameRect display:NO]; + [mFontPanel setContentView:0]; + [[window contentView] addSubview:mStolenContentView]; + proposedFrameSize.width += extraSize.width; + proposedFrameSize.height += extraSize.height; + } + if (mPanelHackedWithButtons) { + NSRect frameRect = { { 0.0, 0.0 }, proposedFrameSize }; + NSRect contentRect = [NSWindow contentRectForFrameRect:frameRect styleMask:[window styleMask]]; + [self relayoutToContentSize:contentRect.size]; + } + return proposedFrameSize; +} + +- (void)relayout +{ + [self relayoutToContentSize:[[mStolenContentView superview] frame].size]; +} + +- (void)relayoutToContentSize:(NSSize)frameSize; +{ + Q_ASSERT(mPanelHackedWithButtons); + + [mOkButton sizeToFit]; + NSSize okSizeHint = [mOkButton frame].size; + + [mCancelButton sizeToFit]; + NSSize cancelSizeHint = [mCancelButton frame].size; + + const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth, + qMax(okSizeHint.width, cancelSizeHint.width)), + CGFloat((frameSize.width - 2.0 * ButtonSideMargin + - ButtonSpacing) * 0.5)); + const CGFloat ButtonHeight = qMax(ButtonMinHeight, + qMax(okSizeHint.height, cancelSizeHint.height)); + + const CGFloat X = DialogSideMargin; + const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin; + + NSRect okRect = { { frameSize.width - ButtonSideMargin - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mOkButton setFrame:okRect]; + [mOkButton setNeedsDisplay:YES]; + + NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mCancelButton setFrame:cancelRect]; + [mCancelButton setNeedsDisplay:YES]; + + NSRect stolenCVRect = { { X, Y }, + { frameSize.width - X - X, frameSize.height - Y - DialogTopMargin } }; + [mStolenContentView setFrame:stolenCVRect]; + [mStolenContentView setNeedsDisplay:YES]; + + [[mStolenContentView superview] setNeedsDisplay:YES]; +} + +- (void)onOkClicked +{ + Q_ASSERT(mPanelHackedWithButtons); + [[mStolenContentView window] close]; + [self finishOffWithCode:NSOKButton]; +} + +- (void)onCancelClicked +{ + Q_ASSERT(mPanelHackedWithButtons); + [[mStolenContentView window] close]; + [self finishOffWithCode:NSCancelButton]; +} + +- (NSFontPanel *)fontPanel +{ + return mFontPanel; +} + +- (NSWindow *)actualPanel +{ + return [mStolenContentView window]; +} + +- (NSSize)dialogExtraSize +{ + // this must be recomputed each time, because sometimes the + // NSFontPanel has the NSDocModalWindowMask flag set, and sometimes + // not -- which affects the frame rect vs. content rect measurements + + // take the different frame rectangles into account for dialogExtra{Width,Height} + NSRect someRect = { { 0.0, 0.0 }, { 100000.0, 100000.0 } }; + NSRect sharedFontPanelContentRect = [mFontPanel contentRectForFrameRect:someRect]; + NSRect ourPanelContentRect = [NSWindow contentRectForFrameRect:someRect styleMask:StyleMask]; + + NSSize result = { mDialogExtraWidth, mDialogExtraHeight }; + result.width -= ourPanelContentRect.size.width - sharedFontPanelContentRect.size.width; + result.height -= ourPanelContentRect.size.height - sharedFontPanelContentRect.size.height; + return result; +} + +- (void)setQtFont:(const QFont &)newFont +{ + delete mQtFont; + mQtFont = new QFont(newFont); +} + +- (QFont)qtFont +{ + return *mQtFont; +} + +- (void)finishOffWithCode:(NSInteger)code +{ + if (mPriv) { + if (mModalSession) { + [NSApp endModalSession:mModalSession]; + mModalSession = 0; + } + + // temporary hack to work around bug in deleteLater() in Qt/Mac Cocoa +#if 1 + bool deleteDialog = mPriv->fontDialog()->testAttribute(Qt::WA_DeleteOnClose); + mPriv->fontDialog()->setAttribute(Qt::WA_DeleteOnClose, false); +#endif + mPriv->done((code == NSOKButton) ? QDialog::Accepted : QDialog::Rejected); +#if 1 + if (deleteDialog) + delete mPriv->fontDialog(); +#endif + } else { + [NSApp stopModalWithCode:code]; + } +} + +- (void)cleanUpAfterMyself +{ + if (mPanelHackedWithButtons) { + NSView *ourContentView = [mFontPanel contentView]; + + // return stolen stuff to its rightful owner + [mStolenContentView removeFromSuperview]; + [mFontPanel setContentView:mStolenContentView]; + + [mOkButton release]; + [mCancelButton release]; + [ourContentView release]; + } + [mFontPanel setDelegate:nil]; + [[NSFontManager sharedFontManager] setDelegate:nil]; +} +@end + +QT_BEGIN_NAMESPACE + +extern void macStartInterceptNSPanelCtor(); +extern void macStopInterceptNSPanelCtor(); +extern NSButton *macCreateButton(const char *text, NSView *superview); + +void *QFontDialogPrivate::openCocoaFontPanel(const QFont &initial, + QWidget *parent, const QString &title, QFontDialog::FontDialogOptions options, + QFontDialogPrivate *priv) +{ + Q_UNUSED(parent); // we would use the parent if only NSFontPanel could be a sheet + QMacCocoaAutoReleasePool pool; + + /* + The standard Cocoa font panel has no OK or Cancel button and + is created as a utility window. For strange reasons (which seem + to stem from the fact that the font panel is based on a NIB + file), the approach we use for the color panel doesn't work for + the font panel (and, inversely, the approach we use here doesn't + quite work for color panel, and crashed last time I tried). So + instead, we take the following steps: + + 1. Constructs a plain NSPanel that looks the way we want it + to look. Specifically, if the NoButtons option is off, we + construct a panel without the NSUtilityWindowMask flag + and with buttons (OK and Cancel). + + 2. Steal the content view from the shared NSFontPanel and + put it inside our new NSPanel's content view, together + with the OK and Cancel buttons. + + 3. Lay out the original content view and the buttons when + the font panel is shown and whenever it is resized. + + 4. Clean up after ourselves. + + PS. Some customization is also done in QCocoaApplication + validModesForFontPanel:. + */ + + Qt::WindowModality modality = Qt::ApplicationModal; + if (priv) + modality = priv->fontDialog()->windowModality(); + + bool needButtons = !(options & QFontDialog::NoButtons); + // don't need our own panel if the title bar isn't visible anyway (in a sheet) + bool needOwnPanel = (needButtons && modality != Qt::WindowModal); + + bool sharedFontPanelExisted = [NSFontPanel sharedFontPanelExists]; + NSFontPanel *sharedFontPanel = [NSFontPanel sharedFontPanel]; + [sharedFontPanel setHidesOnDeactivate:false]; + + // hack to ensure that QCocoaApplication's validModesForFontPanel: + // implementation is honored + if (!sharedFontPanelExisted && needOwnPanel) { + [sharedFontPanel makeKeyAndOrderFront:sharedFontPanel]; + [sharedFontPanel close]; + } + + NSPanel *ourPanel = 0; + NSView *stolenContentView = 0; + NSButton *okButton = 0; + NSButton *cancelButton = 0; + + CGFloat dialogExtraWidth = 0.0; + CGFloat dialogExtraHeight = 0.0; + + if (!needOwnPanel) { + // we can reuse the NSFontPanel unchanged + ourPanel = sharedFontPanel; + } else { + // compute dialogExtra{Width,Height} + dialogExtraWidth = 2.0 * DialogSideMargin; + dialogExtraHeight = DialogTopMargin + ButtonTopMargin + ButtonMinHeight + + ButtonBottomMargin; + + // compute initial contents rectangle + NSRect contentRect = [sharedFontPanel contentRectForFrameRect:[sharedFontPanel frame]]; + contentRect.size.width += dialogExtraWidth; + contentRect.size.height += dialogExtraHeight; + + // create the new panel + ourPanel = [[NSPanel alloc] initWithContentRect:contentRect + styleMask:StyleMask + backing:NSBackingStoreBuffered + defer:YES]; + [ourPanel setReleasedWhenClosed:YES]; + } + + stolenContentView = [sharedFontPanel contentView]; + + if (needButtons) { + // steal the font panel's contents view + [stolenContentView retain]; + [sharedFontPanel setContentView:0]; + + // create a new content view and add the stolen one as a subview + NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; + [ourContentView addSubview:stolenContentView]; + + // create OK and Cancel buttons and add these as subviews + okButton = macCreateButton("&OK", ourContentView); + cancelButton = macCreateButton("Cancel", ourContentView); + + [ourPanel setContentView:ourContentView]; + [ourPanel setDefaultButtonCell:[okButton cell]]; + } + + // create a delegate and set it + QCocoaFontPanelDelegate *delegate = + [[QCocoaFontPanelDelegate alloc] initWithFontPanel:sharedFontPanel + stolenContentView:stolenContentView + okButton:okButton + cancelButton:cancelButton + priv:priv + extraWidth:dialogExtraWidth + extraHeight:dialogExtraHeight]; + [ourPanel setDelegate:delegate]; + [[NSFontManager sharedFontManager] setDelegate:delegate]; + setFont(delegate, initial); + + // hack to get correct initial layout + NSRect frameRect = [ourPanel frame]; + frameRect.size.width += 1.0; + [ourPanel setFrame:frameRect display:NO]; + frameRect.size.width -= 1.0; + frameRect.size = [delegate windowWillResize:ourPanel toSize:frameRect.size]; + [ourPanel setFrame:frameRect display:NO]; + [ourPanel center]; + + [ourPanel setTitle:(NSString*)(CFStringRef)QCFString(title)]; + + if (priv) { + switch (modality) { + case Qt::WindowModal: + if (parent) { +#ifndef QT_MAC_USE_COCOA + WindowRef hiwindowRef = qt_mac_window_for(parent); + NSWindow *window = + [[NSWindow alloc] initWithWindowRef:hiwindowRef]; + // Cocoa docs say I should retain the Carbon ref. + CFRetain(hiwindowRef); +#else + NSWindow *window = qt_mac_window_for(parent); +#endif + [NSApp beginSheet:ourPanel + modalForWindow:window + modalDelegate:0 + didEndSelector:0 + contextInfo:0]; +#ifndef QT_MAC_USE_COCOA + [window release]; +#endif + break; + } + // fallthrough + case Qt::ApplicationModal: + [delegate setModalSession:[NSApp beginModalSessionForWindow:ourPanel]]; + break; + default: + [ourPanel makeKeyAndOrderFront:ourPanel]; + } + } + + return delegate; +} + +void QFontDialogPrivate::closeCocoaFontPanel(void *delegate) +{ + QCocoaFontPanelDelegate *theDelegate = static_cast<QCocoaFontPanelDelegate *>(delegate); + NSWindow *ourPanel = [theDelegate actualPanel]; + [ourPanel close]; + [theDelegate cleanUpAfterMyself]; + [theDelegate autorelease]; +} + +QFont QFontDialogPrivate::execCocoaFontPanel(bool *ok, const QFont &initial, + QWidget *parent, const QString &title, QFontDialog::FontDialogOptions options) +{ + QMacCocoaAutoReleasePool pool; + QCocoaFontPanelDelegate *delegate = + static_cast<QCocoaFontPanelDelegate *>( + openCocoaFontPanel(initial, parent, title, options)); + NSWindow *ourPanel = [delegate actualPanel]; + [ourPanel retain]; + int rval = [NSApp runModalForWindow:ourPanel]; + QFont font([delegate qtFont]); + [ourPanel release]; + [delegate cleanUpAfterMyself]; + [delegate release]; + bool isOk = ((options & QFontDialog::NoButtons) || rval == NSOKButton); + if (ok) + *ok = isOk; + if (isOk) { + return font; + } else { + return initial; + } +} + +void QFontDialogPrivate::setFont(void * delegate, const QFont &font) +{ + QFontEngine *fe = font.d->engineForScript(QUnicodeTables::Common); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (qstrcmp(fe->name(), "CoreText") == 0) { + const NSFont *nsFont = reinterpret_cast<const NSFont *>(static_cast<QCoreTextFontEngineMulti *>(fe)->ctfont); + [[NSFontManager sharedFontManager] setSelectedFont:nsFont isMultiple:NO]; + } +#endif + [static_cast<QCocoaFontPanelDelegate *>(delegate) setQtFont:font]; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/dialogs/qfontdialog_p.h b/src/gui/dialogs/qfontdialog_p.h new file mode 100644 index 0000000000..3a69949d80 --- /dev/null +++ b/src/gui/dialogs/qfontdialog_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTDIALOG_P_H +#define QFONTDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +// + +#include "private/qdialog_p.h" +#include "qfontdatabase.h" +#include "qfontdialog.h" + +#ifndef QT_NO_FONTDIALOG + +QT_BEGIN_NAMESPACE + +class QBoxLayout; +class QCheckBox; +class QComboBox; +class QDialogButtonBox; +class QFontListView; +class QGroupBox; +class QLabel; +class QLineEdit; + +class QFontDialogPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QFontDialog) + +public: + inline QFontDialogPrivate() + : writingSystem(QFontDatabase::Any) + { } + + void updateFamilies(); + void updateStyles(); + void updateSizes(); + + static QFont getFont(bool *ok, const QFont &initial, QWidget *parent, + const QString &title, QFontDialog::FontDialogOptions options); + + void init(); + void _q_sizeChanged(const QString &); + void _q_familyHighlighted(int); + void _q_writingSystemHighlighted(int); + void _q_styleHighlighted(int); + void _q_sizeHighlighted(int); + void _q_updateSample(); + void updateSampleFont(const QFont &newFont); + void retranslateStrings(); + + QLabel *familyAccel; + QLineEdit *familyEdit; + QFontListView *familyList; + + QLabel *styleAccel; + QLineEdit *styleEdit; + QFontListView *styleList; + + QLabel *sizeAccel; + QLineEdit *sizeEdit; + QFontListView *sizeList; + + QGroupBox *effects; + QCheckBox *strikeout; + QCheckBox *underline; + QComboBox *color; + + QGroupBox *sample; + QLineEdit *sampleEdit; + + QLabel *writingSystemAccel; + QComboBox *writingSystemCombo; + + QBoxLayout *buttonLayout; + QBoxLayout *effectsLayout; + QBoxLayout *sampleLayout; + QBoxLayout *sampleEditLayout; + + QDialogButtonBox *buttonBox; + + QFontDatabase fdb; + QString family; + QFontDatabase::WritingSystem writingSystem; + QString style; + int size; + bool smoothScalable; + QFont selectedFont; + QFontDialog::FontDialogOptions opts; + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; + +#ifdef Q_WS_MAC + static void *openCocoaFontPanel(const QFont &initial, + QWidget *parent, const QString &title, + QFontDialog::FontDialogOptions options, + QFontDialogPrivate *priv = 0); + static void closeCocoaFontPanel(void *delegate); + static QFont execCocoaFontPanel(bool *ok, const QFont &initial, QWidget *parent, + const QString &title, QFontDialog::FontDialogOptions options); + static void setFont(void *delegate, const QFont &font); + + inline void done(int result) { q_func()->done(result); } + inline QFontDialog *fontDialog() { return q_func(); } + + void *delegate; + + static bool sharedFontPanelAvailable; +#endif +}; + +#endif // QT_NO_FONTDIALOG + +QT_END_NAMESPACE + +#endif // QFONTDIALOG_P_H diff --git a/src/gui/dialogs/qinputdialog.cpp b/src/gui/dialogs/qinputdialog.cpp new file mode 100644 index 0000000000..b63c8ee446 --- /dev/null +++ b/src/gui/dialogs/qinputdialog.cpp @@ -0,0 +1,1429 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qinputdialog.h" + +#ifndef QT_NO_INPUTDIALOG + +#include "qapplication.h" +#include "qcombobox.h" +#include "qdialogbuttonbox.h" +#include "qlabel.h" +#include "qlayout.h" +#include "qlineedit.h" +#include "qlistwidget.h" +#include "qpushbutton.h" +#include "qspinbox.h" +#include "qstackedlayout.h" +#include "qvalidator.h" +#include "qevent.h" +#include "qdialog_p.h" + +QT_USE_NAMESPACE + +static const char *signalForMember(const char *member) +{ + static const int NumCandidates = 4; + static const char * const candidateSignals[NumCandidates] = { + SIGNAL(textValueSelected(QString)), + SIGNAL(intValueSelected(int)), + SIGNAL(doubleValueSelected(double)), + SIGNAL(accepted()) + }; + + QByteArray normalizedMember(QMetaObject::normalizedSignature(member)); + + int i = 0; + while (i < NumCandidates - 1) { // sic + if (QMetaObject::checkConnectArgs(candidateSignals[i], normalizedMember)) + break; + ++i; + } + return candidateSignals[i]; +} + +QT_BEGIN_NAMESPACE + +/* + These internal classes add extra validation to QSpinBox and QDoubleSpinBox by emitting + textChanged(bool) after events that may potentially change the visible text. Return or + Enter key presses are not propagated if the visible text is invalid. Instead, the visible + text is modified to the last valid value. +*/ +class QInputDialogSpinBox : public QSpinBox +{ + Q_OBJECT + +public: + QInputDialogSpinBox(QWidget *parent) + : QSpinBox(parent) { + connect(lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(notifyTextChanged())); + connect(this, SIGNAL(editingFinished()), this, SLOT(notifyTextChanged())); + } + +signals: + void textChanged(bool); + +private slots: + void notifyTextChanged() { emit textChanged(hasAcceptableInput()); } + +private: + void keyPressEvent(QKeyEvent *event) { + if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && !hasAcceptableInput()) { +#ifndef QT_NO_PROPERTIES + setProperty("value", property("value")); +#endif + } else { + QSpinBox::keyPressEvent(event); + } + notifyTextChanged(); + } + + void mousePressEvent(QMouseEvent *event) { + QSpinBox::mousePressEvent(event); + notifyTextChanged(); + } +}; + +class QInputDialogDoubleSpinBox : public QDoubleSpinBox +{ + Q_OBJECT + +public: + QInputDialogDoubleSpinBox(QWidget *parent = 0) + : QDoubleSpinBox(parent) { + connect(lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(notifyTextChanged())); + connect(this, SIGNAL(editingFinished()), this, SLOT(notifyTextChanged())); + } + +signals: + void textChanged(bool); + +private slots: + void notifyTextChanged() { emit textChanged(hasAcceptableInput()); } + +private: + void keyPressEvent(QKeyEvent *event) { + if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && !hasAcceptableInput()) { +#ifndef QT_NO_PROPERTIES + setProperty("value", property("value")); +#endif + } else { + QDoubleSpinBox::keyPressEvent(event); + } + notifyTextChanged(); + } + + void mousePressEvent(QMouseEvent *event) { + QDoubleSpinBox::mousePressEvent(event); + notifyTextChanged(); + } +}; + +QT_BEGIN_INCLUDE_NAMESPACE +#include "qinputdialog.moc" +QT_END_INCLUDE_NAMESPACE + +class QInputDialogPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QInputDialog) + +public: + QInputDialogPrivate(); + + void ensureLayout(); + void ensureLineEdit(); + void ensureComboBox(); + void ensureListView(); + void ensureIntSpinBox(); + void ensureDoubleSpinBox(); + void ensureEnabledConnection(QAbstractSpinBox *spinBox); + void setInputWidget(QWidget *widget); + void chooseRightTextInputWidget(); + void setComboBoxText(const QString &text); + void setListViewText(const QString &text); + QString listViewText() const; + void ensureLayout() const { const_cast<QInputDialogPrivate *>(this)->ensureLayout(); } + bool useComboBoxOrListView() const { return comboBox && comboBox->count() > 0; } + void _q_textChanged(const QString &text); + void _q_currentRowChanged(const QModelIndex &newIndex, const QModelIndex &oldIndex); + + mutable QLabel *label; + mutable QDialogButtonBox *buttonBox; + mutable QLineEdit *lineEdit; + mutable QSpinBox *intSpinBox; + mutable QDoubleSpinBox *doubleSpinBox; + mutable QComboBox *comboBox; + mutable QListView *listView; + mutable QWidget *inputWidget; + mutable QVBoxLayout *mainLayout; + QInputDialog::InputDialogOptions opts; + QString textValue; + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; +}; + +QInputDialogPrivate::QInputDialogPrivate() + : label(0), buttonBox(0), lineEdit(0), intSpinBox(0), doubleSpinBox(0), comboBox(0), listView(0), + inputWidget(0), mainLayout(0) +{ +} + +void QInputDialogPrivate::ensureLayout() +{ + Q_Q(QInputDialog); + + if (mainLayout) + return; + + if (!inputWidget) { + ensureLineEdit(); + inputWidget = lineEdit; + } + + if (!label) + label = new QLabel(QInputDialog::tr("Enter a value:"), q); +#ifndef QT_NO_SHORTCUT + label->setBuddy(inputWidget); +#endif + label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, q); + QObject::connect(buttonBox, SIGNAL(accepted()), q, SLOT(accept())); + QObject::connect(buttonBox, SIGNAL(rejected()), q, SLOT(reject())); + + mainLayout = new QVBoxLayout(q); + mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + mainLayout->addWidget(label); + mainLayout->addWidget(inputWidget); + mainLayout->addWidget(buttonBox); + ensureEnabledConnection(qobject_cast<QAbstractSpinBox *>(inputWidget)); + inputWidget->show(); +} + +void QInputDialogPrivate::ensureLineEdit() +{ + Q_Q(QInputDialog); + if (!lineEdit) { + lineEdit = new QLineEdit(q); + lineEdit->hide(); + QObject::connect(lineEdit, SIGNAL(textChanged(const QString&)), + q, SLOT(_q_textChanged(const QString&))); + } +} + +void QInputDialogPrivate::ensureComboBox() +{ + Q_Q(QInputDialog); + if (!comboBox) { + comboBox = new QComboBox(q); + comboBox->hide(); + QObject::connect(comboBox, SIGNAL(editTextChanged(const QString&)), + q, SLOT(_q_textChanged(const QString&))); + QObject::connect(comboBox, SIGNAL(currentIndexChanged(const QString&)), + q, SLOT(_q_textChanged(const QString&))); + } +} + +void QInputDialogPrivate::ensureListView() +{ + Q_Q(QInputDialog); + if (!listView) { + ensureComboBox(); + + listView = new QListView(q); + listView->hide(); + listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + listView->setSelectionMode(QAbstractItemView::SingleSelection); + listView->setModel(comboBox->model()); + listView->setCurrentIndex(QModelIndex()); // ### + QObject::connect(listView->selectionModel(), + SIGNAL(currentRowChanged(const QModelIndex&, const QModelIndex&)), + q, SLOT(_q_currentRowChanged(const QModelIndex&, const QModelIndex&))); + } +} + +void QInputDialogPrivate::ensureIntSpinBox() +{ + Q_Q(QInputDialog); + if (!intSpinBox) { + intSpinBox = new QInputDialogSpinBox(q); + intSpinBox->hide(); + QObject::connect(intSpinBox, SIGNAL(valueChanged(int)), + q, SIGNAL(intValueChanged(int))); + } +} + +void QInputDialogPrivate::ensureDoubleSpinBox() +{ + Q_Q(QInputDialog); + if (!doubleSpinBox) { + doubleSpinBox = new QInputDialogDoubleSpinBox(q); + doubleSpinBox->hide(); + QObject::connect(doubleSpinBox, SIGNAL(valueChanged(double)), + q, SIGNAL(doubleValueChanged(double))); + } +} + +void QInputDialogPrivate::ensureEnabledConnection(QAbstractSpinBox *spinBox) +{ + if (spinBox) { + QAbstractButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + QObject::disconnect(spinBox, SIGNAL(textChanged(bool)), okButton, SLOT(setEnabled(bool))); + QObject::connect(spinBox, SIGNAL(textChanged(bool)), okButton, SLOT(setEnabled(bool))); + } +} + +void QInputDialogPrivate::setInputWidget(QWidget *widget) +{ + Q_ASSERT(widget); + if (inputWidget == widget) + return; + + if (mainLayout) { + Q_ASSERT(inputWidget); + mainLayout->removeWidget(inputWidget); + inputWidget->hide(); + mainLayout->insertWidget(1, widget); + widget->show(); + + // disconnect old input widget + QAbstractButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(inputWidget)) + QObject::disconnect(spinBox, SIGNAL(textChanged(bool)), okButton, SLOT(setEnabled(bool))); + + // connect new input widget and update enabled state of OK button + QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(widget); + ensureEnabledConnection(spinBox); + okButton->setEnabled(!spinBox || spinBox->hasAcceptableInput()); + } + + inputWidget = widget; + + // synchronize the text shown in the new text editor with the current + // textValue + if (widget == lineEdit) { + lineEdit->setText(textValue); + } else if (widget == comboBox) { + setComboBoxText(textValue); + } else if (widget == listView) { + setListViewText(textValue); + ensureLayout(); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(listView->selectionModel()->hasSelection()); + } +} + +void QInputDialogPrivate::chooseRightTextInputWidget() +{ + QWidget *widget; + + if (useComboBoxOrListView()) { + if ((opts & QInputDialog::UseListViewForComboBoxItems) && !comboBox->isEditable()) { + ensureListView(); + widget = listView; + } else { + widget = comboBox; + } + } else { + ensureLineEdit(); + widget = lineEdit; + } + + setInputWidget(widget); + + if (inputWidget == comboBox) { + _q_textChanged(comboBox->currentText()); + } else if (inputWidget == listView) { + _q_textChanged(listViewText()); + } +} + +void QInputDialogPrivate::setComboBoxText(const QString &text) +{ + int index = comboBox->findText(text); + if (index != -1) { + comboBox->setCurrentIndex(index); + } else if (comboBox->isEditable()) { + comboBox->setEditText(text); + } +} + +void QInputDialogPrivate::setListViewText(const QString &text) +{ + int row = comboBox->findText(text); + if (row != -1) { + QModelIndex index(comboBox->model()->index(row, 0)); + listView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Clear + | QItemSelectionModel::SelectCurrent); + } +} + +QString QInputDialogPrivate::listViewText() const +{ + if (listView->selectionModel()->hasSelection()) { + int row = listView->selectionModel()->selectedRows().value(0).row(); + return comboBox->itemText(row); + } else { + return QString(); + } +} + +void QInputDialogPrivate::_q_textChanged(const QString &text) +{ + Q_Q(QInputDialog); + if (textValue != text) { + textValue = text; + emit q->textValueChanged(text); + } +} + +void QInputDialogPrivate::_q_currentRowChanged(const QModelIndex &newIndex, + const QModelIndex & /* oldIndex */) +{ + _q_textChanged(comboBox->model()->data(newIndex).toString()); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); +} + +/*! + \class QInputDialog + \brief The QInputDialog class provides a simple convenience dialog to get a single value from the user. + \ingroup dialogs + \mainclass + + The input value can be a string, a number or an item from a list. A + label must be set to tell the user what they should enter. + + Four static convenience functions are provided: + getText(), getInt(), getDouble(), and getItem(). All the + functions can be used in a similar way, for example: + + \snippet examples/dialogs/standarddialogs/dialog.cpp 3 + + The \c ok variable is set to true if the user clicks \gui OK; + otherwise it is set to false. + + \img inputdialogs.png Input Dialogs + + The \l{dialogs/standarddialogs}{Standard Dialogs} example shows + how to use QInputDialog as well as other built-in Qt dialogs. + + \sa QMessageBox, {Standard Dialogs Example} +*/ + +/*! + \enum QInputDialog::InputMode + \since 4.5 + + This enum describes the different modes of input that can be selected for the dialog. + + \value TextInput Used to input text strings. + \value IntInput Used to input integers. + \value DoubleInput Used to input floating point numbers with double precision accuracy. + + \sa inputMode +*/ + +/*! + \since 4.5 + + Constructs a new input dialog with the given \a parent and window \a flags. +*/ +QInputDialog::QInputDialog(QWidget *parent, Qt::WindowFlags flags) + : QDialog(*new QInputDialogPrivate, parent, flags) +{ +} + +/*! + \since 4.5 + + Destroys the input dialog. +*/ +QInputDialog::~QInputDialog() +{ +} + +/*! + \since 4.5 + + \property QInputDialog::inputMode + + \brief the mode used for input + + This property help determines which widget is used for entering input into the dialog. +*/ +void QInputDialog::setInputMode(InputMode mode) +{ + Q_D(QInputDialog); + + QWidget *widget; + + /* + Warning: Some functions in QInputDialog rely on implementation details + of the code below. Look for the comments that accompany the calls to + setInputMode() throughout this file before you change the code below. + */ + + switch (mode) { + case IntInput: + d->ensureIntSpinBox(); + widget = d->intSpinBox; + break; + case DoubleInput: + d->ensureDoubleSpinBox(); + widget = d->doubleSpinBox; + break; + default: + Q_ASSERT(mode == TextInput); + d->chooseRightTextInputWidget(); + return; + } + + d->setInputWidget(widget); +} + +QInputDialog::InputMode QInputDialog::inputMode() const +{ + Q_D(const QInputDialog); + + if (d->inputWidget) { + if (d->inputWidget == d->intSpinBox) { + return IntInput; + } else if (d->inputWidget == d->doubleSpinBox) { + return DoubleInput; + } + } + + return TextInput; +} + +/*! + \since 4.5 + + \property QInputDialog::labelText + + \brief the text to for the label to describe what needs to be input +*/ +void QInputDialog::setLabelText(const QString &text) +{ + Q_D(QInputDialog); + if (!d->label) { + d->label = new QLabel(text, this); + } else { + d->label->setText(text); + } +} + +QString QInputDialog::labelText() const +{ + Q_D(const QInputDialog); + d->ensureLayout(); + return d->label->text(); +} + +/*! + \enum QInputDialog::InputDialogOption + + \since 4.5 + + This enum specifies various options that affect the look and feel + of an input dialog. + + \value NoButtons Don't display \gui{OK} and \gui{Cancel} buttons. (Useful for "live dialogs".) + \value UseListViewForComboBoxItems Use a QListView rather than a non-editable QComboBox for + displaying the items set with setComboBoxItems(). + + \sa options, setOption(), testOption() +*/ + +/*! + Sets the given \a option to be enabled if \a on is true; + otherwise, clears the given \a option. + + \sa options, testOption() +*/ +void QInputDialog::setOption(InputDialogOption option, bool on) +{ + Q_D(QInputDialog); + if (!(d->opts & option) != !on) + setOptions(d->opts ^ option); +} + +/*! + Returns true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption() +*/ +bool QInputDialog::testOption(InputDialogOption option) const +{ + Q_D(const QInputDialog); + return (d->opts & option) != 0; +} + +/*! + \property QInputDialog::options + \brief the various options that affect the look and feel of the dialog + \since 4.5 + + By default, all options are disabled. + + \sa setOption(), testOption() +*/ +void QInputDialog::setOptions(InputDialogOptions options) +{ + Q_D(QInputDialog); + + InputDialogOptions changed = (options ^ d->opts); + if (!changed) + return; + + d->opts = options; + d->ensureLayout(); + + if (changed & NoButtons) + d->buttonBox->setVisible(!(options & NoButtons)); + if ((changed & UseListViewForComboBoxItems) && inputMode() == TextInput) + d->chooseRightTextInputWidget(); +} + +QInputDialog::InputDialogOptions QInputDialog::options() const +{ + Q_D(const QInputDialog); + return d->opts; +} + +/*! + \since 4.5 + + \property QInputDialog::textValue + + \brief the text value for the input dialog + + This property is only relevant when the input dialog is used in + TextInput mode. +*/ +void QInputDialog::setTextValue(const QString &text) +{ + Q_D(QInputDialog); + + setInputMode(TextInput); + if (d->inputWidget == d->lineEdit) { + d->lineEdit->setText(text); + } else if (d->inputWidget == d->comboBox) { + d->setComboBoxText(text); + } else { + d->setListViewText(text); + } +} + +QString QInputDialog::textValue() const +{ + Q_D(const QInputDialog); + return d->textValue; +} + +/*! + \since 4.5 + + \property QInputDialog::textEchoMode + + \brief the echo mode for the text value + + This property is only relevant when the input dialog is used in + TextInput mode. +*/ +void QInputDialog::setTextEchoMode(QLineEdit::EchoMode mode) +{ + Q_D(QInputDialog); + d->ensureLineEdit(); + d->lineEdit->setEchoMode(mode); +} + +QLineEdit::EchoMode QInputDialog::textEchoMode() const +{ + Q_D(const QInputDialog); + if (d->lineEdit) { + return d->lineEdit->echoMode(); + } else { + return QLineEdit::Normal; + } +} + +/*! + \since 4.5 + + \property QInputDialog::comboBoxEditable + + \brief whether or not the combo box is used in the input dialog is editable +*/ +void QInputDialog::setComboBoxEditable(bool editable) +{ + Q_D(QInputDialog); + d->ensureComboBox(); + d->comboBox->setEditable(editable); + if (inputMode() == TextInput) + d->chooseRightTextInputWidget(); +} + +bool QInputDialog::isComboBoxEditable() const +{ + Q_D(const QInputDialog); + if (d->comboBox) { + return d->comboBox->isEditable(); + } else { + return false; + } +} + +/*! + \since 4.5 + + \property QInputDialog::comboBoxItems + + \brief the items used in the combobox for the input dialog +*/ +void QInputDialog::setComboBoxItems(const QStringList &items) +{ + Q_D(QInputDialog); + + d->ensureComboBox(); + d->comboBox->blockSignals(true); + d->comboBox->clear(); + d->comboBox->addItems(items); + d->comboBox->blockSignals(false); + + if (inputMode() == TextInput) + d->chooseRightTextInputWidget(); +} + +QStringList QInputDialog::comboBoxItems() const +{ + Q_D(const QInputDialog); + QStringList result; + if (d->comboBox) { + const int count = d->comboBox->count(); + for (int i = 0; i < count; ++i) + result.append(d->comboBox->itemText(i)); + } + return result; +} + +/*! + \property QInputDialog::intValue + \since 4.5 + \brief the current integer value accepted as input + + This property is only relevant when the input dialog is used in + IntInput mode. +*/ +void QInputDialog::setIntValue(int value) +{ + Q_D(QInputDialog); + setInputMode(IntInput); + d->intSpinBox->setValue(value); +} + +int QInputDialog::intValue() const +{ + Q_D(const QInputDialog); + if (d->intSpinBox) { + return d->intSpinBox->value(); + } else { + return 0; + } +} + +/*! + \property QInputDialog::intMinimum + \since 4.5 + \brief the minimum integer value accepted as input + + This property is only relevant when the input dialog is used in + IntInput mode. +*/ +void QInputDialog::setIntMinimum(int min) +{ + Q_D(QInputDialog); + d->ensureIntSpinBox(); + d->intSpinBox->setMinimum(min); +} + +int QInputDialog::intMinimum() const +{ + Q_D(const QInputDialog); + if (d->intSpinBox) { + return d->intSpinBox->minimum(); + } else { + return 0; + } +} + +/*! + \property QInputDialog::intMaximum + \since 4.5 + \brief the maximum integer value accepted as input + + This property is only relevant when the input dialog is used in + IntInput mode. +*/ +void QInputDialog::setIntMaximum(int max) +{ + Q_D(QInputDialog); + d->ensureIntSpinBox(); + d->intSpinBox->setMaximum(max); +} + +int QInputDialog::intMaximum() const +{ + Q_D(const QInputDialog); + if (d->intSpinBox) { + return d->intSpinBox->maximum(); + } else { + return 99; + } +} + +/*! + Sets the range of integer values accepted by the dialog when used in + IntInput mode, with minimum and maximum values specified by \a min and + \a max respectively. +*/ +void QInputDialog::setIntRange(int min, int max) +{ + Q_D(QInputDialog); + d->ensureIntSpinBox(); + d->intSpinBox->setRange(min, max); +} + +/*! + \property QInputDialog::intStep + \since 4.5 + \brief the step by which the integer value is increased and decreased + + This property is only relevant when the input dialog is used in + IntInput mode. +*/ +void QInputDialog::setIntStep(int step) +{ + Q_D(QInputDialog); + d->ensureIntSpinBox(); + d->intSpinBox->setSingleStep(step); +} + +int QInputDialog::intStep() const +{ + Q_D(const QInputDialog); + if (d->intSpinBox) { + return d->intSpinBox->singleStep(); + } else { + return 1; + } +} + +/*! + \property QInputDialog::doubleValue + \since 4.5 + \brief the current double precision floating point value accepted as input + + This property is only relevant when the input dialog is used in + DoubleInput mode. +*/ +void QInputDialog::setDoubleValue(double value) +{ + Q_D(QInputDialog); + setInputMode(DoubleInput); + d->doubleSpinBox->setValue(value); +} + +double QInputDialog::doubleValue() const +{ + Q_D(const QInputDialog); + if (d->doubleSpinBox) { + return d->doubleSpinBox->value(); + } else { + return 0.0; + } +} + +/*! + \property QInputDialog::doubleMinimum + \since 4.5 + \brief the minimum double precision floating point value accepted as input + + This property is only relevant when the input dialog is used in + DoubleInput mode. +*/ +void QInputDialog::setDoubleMinimum(double min) +{ + Q_D(QInputDialog); + d->ensureDoubleSpinBox(); + d->doubleSpinBox->setMinimum(min); +} + +double QInputDialog::doubleMinimum() const +{ + Q_D(const QInputDialog); + if (d->doubleSpinBox) { + return d->doubleSpinBox->minimum(); + } else { + return 0.0; + } +} + +/*! + \property QInputDialog::doubleMaximum + \since 4.5 + \brief the maximum double precision floating point value accepted as input + + This property is only relevant when the input dialog is used in + DoubleInput mode. +*/ +void QInputDialog::setDoubleMaximum(double max) +{ + Q_D(QInputDialog); + d->ensureDoubleSpinBox(); + d->doubleSpinBox->setMaximum(max); +} + +double QInputDialog::doubleMaximum() const +{ + Q_D(const QInputDialog); + if (d->doubleSpinBox) { + return d->doubleSpinBox->maximum(); + } else { + return 99.99; + } +} + +/*! + Sets the range of double precision floating point values accepted by the + dialog when used in DoubleInput mode, with minimum and maximum values + specified by \a min and \a max respectively. +*/ +void QInputDialog::setDoubleRange(double min, double max) +{ + Q_D(QInputDialog); + d->ensureDoubleSpinBox(); + d->doubleSpinBox->setRange(min, max); +} + +/*! + \since 4.5 + + \property QInputDialog::doubleDecimals + + \brief sets the percision of the double spinbox in decimals + + \sa QDoubleSpinBox::setDecimals() +*/ +void QInputDialog::setDoubleDecimals(int decimals) +{ + Q_D(QInputDialog); + d->ensureDoubleSpinBox(); + d->doubleSpinBox->setDecimals(decimals); +} + +int QInputDialog::doubleDecimals() const +{ + Q_D(const QInputDialog); + if (d->doubleSpinBox) { + return d->doubleSpinBox->decimals(); + } else { + return 2; + } +} + +/*! + \since 4.5 + + \property QInputDialog::okButtonText + + \brief the text for the button used to accept the entry in the dialog +*/ +void QInputDialog::setOkButtonText(const QString &text) +{ + Q_D(const QInputDialog); + d->ensureLayout(); + d->buttonBox->button(QDialogButtonBox::Ok)->setText(text); +} + +QString QInputDialog::okButtonText() const +{ + Q_D(const QInputDialog); + d->ensureLayout(); + return d->buttonBox->button(QDialogButtonBox::Ok)->text(); +} + +/*! + \since 4.5 + + \property QInputDialog::cancelButtonText + \brief the text for the button used to cancel the dialog +*/ +void QInputDialog::setCancelButtonText(const QString &text) +{ + Q_D(const QInputDialog); + d->ensureLayout(); + d->buttonBox->button(QDialogButtonBox::Cancel)->setText(text); +} + +QString QInputDialog::cancelButtonText() const +{ + Q_D(const QInputDialog); + d->ensureLayout(); + return d->buttonBox->button(QDialogButtonBox::Cancel)->text(); +} + +/*! + \since 4.5 + \overload + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QInputDialog::open(QObject *receiver, const char *member) +{ + Q_D(QInputDialog); + connect(this, signalForMember(member), receiver, member); + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +/*! + \reimp +*/ +QSize QInputDialog::minimumSizeHint() const +{ + Q_D(const QInputDialog); + d->ensureLayout(); + return QDialog::minimumSizeHint(); +} + +/*! + \reimp +*/ +QSize QInputDialog::sizeHint() const +{ + Q_D(const QInputDialog); + d->ensureLayout(); + return QDialog::sizeHint(); +} + +/*! + \reimp +*/ +void QInputDialog::setVisible(bool visible) +{ + Q_D(const QInputDialog); + if (visible) { + d->ensureLayout(); + d->inputWidget->setFocus(); + if (d->inputWidget == d->lineEdit) { + d->lineEdit->selectAll(); + } else if (d->inputWidget == d->intSpinBox) { + d->intSpinBox->selectAll(); + } else if (d->inputWidget == d->doubleSpinBox) { + d->doubleSpinBox->selectAll(); + } + } + QDialog::setVisible(visible); +} + +/*! + Closes the dialog and sets its result code to \a result. If this dialog + is shown with exec(), done() causes the local event loop to finish, + and exec() to return \a result. + + \sa QDialog::done() +*/ +void QInputDialog::done(int result) +{ + Q_D(QInputDialog); + QDialog::done(result); + if (result) { + InputMode mode = inputMode(); + switch (mode) { + case DoubleInput: + emit doubleValueSelected(doubleValue()); + break; + case IntInput: + emit intValueSelected(intValue()); + break; + default: + Q_ASSERT(mode == TextInput); + emit textValueSelected(textValue()); + } + } + if (d->receiverToDisconnectOnClose) { + disconnect(this, signalForMember(d->memberToDisconnectOnClose), + d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); + d->receiverToDisconnectOnClose = 0; + } + d->memberToDisconnectOnClose.clear(); +} + +/*! + Static convenience function to get a string from the user. \a + title is the text which is displayed in the title bar of the + dialog. \a label is the text which is shown to the user (it should + say what should be entered). \a text is the default text which is + placed in the line edit. The \a mode is the echo mode the line + edit will use. If \a ok is nonnull \e *\a ok will be set to true + if the user pressed \gui OK and to false if the user pressed + \gui Cancel. The dialog's parent is \a parent. The dialog will be + modal and uses the specified widget \a flags. + + This function returns the text which has been entered in the line + edit. It will not return an empty string. + + Use this static function like this: + + \snippet examples/dialogs/standarddialogs/dialog.cpp 3 + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QInputDialog constructors. + + \sa getInteger(), getDouble(), getItem() +*/ + +QString QInputDialog::getText(QWidget *parent, const QString &title, const QString &label, + QLineEdit::EchoMode mode, const QString &text, bool *ok, + Qt::WindowFlags flags) +{ + QInputDialog dialog(parent, flags); + dialog.setWindowTitle(title); + dialog.setLabelText(label); + dialog.setTextValue(text); + dialog.setTextEchoMode(mode); + + int ret = dialog.exec(); + if (ok) + *ok = !!ret; + if (ret) { + return dialog.textValue(); + } else { + return text; + } +} + +/*! + Static convenience function to get an integer input from the + user. \a title is the text which is displayed in the title bar + of the dialog. \a label is the text which is shown to the user + (it should say what should be entered). \a value is the default + integer which the spinbox will be set to. \a min and \a + max are the minimum and maximum values the user may choose, + and \a step is the amount by which the values change as the user + presses the arrow buttons to increment or decrement the value. + + If \a ok is nonnull *\a ok will be set to true if the user + pressed \gui OK and to false if the user pressed \gui Cancel. The + dialog's parent is \a parent. The dialog will be modal and uses + the widget \a flags. + + On success, this function returns the integer which has been + entered by the user; on failure, it returns the initial \a value. + + Use this static function like this: + + \snippet examples/dialogs/standarddialogs/dialog.cpp 0 + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QInputDialog constructors. + + \sa getText(), getDouble(), getItem() +*/ + +int QInputDialog::getInt(QWidget *parent, const QString &title, const QString &label, int value, + int min, int max, int step, bool *ok, Qt::WindowFlags flags) +{ + QInputDialog dialog(parent, flags); + dialog.setWindowTitle(title); + dialog.setLabelText(label); + dialog.setIntRange(min, max); + dialog.setIntValue(value); + dialog.setIntStep(step); + + int ret = dialog.exec(); + if (ok) + *ok = !!ret; + if (ret) { + return dialog.intValue(); + } else { + return value; + } +} + +/*! + Static convenience function to get a floating point number from + the user. \a title is the text which is displayed in the title + bar of the dialog. \a label is the text which is shown to the user + (it should say what should be entered). \a value is the default + floating point number that the line edit will be set to. \a + min and \a max are the minimum and maximum values the + user may choose, and \a decimals is the maximum number of decimal + places the number may have. + + If \a ok is nonnull, *\a ok will be set to true if the user + pressed \gui OK and to false if the user pressed \gui Cancel. The + dialog's parent is \a parent. The dialog will be modal and uses + the widget \a flags. + + This function returns the floating point number which has been + entered by the user. + + Use this static function like this: + + \snippet examples/dialogs/standarddialogs/dialog.cpp 1 + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QInputDialog constructors. + + \sa getText(), getInteger(), getItem() +*/ + +double QInputDialog::getDouble(QWidget *parent, const QString &title, const QString &label, + double value, double min, double max, int decimals, bool *ok, + Qt::WindowFlags flags) +{ + QInputDialog dialog(parent, flags); + dialog.setWindowTitle(title); + dialog.setLabelText(label); + dialog.setDoubleRange(min, max); + dialog.setDoubleValue(value); + dialog.setDoubleDecimals(decimals); + + int ret = dialog.exec(); + if (ok) + *ok = !!ret; + if (ret) { + return dialog.doubleValue(); + } else { + return value; + } +} + +/*! + Static convenience function to let the user select an item from a + string list. \a title is the text which is displayed in the title + bar of the dialog. \a label is the text which is shown to the user (it + should say what should be entered). \a items is the + string list which is inserted into the combobox, and \a current is the number + of the item which should be the current item. If \a editable is true + the user can enter their own text; if \a editable is false the user + may only select one of the existing items. + + If \a ok is nonnull \e *\a ok will be set to true if the user + pressed \gui OK and to false if the user pressed \gui Cancel. The + dialog's parent is \a parent. The dialog will be modal and uses + the widget \a flags. + + This function returns the text of the current item, or if \a + editable is true, the current text of the combobox. + + Use this static function like this: + + \snippet examples/dialogs/standarddialogs/dialog.cpp 2 + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QInputDialog constructors. + + \sa getText(), getInteger(), getDouble() +*/ + +QString QInputDialog::getItem(QWidget *parent, const QString &title, const QString &label, + const QStringList &items, int current, bool editable, bool *ok, + Qt::WindowFlags flags) +{ + QString text(items.value(current)); + + QInputDialog dialog(parent, flags); + dialog.setWindowTitle(title); + dialog.setLabelText(label); + dialog.setComboBoxItems(items); + dialog.setTextValue(text); + dialog.setComboBoxEditable(editable); + + int ret = dialog.exec(); + if (ok) + *ok = !!ret; + if (ret) { + return dialog.textValue(); + } else { + return text; + } +} + +/*! + \obsolete + + Use getInt() instead. +*/ +int QInputDialog::getInteger(QWidget *parent, const QString &title, const QString &label, + int value, int min, int max, int step, bool *ok, + Qt::WindowFlags flags) +{ + return getInt(parent, title, label, value, min, max, step, ok, flags); +} + +/*! + \fn QString QInputDialog::getText(const QString &title, const QString &label, + QLineEdit::EchoMode echo = QLineEdit::Normal, + const QString &text = QString(), bool *ok = 0, + QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0) + + Call getText(\a parent, \a title, \a label, \a echo, \a text, \a + ok, \a flags) instead. + + The \a name parameter is ignored. +*/ + +/*! + \fn int QInputDialog::getInteger(const QString &title, const QString &label, int value = 0, + int min = -2147483647, int max = 2147483647, + int step = 1, bool *ok = 0, + QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0) + + + Call getInteger(\a parent, \a title, \a label, \a value, \a + min, \a max, \a step, \a ok, \a flags) instead. + + The \a name parameter is ignored. +*/ + +/*! + \fn double QInputDialog::getDouble(const QString &title, const QString &label, double value = 0, + double min = -2147483647, double max = 2147483647, + int decimals = 1, bool *ok = 0, + QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0) + + Call getDouble(\a parent, \a title, \a label, \a value, \a + min, \a max, \a decimals, \a ok, \a flags). + + The \a name parameter is ignored. +*/ + +/*! + \fn QString QInputDialog::getItem(const QString &title, const QString &label, const QStringList &list, + int current = 0, bool editable = true, bool *ok = 0, + QWidget *parent = 0, const char *name = 0, Qt::WindowFlags flags = 0) + + Call getItem(\a parent, \a title, \a label, \a list, \a current, + \a editable, \a ok, \a flags) instead. + + The \a name parameter is ignored. +*/ + +/*! + \fn void QInputDialog::doubleValueChanged(double value) + + This signal is emitted whenever the double value changes in the dialog. + The current value is specified by \a value. + + This signal is only relevant when the input dialog is used in + DoubleInput mode. +*/ + +/*! + \fn void QInputDialog::doubleValueSelected(double value) + + This signal is emitted whenever the user selects a double value by + accepting the dialog; for example, by clicking the \gui{OK} button. + The selected value is specified by \a value. + + This signal is only relevant when the input dialog is used in + DoubleInput mode. +*/ + +/*! + \fn void QInputDialog::intValueChanged(int value) + + This signal is emitted whenever the integer value changes in the dialog. + The current value is specified by \a value. + + This signal is only relevant when the input dialog is used in + IntInput mode. +*/ + +/*! + \fn void QInputDialog::intValueSelected(int value) + + This signal is emitted whenever the user selects a integer value by + accepting the dialog; for example, by clicking the \gui{OK} button. + The selected value is specified by \a value. + + This signal is only relevant when the input dialog is used in + IntInput mode. +*/ + +/*! + \fn void QInputDialog::textValueChanged(const QString &text) + + This signal is emitted whenever the text string changes in the dialog. + The current string is specified by \a text. + + This signal is only relevant when the input dialog is used in + TextInput mode. +*/ + +/*! + \fn void QInputDialog::textValueSelected(const QString &text) + + This signal is emitted whenever the user selects a text string by + accepting the dialog; for example, by clicking the \gui{OK} button. + The selected string is specified by \a text. + + This signal is only relevant when the input dialog is used in + TextInput mode. +*/ + +QT_END_NAMESPACE + +#include "moc_qinputdialog.cpp" + +#endif // QT_NO_INPUTDIALOG diff --git a/src/gui/dialogs/qinputdialog.h b/src/gui/dialogs/qinputdialog.h new file mode 100644 index 0000000000..52b338af45 --- /dev/null +++ b/src/gui/dialogs/qinputdialog.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QINPUTDIALOG_H +#define QINPUTDIALOG_H + +#include <QtGui/qdialog.h> +#include <QtCore/qstring.h> +#include <QtGui/qlineedit.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_INPUTDIALOG + +class QInputDialogPrivate; + +class Q_GUI_EXPORT QInputDialog : public QDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QInputDialog) +// Q_ENUMS(InputMode InputDialogOption) + QDOC_PROPERTY(InputMode inputMode READ inputMode WRITE setInputMode) + QDOC_PROPERTY(QString labelText READ labelText WRITE setLabelText) + QDOC_PROPERTY(InputDialogOptions options READ options WRITE setOptions) + QDOC_PROPERTY(QString textValue READ textValue WRITE setTextValue NOTIFY textValueChanged) + QDOC_PROPERTY(int intValue READ intValue WRITE setIntValue NOTIFY intValueChanged) + QDOC_PROPERTY(int doubleValue READ doubleValue WRITE setDoubleValue NOTIFY doubleValueChanged) + QDOC_PROPERTY(QLineEdit::EchoMode textEchoMode READ textEchoMode WRITE setTextEchoMode) + QDOC_PROPERTY(bool comboBoxEditable READ isComboBoxEditable WRITE setComboBoxEditable) + QDOC_PROPERTY(QStringList comboBoxItems READ comboBoxItems WRITE setComboBoxItems) + QDOC_PROPERTY(int intMinimum READ intMinimum WRITE setIntMinimum) + QDOC_PROPERTY(int intMaximum READ intMaximum WRITE setIntMaximum) + QDOC_PROPERTY(int intStep READ intStep WRITE setIntStep) + QDOC_PROPERTY(double doubleMinimum READ doubleMinimum WRITE setDoubleMinimum) + QDOC_PROPERTY(double doubleMaximum READ doubleMaximum WRITE setDoubleMaximum) + QDOC_PROPERTY(int doubleDecimals READ doubleDecimals WRITE setDoubleDecimals) + QDOC_PROPERTY(QString okButtonText READ okButtonText WRITE setOkButtonText) + QDOC_PROPERTY(QString cancelButtonText READ cancelButtonText WRITE setCancelButtonText) + +public: + enum InputDialogOption { + NoButtons = 0x00000001, + UseListViewForComboBoxItems = 0x00000002 + }; + + Q_DECLARE_FLAGS(InputDialogOptions, InputDialogOption) + + enum InputMode { + TextInput, + IntInput, + DoubleInput + }; + + QInputDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~QInputDialog(); + + void setInputMode(InputMode mode); + InputMode inputMode() const; + + void setLabelText(const QString &text); + QString labelText() const; + + void setOption(InputDialogOption option, bool on = true); + bool testOption(InputDialogOption option) const; + void setOptions(InputDialogOptions options); + InputDialogOptions options() const; + + void setTextValue(const QString &text); + QString textValue() const; + + void setTextEchoMode(QLineEdit::EchoMode mode); + QLineEdit::EchoMode textEchoMode() const; + + void setComboBoxEditable(bool editable); + bool isComboBoxEditable() const; + + void setComboBoxItems(const QStringList &items); + QStringList comboBoxItems() const; + + void setIntValue(int value); + int intValue() const; + + void setIntMinimum(int min); + int intMinimum() const; + + void setIntMaximum(int max); + int intMaximum() const; + + void setIntRange(int min, int max); + + void setIntStep(int step); + int intStep() const; + + void setDoubleValue(double value); + double doubleValue() const; + + void setDoubleMinimum(double min); + double doubleMinimum() const; + + void setDoubleMaximum(double max); + double doubleMaximum() const; + + void setDoubleRange(double min, double max); + + void setDoubleDecimals(int decimals); + int doubleDecimals() const; + + void setOkButtonText(const QString &text); + QString okButtonText() const; + + void setCancelButtonText(const QString &text); + QString cancelButtonText() const; + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + + void setVisible(bool visible); + + static QString getText(QWidget *parent, const QString &title, const QString &label, + QLineEdit::EchoMode echo = QLineEdit::Normal, + const QString &text = QString(), bool *ok = 0, Qt::WindowFlags flags = 0); + static int getInt(QWidget *parent, const QString &title, const QString &label, int value = 0, + int minValue = -2147483647, int maxValue = 2147483647, + int step = 1, bool *ok = 0, Qt::WindowFlags flags = 0); + static double getDouble(QWidget *parent, const QString &title, const QString &label, double value = 0, + double minValue = -2147483647, double maxValue = 2147483647, + int decimals = 1, bool *ok = 0, Qt::WindowFlags flags = 0); + static QString getItem(QWidget *parent, const QString &title, const QString &label, + const QStringList &items, int current = 0, bool editable = true, + bool *ok = 0, Qt::WindowFlags flags = 0); + + // obsolete + static int getInteger(QWidget *parent, const QString &title, const QString &label, int value = 0, + int minValue = -2147483647, int maxValue = 2147483647, + int step = 1, bool *ok = 0, Qt::WindowFlags flags = 0); + +#ifdef QT3_SUPPORT + inline static QT3_SUPPORT QString getText(const QString &title, const QString &label, + QLineEdit::EchoMode echo = QLineEdit::Normal, + const QString &text = QString(), bool *ok = 0, + QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0) + { return getText(parent, title, label, echo, text, ok, flags); } + inline static QT3_SUPPORT int getInteger(const QString &title, const QString &label, int value = 0, + int minValue = -2147483647, int maxValue = 2147483647, + int step = 1, bool *ok = 0, + QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0) + { return getInteger(parent, title, label, value, minValue, maxValue, step, ok, flags); } + inline static QT3_SUPPORT double getDouble(const QString &title, const QString &label, double value = 0, + double minValue = -2147483647, double maxValue = 2147483647, + int decimals = 1, bool *ok = 0, + QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0) + { return getDouble(parent, title, label, value, minValue, maxValue, decimals, ok, flags); } + inline static QT3_SUPPORT QString getItem(const QString &title, const QString &label, const QStringList &list, + int current = 0, bool editable = true, bool *ok = 0, + QWidget *parent = 0, const char * = 0, Qt::WindowFlags flags = 0) + { return getItem(parent, title, label, list, current, editable, ok, flags); } +#endif + +Q_SIGNALS: + // ### emit signals! + void textValueChanged(const QString &text); + void textValueSelected(const QString &text); + void intValueChanged(int value); + void intValueSelected(int value); + void doubleValueChanged(double value); + void doubleValueSelected(double value); + + +public: + void done(int result); // ### Qt 5: Make protected. + +private: + Q_DISABLE_COPY(QInputDialog) + Q_PRIVATE_SLOT(d_func(), void _q_textChanged(const QString&)) + Q_PRIVATE_SLOT(d_func(), void _q_currentRowChanged(const QModelIndex&, const QModelIndex&)) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QInputDialog::InputDialogOptions) + +#endif // QT_NO_INPUTDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QINPUTDIALOG_H diff --git a/src/gui/dialogs/qmessagebox.cpp b/src/gui/dialogs/qmessagebox.cpp new file mode 100644 index 0000000000..f3434050af --- /dev/null +++ b/src/gui/dialogs/qmessagebox.cpp @@ -0,0 +1,2688 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qmessagebox.h> + +#ifndef QT_NO_MESSAGEBOX + +#include <QtGui/qdialogbuttonbox.h> +#include "private/qlabel_p.h" +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> +#include <QtGui/qstyle.h> +#include <QtGui/qstyleoption.h> +#include <QtGui/qgridlayout.h> +#include <QtGui/qdesktopwidget.h> +#include <QtGui/qpushbutton.h> +#include <QtGui/qaccessible.h> +#include <QtGui/qicon.h> +#include <QtGui/qtextdocument.h> +#include <QtGui/qapplication.h> +#include <QtGui/qtextedit.h> +#include <QtGui/qmenu.h> +#include "qdialog_p.h" +#include <QtGui/qfont.h> +#include <QtGui/qfontmetrics.h> +#include <QtGui/qclipboard.h> + +#ifdef Q_OS_WINCE +extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp +extern bool qt_wince_is_smartphone();//defined in qguifunctions_wince.cpp +extern bool qt_wince_is_pocket_pc(); //defined in qguifunctions_wince.cpp + +#include "qguifunctions_wince.h" +#endif + +QT_BEGIN_NAMESPACE + +extern QHash<QByteArray, QFont> *qt_app_fonts_hash(); + +enum Button { Old_Ok = 1, Old_Cancel = 2, Old_Yes = 3, Old_No = 4, Old_Abort = 5, Old_Retry = 6, + Old_Ignore = 7, Old_YesAll = 8, Old_NoAll = 9, Old_ButtonMask = 0xFF, + NewButtonMask = 0xFFFFFC00 }; + +enum DetailButtonLabel { ShowLabel = 0, HideLabel = 1 }; +#ifndef QT_NO_TEXTEDIT +class QMessageBoxDetailsText : public QWidget +{ +public: + class TextEdit : public QTextEdit + { + public: + TextEdit(QWidget *parent=0) : QTextEdit(parent) { } + void contextMenuEvent(QContextMenuEvent * e) + { +#ifndef QT_NO_CONTEXTMENU + QMenu *menu = createStandardContextMenu(); + menu->exec(e->globalPos()); + delete menu; +#else + Q_UNUSED(e); +#endif + } + }; + + QMessageBoxDetailsText(QWidget *parent=0) + : QWidget(parent) + { + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + layout->addWidget(line); + textEdit = new TextEdit(); + textEdit->setFixedHeight(100); + textEdit->setFocusPolicy(Qt::NoFocus); + textEdit->setReadOnly(true); + layout->addWidget(textEdit); + setLayout(layout); + } + void setText(const QString &text) { textEdit->setPlainText(text); } + QString text() const { return textEdit->toPlainText(); } + QString label(DetailButtonLabel label) + { return label == ShowLabel ? QMessageBox::tr("Show Details...") + : QMessageBox::tr("Hide Details..."); } +private: + TextEdit *textEdit; +}; +#endif // QT_NO_TEXTEDIT + +class QMessageBoxPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QMessageBox) + +public: + QMessageBoxPrivate() : escapeButton(0), defaultButton(0), clickedButton(0), detailsButton(0), +#ifndef QT_NO_TEXTEDIT + detailsText(0), +#endif + compatMode(false), autoAddOkButton(true), + detectedEscapeButton(0), informativeLabel(0) { } + + void init(const QString &title = QString(), const QString &text = QString()); + void _q_buttonClicked(QAbstractButton *); + + QAbstractButton *findButton(int button0, int button1, int button2, int flags); + void addOldButtons(int button0, int button1, int button2); + + QAbstractButton *abstractButtonForId(int id) const; + int execReturnCode(QAbstractButton *button); + + void detectEscapeButton(); + void updateSize(); + int layoutMinimumWidth(); + void retranslateStrings(); + +#ifdef Q_OS_WINCE + void hideSpecial(); +#endif + + static int showOldMessageBox(QWidget *parent, QMessageBox::Icon icon, + const QString &title, const QString &text, + int button0, int button1, int button2); + static int showOldMessageBox(QWidget *parent, QMessageBox::Icon icon, + const QString &title, const QString &text, + const QString &button0Text, + const QString &button1Text, + const QString &button2Text, + int defaultButtonNumber, + int escapeButtonNumber); + + static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, + QMessageBox::Icon icon, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); + + static QPixmap standardIcon(QMessageBox::Icon icon, QMessageBox *mb); + + QLabel *label; + QMessageBox::Icon icon; + QLabel *iconLabel; + QDialogButtonBox *buttonBox; + QList<QAbstractButton *> customButtonList; + QAbstractButton *escapeButton; + QPushButton *defaultButton; + QAbstractButton *clickedButton; + QPushButton *detailsButton; +#ifndef QT_NO_TEXTEDIT + QMessageBoxDetailsText *detailsText; +#endif + bool compatMode; + bool autoAddOkButton; + QAbstractButton *detectedEscapeButton; + QLabel *informativeLabel; + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; + QByteArray signalToDisconnectOnClose; +}; + +void QMessageBoxPrivate::init(const QString &title, const QString &text) +{ + Q_Q(QMessageBox); + + label = new QLabel; + label->setObjectName(QLatin1String("qt_msgbox_label")); + label->setTextInteractionFlags(Qt::TextInteractionFlags(q->style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, 0, q))); + label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + label->setOpenExternalLinks(true); +#if defined(Q_WS_MAC) + label->setContentsMargins(16, 0, 0, 0); +#elif !defined(Q_WS_QWS) + label->setContentsMargins(2, 0, 0, 0); + label->setIndent(9); +#endif + icon = QMessageBox::NoIcon; + iconLabel = new QLabel; + iconLabel->setObjectName(QLatin1String("qt_msgboxex_icon_label")); + iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + buttonBox = new QDialogButtonBox; + buttonBox->setObjectName(QLatin1String("qt_msgbox_buttonbox")); + buttonBox->setCenterButtons(q->style()->styleHint(QStyle::SH_MessageBox_CenterButtons, 0, q)); + QObject::connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), + q, SLOT(_q_buttonClicked(QAbstractButton*))); + + QGridLayout *grid = new QGridLayout; +#ifndef Q_WS_MAC + grid->addWidget(iconLabel, 0, 0, 2, 1, Qt::AlignTop); + grid->addWidget(label, 0, 1, 1, 1); + // -- leave space for information label -- + grid->addWidget(buttonBox, 2, 0, 1, 2); +#else + grid->setMargin(0); + grid->setVerticalSpacing(8); + grid->setHorizontalSpacing(0); + q->setContentsMargins(24, 15, 24, 20); + grid->addWidget(iconLabel, 0, 0, 2, 1, Qt::AlignTop | Qt::AlignLeft); + grid->addWidget(label, 0, 1, 1, 1); + // -- leave space for information label -- + grid->setRowStretch(1, 100); + grid->setRowMinimumHeight(2, 6); + grid->addWidget(buttonBox, 3, 1, 1, 1); +#endif + + grid->setSizeConstraint(QLayout::SetNoConstraint); + q->setLayout(grid); + + if (!title.isEmpty() || !text.isEmpty()) { + q->setWindowTitle(title); + q->setText(text); + } + q->setModal(true); + +#ifdef Q_WS_MAC + QFont f = q->font(); + f.setBold(true); + label->setFont(f); +#endif + retranslateStrings(); +} + +int QMessageBoxPrivate::layoutMinimumWidth() +{ + Q_Q(QMessageBox); + + q->layout()->activate(); + return q->layout()->totalMinimumSize().width(); +} + +void QMessageBoxPrivate::updateSize() +{ + Q_Q(QMessageBox); + + if (!q->isVisible()) + return; + + QSize screenSize = QApplication::desktop()->availableGeometry(QCursor::pos()).size(); +#ifdef Q_WS_QWS + // the width of the screen, less the window border. + int hardLimit = screenSize.width() - (q->frameGeometry().width() - q->geometry().width()); +#elif defined(Q_OS_WINCE) + // the width of the screen, less the window border. + int hardLimit = screenSize.width() - (q->frameGeometry().width() - q->geometry().width()); +#else + int hardLimit = qMin(screenSize.width() - 480, 1000); // can never get bigger than this +#endif +#ifdef Q_WS_MAC + int softLimit = qMin(screenSize.width()/2, 420); +#elif defined(Q_WS_QWS) + int softLimit = qMin(hardLimit, 500); +#else + // note: ideally on windows, hard and soft limits but it breaks compat +#ifndef Q_OS_WINCE + int softLimit = qMin(screenSize.width()/2, 500); +#else + int softLimit = qMin(screenSize.width() * 3 / 4, 500); +#endif //Q_OS_WINCE +#endif + + if (informativeLabel) + informativeLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + + label->setWordWrap(false); // makes the label return min size + int width = layoutMinimumWidth(); + + if (width > softLimit) { + label->setWordWrap(true); + width = qMax(softLimit, layoutMinimumWidth()); + + if (width > hardLimit) { + label->d_func()->ensureTextControl(); + if (QTextControl *control = label->d_func()->control) { + QTextOption opt = control->document()->defaultTextOption(); + opt.setWrapMode(QTextOption::WrapAnywhere); + control->document()->setDefaultTextOption(opt); + } + width = hardLimit; + } + } + + if (informativeLabel) { + label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + QSizePolicy policy(QSizePolicy::Minimum, QSizePolicy::Preferred); + policy.setHeightForWidth(true); + informativeLabel->setSizePolicy(policy); + width = qMax(width, layoutMinimumWidth()); + if (width > hardLimit) { // longest word is really big, so wrap anywhere + informativeLabel->d_func()->ensureTextControl(); + if (QTextControl *control = informativeLabel->d_func()->control) { + QTextOption opt = control->document()->defaultTextOption(); + opt.setWrapMode(QTextOption::WrapAnywhere); + control->document()->setDefaultTextOption(opt); + } + width = hardLimit; + } + policy.setHeightForWidth(label->wordWrap()); + label->setSizePolicy(policy); + } + + QFontMetrics fm(qApp->font("QWorkspaceTitleBar")); + int windowTitleWidth = qMin(fm.width(q->windowTitle()) + 50, hardLimit); + if (windowTitleWidth > width) + width = windowTitleWidth; + + q->layout()->activate(); + int height = (q->layout()->hasHeightForWidth()) + ? q->layout()->totalHeightForWidth(width) + : q->layout()->totalMinimumSize().height(); + q->setFixedSize(width, height); + QCoreApplication::removePostedEvents(q, QEvent::LayoutRequest); +} + + +#ifdef Q_OS_WINCE +/*! + \internal + Hides special buttons which are rather shown in the title bar + on WinCE, to conserve screen space. +*/ + +void QMessageBoxPrivate::hideSpecial() +{ + Q_Q(QMessageBox); + QList<QPushButton*> list = qFindChildren<QPushButton*>(q); + for (int i=0; i<list.size(); ++i) { + QPushButton *pb = list.at(i); + QString text = pb->text(); + text.remove(QChar::fromLatin1('&')); + if (text == qApp->translate("QMessageBox", "OK" )) + pb->setFixedSize(0,0); + } +} +#endif + +static int oldButton(int button) +{ + switch (button & QMessageBox::ButtonMask) { + case QMessageBox::Ok: + return Old_Ok; + case QMessageBox::Cancel: + return Old_Cancel; + case QMessageBox::Yes: + return Old_Yes; + case QMessageBox::No: + return Old_No; + case QMessageBox::Abort: + return Old_Abort; + case QMessageBox::Retry: + return Old_Retry; + case QMessageBox::Ignore: + return Old_Ignore; + case QMessageBox::YesToAll: + return Old_YesAll; + case QMessageBox::NoToAll: + return Old_NoAll; + default: + return 0; + } +} + +int QMessageBoxPrivate::execReturnCode(QAbstractButton *button) +{ + int ret = buttonBox->standardButton(button); + if (ret == QMessageBox::NoButton) { + ret = customButtonList.indexOf(button); // if button == 0, correctly sets ret = -1 + } else if (compatMode) { + ret = oldButton(ret); + } + return ret; +} + +void QMessageBoxPrivate::_q_buttonClicked(QAbstractButton *button) +{ + Q_Q(QMessageBox); +#ifndef QT_NO_TEXTEDIT + if (detailsButton && detailsText && button == detailsButton) { + detailsButton->setText(detailsText->isHidden() ? detailsText->label(HideLabel) : detailsText->label(ShowLabel)); + detailsText->setHidden(!detailsText->isHidden()); + updateSize(); + } else +#endif + { + clickedButton = button; + q->done(execReturnCode(button)); // does not trigger closeEvent + emit q->buttonClicked(button); + + if (receiverToDisconnectOnClose) { + QObject::disconnect(q, signalToDisconnectOnClose, receiverToDisconnectOnClose, + memberToDisconnectOnClose); + receiverToDisconnectOnClose = 0; + } + signalToDisconnectOnClose.clear(); + memberToDisconnectOnClose.clear(); + } +} + +/*! + \class QMessageBox + + \brief The QMessageBox class provides a modal dialog for informing + the user or for asking the user a question and receiving an answer. + + \ingroup dialogs + \mainclass + + A message box displays a primary \l{QMessageBox::text}{text} to + alert the user to a situation, an \l{QMessageBox::informativeText} + {informative text} to further explain the alert or to ask the user + a question, and an optional \l{QMessageBox::detailedText} + {detailed text} to provide even more data if the user requests + it. A message box can also display an \l{QMessageBox::icon} {icon} + and \l{QMessageBox::standardButtons} {standard buttons} for + accepting a user response. + + Two APIs for using QMessageBox are provided, the property-based + API, and the static functions. Calling one of the static functions + is the simpler approach, but it is less flexible than using the + property-based API, and the result is less informative. Using the + property-based API is recommended. + + \section1 The Property-based API + + To use the property-based API, construct an instance of + QMessageBox, set the desired properties, and call exec() to show + the message. The simplest configuration is to set only the + \l{QMessageBox::text} {message text} property. + + \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 5 + + The user must click the \gui{OK} button to dismiss the message + box. The rest of the GUI is blocked until the message box is + dismissed. + + \image msgbox1.png + + A better approach than just alerting the user to an event is to + also ask the user what to do about it. Store the question in the + \l{QMessageBox::informativeText} {informative text} property, and + set the \l{QMessageBox::standardButtons} {standard buttons} + property to the set of buttons you want as the set of user + responses. The buttons are specified by combining values from + StandardButtons using the bitwise OR operator. The display order + for the buttons is platform-dependent. For example, on Windows, + \gui{Save} is displayed to the left of \gui{Cancel}, whereas on + Mac OS, the order is reversed. + + Mark one of your standard buttons to be your + \l{QMessageBox::defaultButton()} {default button}. + + \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 6 + + This is the approach recommended in the + \l{http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGWindows/chapter_18_section_7.html} + {Mac OS X Guidlines}. Similar guidlines apply for the other + platforms, but note the different ways the + \l{QMessageBox::informativeText} {informative text} is handled for + different platforms. + + \image msgbox2.png + + The exec() slot returns the StandardButtons value of the button + that was clicked. + + \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 7 + + To give the user more information to help him answer the question, + set the \l{QMessageBox::detailedText} {detailed text} property. If + the \l{QMessageBox::detailedText} {detailed text} property is set, + the \gui{Show Details...} button will be shown. + + \image msgbox3.png + + Clicking the \gui{Show Details...} button displays the detailed text. + + \image msgbox4.png + + \section2 Rich Text and the Text Format Property + + The \l{QMessageBox::detailedText} {detailed text} property is + always interpreted as plain text. The \l{QMessageBox::text} {main + text} and \l{QMessageBox::informativeText} {informative text} + properties can be either plain text or rich text. These strings + are interpreted according to the setting of the + \l{QMessageBox::textFormat} {text format} property. The default + setting is \l{Qt::AutoText} {auto-text}. + + Note that for some plain text strings containing XML + meta-characters, the auto-text \l{Qt::mightBeRichText()} {rich + text detection test} may fail causing your plain text string to be + interpreted incorrectly as rich text. In these rare cases, use + Qt::convertFromPlainText() to convert your plain text string to a + visually equivalent rich text string, or set the + \l{QMessageBox::textFormat} {text format} property explicitly with + setTextFormat(). + + \section2 Severity Levels and the Icon and Pixmap Properties + + QMessageBox supports four predefined message severity levels, or + message types, which really only differ in the predefined icon + they each show. Specify one of the four predefined message types + by setting the \l{QMessageBox::icon} {icon} property to one of the + \l{QMessageBox::Icon} {predefined Icons}. The following rules are + guidelines: + + \table + \row + \o \img qmessagebox-quest.png + \o \l Question + \o For asking a question during normal operations. + \row + \o \img qmessagebox-info.png + \o \l Information + \o For reporting information about normal operations. + \row + \o \img qmessagebox-warn.png + \o \l Warning + \o For reporting non-critical errors. + \row + \o \img qmessagebox-crit.png + \o \l Critical + \o For reporting critical errors. + \endtable + + The default value is \l{QMessageBox::NoIcon} {No Icon}. The + message boxes are otherwise the same for all cases. When using a + standard icon, use the one recommended in the table, or use the + one recommended by the style guidelines for your platform. If none + of the standard icons is right for your message box, you can use a + custom icon by setting the \l{QMessageBox::iconPixmap} {icon + pixmap} property instead of setting the \l{QMessageBox::icon} + {icon} property. + + In summary, to set an icon, use \e{either} setIcon() for one of + the standard icons, \e{or} setIconPixmap() for a custom icon. + + \section1 The Static Functions API + + Building message boxes with the static functions API, although + convenient, is less flexible than using the property-based API, + because the static function signatures lack parameters for setting + the \l{QMessageBox::informativeText} {informative text} and + \l{QMessageBox::detailedText} {detailed text} properties. One + work-around for this has been to use the \c{title} parameter as + the message box main text and the \c{text} parameter as the + message box informative text. Because this has the obvious + drawback of making a less readable message box, platform + guidelines do not recommend it. The \e{Microsoft Windows User + Interface Guidelines} recommend using the + \l{QCoreApplication::applicationName} {application name} as the + \l{QMessageBox::setWindowTitle()} {window's title}, which means + that if you have an informative text in addition to your main + text, you must concatenate it to the \c{text} parameter. + + Note that the static function signatures have changed with respect + to their button parameters, which are now used to set the + \l{QMessageBox::standardButtons} {standard buttons} and the + \l{QMessageBox::defaultButton()} {default button}. + + Static functions are available for creating information(), + question(), warning(), and critical() message boxes. + + \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 0 + + The \l{dialogs/standarddialogs}{Standard Dialogs} example shows + how to use QMessageBox and the other built-in Qt dialogs. + + \section1 Advanced Usage + + If the \l{QMessageBox::StandardButtons} {standard buttons} are not + flexible enough for your message box, you can use the addButton() + overload that takes a text and a ButtonRoleto to add custom + buttons. The ButtonRole is used by QMessageBox to determine the + ordering of the buttons on screen (which varies according to the + platform). You can test the value of clickedButton() after calling + exec(). For example, + + \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 2 + + \section1 Default and Escape Keys + + The default button (i.e., the button activated when \key Enter is + pressed) can be specified using setDefaultButton(). If a default + button is not specified, QMessageBox tries to find one based on + the \l{ButtonRole} {button roles} of the buttons used in the + message box. + + The escape button (the button activated when \key Esc is pressed) + can be specified using setEscapeButton(). If an escape button is + not specified, QMessageBox tries to find one using these rules: + + \list 1 + + \o If there is only one button, it is the button activated when + \key Esc is pressed. + + \o If there is a \l Cancel button, it is the button activated when + \key Esc is pressed. + + \o If there is exactly one button having either + \l{QMessageBox::RejectRole} {the Reject role} or the + \l{QMessageBox::NoRole} {the No role}, it is the button + activated when \key Esc is pressed. + + \endlist + + When an escape button can't be determined using these rules, + pressing \key Esc has no effect. + + \sa QDialogButtonBox, {fowler}{GUI Design Handbook: Message Box}, {Standard Dialogs Example}, {Application Example} +*/ + +/*! + \enum QMessageBox::StandardButton + \since 4.2 + + These enums describe flags for standard buttons. Each button has a + defined \l ButtonRole. + + \value Ok An "OK" button defined with the \l AcceptRole. + \value Open A "Open" button defined with the \l AcceptRole. + \value Save A "Save" button defined with the \l AcceptRole. + \value Cancel A "Cancel" button defined with the \l RejectRole. + \value Close A "Close" button defined with the \l RejectRole. + \value Discard A "Discard" or "Don't Save" button, depending on the platform, + defined with the \l DestructiveRole. + \value Apply An "Apply" button defined with the \l ApplyRole. + \value Reset A "Reset" button defined with the \l ResetRole. + \value RestoreDefaults A "Restore Defaults" button defined with the \l ResetRole. + \value Help A "Help" button defined with the \l HelpRole. + \value SaveAll A "Save All" button defined with the \l AcceptRole. + \value Yes A "Yes" button defined with the \l YesRole. + \value YesToAll A "Yes to All" button defined with the \l YesRole. + \value No A "No" button defined with the \l NoRole. + \value NoToAll A "No to All" button defined with the \l NoRole. + \value Abort An "Abort" button defined with the \l RejectRole. + \value Retry A "Retry" button defined with the \l AcceptRole. + \value Ignore An "Ignore" button defined with the \l AcceptRole. + + \value NoButton An invalid button. + + \omitvalue FirstButton + \omitvalue LastButton + + The following values are obsolete: + + \value YesAll Use YesToAll instead. + \value NoAll Use NoToAll instead. + \value Default Use the \c defaultButton argument of + information(), warning(), etc. instead, or call + setDefaultButton(). + \value Escape Call setEscapeButton() instead. + \value FlagMask + \value ButtonMask + + \sa ButtonRole, standardButtons +*/ + +/*! + \fn void QMessageBox::buttonClicked(QAbstractButton *button) + + This signal is emitted whenever a button is clicked inside the QMessageBox. + The button that was clicked in returned in \a button. +*/ + +/*! + Constructs a message box with no text and no buttons. \a parent is + passed to the QDialog constructor. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + On Mac OS X, if \a parent is not 0 and you want your message box + to appear as a Qt::Sheet of that parent, set the message box's + \l{setWindowModality()} {window modality} to Qt::WindowModal + (default). Otherwise, the message box will be a standard dialog. + +*/ +QMessageBox::QMessageBox(QWidget *parent) + : QDialog(*new QMessageBoxPrivate, parent, Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) +{ + Q_D(QMessageBox); + d->init(); +} + +/*! + Constructs a message box with the given \a icon, \a title, \a + text, and standard \a buttons. Standard or custom buttons can be + added at any time using addButton(). The \a parent and \a f + arguments are passed to the QDialog constructor. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + On Mac OS X, if \a parent is not 0 and you want your message box + to appear as a Qt::Sheet of that parent, set the message box's + \l{setWindowModality()} {window modality} to Qt::WindowModal + (default). Otherwise, the message box will be a standard dialog. + + \sa setWindowTitle(), setText(), setIcon(), setStandardButtons() +*/ +QMessageBox::QMessageBox(Icon icon, const QString &title, const QString &text, + StandardButtons buttons, QWidget *parent, + Qt::WindowFlags f) +: QDialog(*new QMessageBoxPrivate, parent, f | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) +{ + Q_D(QMessageBox); + d->init(title, text); + setIcon(icon); + if (buttons != NoButton) + setStandardButtons(buttons); +} + +/*! + Destroys the message box. +*/ +QMessageBox::~QMessageBox() +{ +} + +/*! + \since 4.2 + + Adds the given \a button to the message box with the specified \a + role. + + \sa removeButton(), button(), setStandardButtons() +*/ +void QMessageBox::addButton(QAbstractButton *button, ButtonRole role) +{ + Q_D(QMessageBox); + if (!button) + return; + removeButton(button); + d->buttonBox->addButton(button, (QDialogButtonBox::ButtonRole)role); + d->customButtonList.append(button); + d->autoAddOkButton = false; +} + +/*! + \since 4.2 + \overload + + Creates a button with the given \a text, adds it to the message box for the + specified \a role, and returns it. +*/ +QPushButton *QMessageBox::addButton(const QString& text, ButtonRole role) +{ + Q_D(QMessageBox); + QPushButton *pushButton = new QPushButton(text); + addButton(pushButton, role); + d->updateSize(); + return pushButton; +} + +/*! + \since 4.2 + \overload + + Adds a standard \a button to the message box if it is valid to do so, and + returns the push button. + + \sa setStandardButtons() +*/ +QPushButton *QMessageBox::addButton(StandardButton button) +{ + Q_D(QMessageBox); + QPushButton *pushButton = d->buttonBox->addButton((QDialogButtonBox::StandardButton)button); + if (pushButton) + d->autoAddOkButton = false; + return pushButton; +} + +/*! + \since 4.2 + + Removes \a button from the button box without deleting it. + + \sa addButton(), setStandardButtons() +*/ +void QMessageBox::removeButton(QAbstractButton *button) +{ + Q_D(QMessageBox); + d->customButtonList.removeAll(button); + if (d->escapeButton == button) + d->escapeButton = 0; + if (d->defaultButton == button) + d->defaultButton = 0; + d->buttonBox->removeButton(button); + d->updateSize(); +} + +/*! + \property QMessageBox::standardButtons + \brief collection of standard buttons in the message box + \since 4.2 + + This property controls which standard buttons are used by the message box. + + By default, this property contains no standard buttons. + + \sa addButton() +*/ +void QMessageBox::setStandardButtons(StandardButtons buttons) +{ + Q_D(QMessageBox); + d->buttonBox->setStandardButtons(QDialogButtonBox::StandardButtons(int(buttons))); + + QList<QAbstractButton *> buttonList = d->buttonBox->buttons(); + if (!buttonList.contains(d->escapeButton)) + d->escapeButton = 0; + if (!buttonList.contains(d->defaultButton)) + d->defaultButton = 0; + d->autoAddOkButton = false; + d->updateSize(); +} + +QMessageBox::StandardButtons QMessageBox::standardButtons() const +{ + Q_D(const QMessageBox); + return QMessageBox::StandardButtons(int(d->buttonBox->standardButtons())); +} + +/*! + \since 4.2 + + Returns the standard button enum value corresponding to the given \a button, + or NoButton if the given \a button isn't a standard button. + + \sa button(), standardButtons() +*/ +QMessageBox::StandardButton QMessageBox::standardButton(QAbstractButton *button) const +{ + Q_D(const QMessageBox); + return (QMessageBox::StandardButton)d->buttonBox->standardButton(button); +} + +/*! + \since 4.2 + + Returns a pointer corresponding to the standard button \a which, + or 0 if the standard button doesn't exist in this message box. + + \sa standardButtons, standardButton() +*/ +QAbstractButton *QMessageBox::button(StandardButton which) const +{ + Q_D(const QMessageBox); + return d->buttonBox->button(QDialogButtonBox::StandardButton(which)); +} + +/*! + \since 4.2 + + Returns the button that is activated when escape is pressed. + + By default, QMessageBox attempts to automatically detect an + escape button as follows: + + \list 1 + \o If there is only one button, it is made the escape button. + \o If there is a \l Cancel button, it is made the escape button. + \o On Mac OS X only, if there is exactly one button with the role + QMessageBox::RejectRole, it is made the escape button. + \endlist + + When an escape button could not be automatically detected, pressing + \key Esc has no effect. + + \sa addButton() +*/ +QAbstractButton *QMessageBox::escapeButton() const +{ + Q_D(const QMessageBox); + return d->escapeButton; +} + +/*! + \since 4.2 + + Sets the button that gets activated when the \key Escape key is + pressed to \a button. + + \sa addButton(), clickedButton() +*/ +void QMessageBox::setEscapeButton(QAbstractButton *button) +{ + Q_D(QMessageBox); + if (d->buttonBox->buttons().contains(button)) + d->escapeButton = button; +} + +/*! + \since 4.3 + + Sets the buttons that gets activated when the \key Escape key is + pressed to \a button. + + \sa addButton(), clickedButton() +*/ +void QMessageBox::setEscapeButton(QMessageBox::StandardButton button) +{ + Q_D(QMessageBox); + setEscapeButton(d->buttonBox->button(QDialogButtonBox::StandardButton(button))); +} + +void QMessageBoxPrivate::detectEscapeButton() +{ + if (escapeButton) { // escape button explicitly set + detectedEscapeButton = escapeButton; + return; + } + + // Cancel button automatically becomes escape button + detectedEscapeButton = buttonBox->button(QDialogButtonBox::Cancel); + if (detectedEscapeButton) + return; + + // If there is only one button, make it the escape button + const QList<QAbstractButton *> buttons = buttonBox->buttons(); + if (buttons.count() == 1) { + detectedEscapeButton = buttons.first(); + return; + } + + // if the message box has one RejectRole button, make it the escape button + for (int i = 0; i < buttons.count(); i++) { + if (buttonBox->buttonRole(buttons.at(i)) == QDialogButtonBox::RejectRole) { + if (detectedEscapeButton) { // already detected! + detectedEscapeButton = 0; + break; + } + detectedEscapeButton = buttons.at(i); + } + } + if (detectedEscapeButton) + return; + + // if the message box has one NoRole button, make it the escape button + for (int i = 0; i < buttons.count(); i++) { + if (buttonBox->buttonRole(buttons.at(i)) == QDialogButtonBox::NoRole) { + if (detectedEscapeButton) { // already detected! + detectedEscapeButton = 0; + break; + } + detectedEscapeButton = buttons.at(i); + } + } +} + +/*! + \since 4.2 + + Returns the button that was clicked by the user, + or 0 if the user hit the \key Esc key and + no \l{setEscapeButton()}{escape button} was set. + + If exec() hasn't been called yet, returns 0. + + Example: + + \snippet doc/src/snippets/code/src_gui_dialogs_qmessagebox.cpp 3 + + \sa standardButton(), button() +*/ +QAbstractButton *QMessageBox::clickedButton() const +{ + Q_D(const QMessageBox); + return d->clickedButton; +} + +/*! + \since 4.2 + + Returns the button that should be the message box's + \l{QPushButton::setDefault()}{default button}. Returns 0 + if no default button was set. + + \sa addButton(), QPushButton::setDefault() +*/ +QPushButton *QMessageBox::defaultButton() const +{ + Q_D(const QMessageBox); + return d->defaultButton; +} + +/*! + \since 4.2 + + Sets the message box's \l{QPushButton::setDefault()}{default button} + to \a button. + + \sa addButton(), QPushButton::setDefault() +*/ +void QMessageBox::setDefaultButton(QPushButton *button) +{ + Q_D(QMessageBox); + if (!d->buttonBox->buttons().contains(button)) + return; + d->defaultButton = button; + button->setDefault(true); + button->setFocus(); +} + +/*! + \since 4.3 + + Sets the message box's \l{QPushButton::setDefault()}{default button} + to \a button. + + \sa addButton(), QPushButton::setDefault() +*/ +void QMessageBox::setDefaultButton(QMessageBox::StandardButton button) +{ + Q_D(QMessageBox); + setDefaultButton(d->buttonBox->button(QDialogButtonBox::StandardButton(button))); +} + +/*! + \property QMessageBox::text + \brief the message box text to be displayed. + + The text will be interpreted either as a plain text or as rich text, + depending on the text format setting (\l QMessageBox::textFormat). + The default setting is Qt::AutoText, i.e., the message box will try + to auto-detect the format of the text. + + The default value of this property is an empty string. + + \sa textFormat, QMessageBox::informativeText, QMessageBox::detailedText +*/ +QString QMessageBox::text() const +{ + Q_D(const QMessageBox); + return d->label->text(); +} + +void QMessageBox::setText(const QString &text) +{ + Q_D(QMessageBox); + d->label->setText(text); + d->label->setWordWrap(d->label->textFormat() == Qt::RichText + || (d->label->textFormat() == Qt::AutoText && Qt::mightBeRichText(text))); + d->updateSize(); +} + +/*! + \enum QMessageBox::Icon + + This enum has the following values: + + \value NoIcon the message box does not have any icon. + + \value Question an icon indicating that + the message is asking a question. + + \value Information an icon indicating that + the message is nothing out of the ordinary. + + \value Warning an icon indicating that the + message is a warning, but can be dealt with. + + \value Critical an icon indicating that + the message represents a critical problem. + +*/ + +/*! + \property QMessageBox::icon + \brief the message box's icon + + The icon of the message box can be specified with one of the + values: + + \list + \o QMessageBox::NoIcon + \o QMessageBox::Question + \o QMessageBox::Information + \o QMessageBox::Warning + \o QMessageBox::Critical + \endlist + + The default is QMessageBox::NoIcon. + + The pixmap used to display the actual icon depends on the current + \l{QWidget::style()} {GUI style}. You can also set a custom pixmap + for the icon by setting the \l{QMessageBox::iconPixmap} {icon + pixmap} property. + + \sa iconPixmap +*/ +QMessageBox::Icon QMessageBox::icon() const +{ + Q_D(const QMessageBox); + return d->icon; +} + +void QMessageBox::setIcon(Icon icon) +{ + Q_D(QMessageBox); + setIconPixmap(QMessageBoxPrivate::standardIcon((QMessageBox::Icon)icon, + this)); + d->icon = icon; +} + +/*! + \property QMessageBox::iconPixmap + \brief the current icon + + The icon currently used by the message box. Note that it's often + hard to draw one pixmap that looks appropriate in all GUI styles; + you may want to supply a different pixmap for each platform. + + By default, this property is undefined. + + \sa icon +*/ +QPixmap QMessageBox::iconPixmap() const +{ + Q_D(const QMessageBox); + if (d->iconLabel && d->iconLabel->pixmap()) + return *d->iconLabel->pixmap(); + return QPixmap(); +} + +void QMessageBox::setIconPixmap(const QPixmap &pixmap) +{ + Q_D(QMessageBox); + d->iconLabel->setPixmap(pixmap); + d->updateSize(); + d->icon = NoIcon; +} + +/*! + \property QMessageBox::textFormat + \brief the format of the text displayed by the message box + + The current text format used by the message box. See the \l + Qt::TextFormat enum for an explanation of the possible options. + + The default format is Qt::AutoText. + + \sa setText() +*/ +Qt::TextFormat QMessageBox::textFormat() const +{ + Q_D(const QMessageBox); + return d->label->textFormat(); +} + +void QMessageBox::setTextFormat(Qt::TextFormat format) +{ + Q_D(QMessageBox); + d->label->setTextFormat(format); + d->label->setWordWrap(format == Qt::RichText + || (format == Qt::AutoText && Qt::mightBeRichText(d->label->text()))); + d->updateSize(); +} + +/*! + \reimp +*/ +bool QMessageBox::event(QEvent *e) +{ + bool result =QDialog::event(e); + switch (e->type()) { + case QEvent::LayoutRequest: + d_func()->updateSize(); + break; + case QEvent::LanguageChange: + d_func()->retranslateStrings(); + break; +#ifdef Q_OS_WINCE + case QEvent::OkRequest: + case QEvent::HelpRequest: { + QString bName = + (e->type() == QEvent::OkRequest) + ? qApp->translate("QMessageBox", "OK") + : qApp->translate("QMessageBox", "Help"); + QList<QPushButton*> list = qFindChildren<QPushButton*>(this); + for (int i=0; i<list.size(); ++i) { + QPushButton *pb = list.at(i); + if (pb->text() == bName) { + if (pb->isEnabled()) + pb->click(); + return pb->isEnabled(); + } + } + } +#endif + default: + break; + } + return result; +} + +/*! + \reimp +*/ +void QMessageBox::resizeEvent(QResizeEvent *event) +{ + QDialog::resizeEvent(event); +} + +/*! + \reimp +*/ +void QMessageBox::closeEvent(QCloseEvent *e) +{ + Q_D(QMessageBox); + if (!d->detectedEscapeButton) { + e->ignore(); + return; + } + QDialog::closeEvent(e); + d->clickedButton = d->detectedEscapeButton; + setResult(d->execReturnCode(d->detectedEscapeButton)); +} + +/*! + \reimp +*/ +void QMessageBox::changeEvent(QEvent *ev) +{ + Q_D(QMessageBox); + switch (ev->type()) { + case QEvent::StyleChange: + { + if (d->icon != NoIcon) + setIcon(d->icon); + Qt::TextInteractionFlags flags(style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, 0, this)); + d->label->setTextInteractionFlags(flags); + d->buttonBox->setCenterButtons(style()->styleHint(QStyle::SH_MessageBox_CenterButtons, 0, this)); + if (d->informativeLabel) + d->informativeLabel->setTextInteractionFlags(flags); + // intentional fall through + } + case QEvent::FontChange: + case QEvent::ApplicationFontChange: +#ifdef Q_WS_MAC + { + QFont f = font(); + f.setBold(true); + d->label->setFont(f); + } +#endif + default: + break; + } + QDialog::changeEvent(ev); +} + +/*! + \reimp +*/ +void QMessageBox::keyPressEvent(QKeyEvent *e) +{ + Q_D(QMessageBox); + if (e->key() == Qt::Key_Escape +#ifdef Q_WS_MAC + || (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) +#endif + ) { + if (d->detectedEscapeButton) { +#ifdef Q_WS_MAC + d->detectedEscapeButton->animateClick(); +#else + d->detectedEscapeButton->click(); +#endif + } + return; + } + +#if defined (Q_OS_WIN) && !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_SHORTCUT) + if (e == QKeySequence::Copy) { + QString separator = QString::fromLatin1("---------------------------\n"); + QString textToCopy = separator; + separator.prepend(QLatin1String("\n")); + textToCopy += windowTitle() + separator; // title + textToCopy += d->label->text() + separator; // text + + if (d->informativeLabel) + textToCopy += d->informativeLabel->text() + separator; + + QString buttonTexts; + QList<QAbstractButton *> buttons = d->buttonBox->buttons(); + for (int i = 0; i < buttons.count(); i++) { + buttonTexts += buttons[i]->text() + QLatin1String(" "); + } + textToCopy += buttonTexts + separator; + + qApp->clipboard()->setText(textToCopy); + return; + } +#endif //QT_NO_SHORTCUT QT_NO_CLIPBOARD Q_OS_WIN + +#ifndef QT_NO_SHORTCUT + if (!(e->modifiers() & Qt::AltModifier)) { + int key = e->key() & ~((int)Qt::MODIFIER_MASK|(int)Qt::UNICODE_ACCEL); + if (key) { + const QList<QAbstractButton *> buttons = d->buttonBox->buttons(); + for (int i = 0; i < buttons.count(); ++i) { + QAbstractButton *pb = buttons.at(i); + int acc = pb->shortcut() & ~((int)Qt::MODIFIER_MASK|(int)Qt::UNICODE_ACCEL); + if (acc == key) { + pb->animateClick(); + return; + } + } + } + } +#endif + QDialog::keyPressEvent(e); +} + +#ifdef Q_OS_WINCE +/*! + \reimp +*/ +void QMessageBox::setVisible(bool visible) +{ + Q_D(QMessageBox); + if (visible) + d->hideSpecial(); + QDialog::setVisible(visible); +} +#endif + + +/*! + \overload + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QMessageBox::open(QObject *receiver, const char *member) +{ + Q_D(QMessageBox); + const char *signal = member && strchr(member, '*') ? SIGNAL(buttonClicked(QAbstractButton*)) + : SIGNAL(finished(int)); + connect(this, signal, receiver, member); + d->signalToDisconnectOnClose = signal; + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +/*! + \since 4.5 + + Returns a list of all the buttons that have been added to the message box. + + \sa buttonRole(), addButton(), removeButton() +*/ +QList<QAbstractButton *> QMessageBox::buttons() const +{ + Q_D(const QMessageBox); + return d->buttonBox->buttons(); +} + +/*! + \since 4.5 + + Returns the button role for the specified \a button. This function returns + \l InvalidRole if \a button is 0 or has not been added to the message box. + + \sa buttons(), addButton() +*/ +QMessageBox::ButtonRole QMessageBox::buttonRole(QAbstractButton *button) const +{ + Q_D(const QMessageBox); + return QMessageBox::ButtonRole(d->buttonBox->buttonRole(button)); +} + +/*! + \reimp +*/ +void QMessageBox::showEvent(QShowEvent *e) +{ + Q_D(QMessageBox); + if (d->autoAddOkButton) { + addButton(Ok); +#if defined(Q_OS_WINCE) + d->hideSpecial(); +#endif + } + if (d->detailsButton) + addButton(d->detailsButton, QMessageBox::ActionRole); + d->detectEscapeButton(); + d->updateSize(); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::Alert); +#endif +#ifdef Q_WS_WIN + HMENU systemMenu = GetSystemMenu((HWND)winId(), FALSE); + if (!d->detectedEscapeButton) { + EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED); + } + else { + EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED); + } +#endif + QDialog::showEvent(e); +} + + +static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, + QMessageBox::Icon icon, + const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) +{ + // necessary for source compatibility with Qt 4.0 and 4.1 + // handles (Yes, No) and (Yes|Default, No) + if (defaultButton && !(buttons & defaultButton)) + return (QMessageBox::StandardButton) + QMessageBoxPrivate::showOldMessageBox(parent, icon, title, + text, int(buttons), + int(defaultButton), 0); + + QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent); + QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&msgBox); + Q_ASSERT(buttonBox != 0); + + uint mask = QMessageBox::FirstButton; + while (mask <= QMessageBox::LastButton) { + uint sb = buttons & mask; + mask <<= 1; + if (!sb) + continue; + QPushButton *button = msgBox.addButton((QMessageBox::StandardButton)sb); + // Choose the first accept role as the default + if (msgBox.defaultButton()) + continue; + if ((defaultButton == QMessageBox::NoButton && buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) + || (defaultButton != QMessageBox::NoButton && sb == uint(defaultButton))) + msgBox.setDefaultButton(button); + } + if (msgBox.exec() == -1) + return QMessageBox::Cancel; + return msgBox.standardButton(msgBox.clickedButton()); +} + +/*! + \since 4.2 + + Opens an information message box with the specified \a title and + \a text. The standard \a buttons are added to the message box. \a + defaultButton specifies the button used when \key Enter is + pressed. \a defaultButton must refer to a button that was given in \a buttons. + If \a defaultButton is QMessageBox::NoButton, QMessageBox + chooses a suitable default automatically. + + Returns the identity of the standard button that was clicked. If + \key Esc was pressed instead, the \l{Default and Escape Keys} + {escape button} is returned. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \sa question(), warning(), critical() +*/ +QMessageBox::StandardButton QMessageBox::information(QWidget *parent, const QString &title, + const QString& text, StandardButtons buttons, + StandardButton defaultButton) +{ + return showNewMessageBox(parent, Information, title, text, buttons, + defaultButton); +} + + +/*! + \since 4.2 + + Opens a question message box with the specified \a title and \a + text. The standard \a buttons are added to the message box. \a + defaultButton specifies the button used when \key Enter is + pressed. \a defaultButton must refer to a button that was given in \a buttons. + If \a defaultButton is QMessageBox::NoButton, QMessageBox + chooses a suitable default automatically. + + Returns the identity of the standard button that was clicked. If + \key Esc was pressed instead, the \l{Default and Escape Keys} + {escape button} is returned. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \sa information(), warning(), critical() +*/ +QMessageBox::StandardButton QMessageBox::question(QWidget *parent, const QString &title, + const QString& text, StandardButtons buttons, + StandardButton defaultButton) +{ + return showNewMessageBox(parent, Question, title, text, buttons, defaultButton); +} + +/*! + \since 4.2 + + Opens a warning message box with the specified \a title and \a + text. The standard \a buttons are added to the message box. \a + defaultButton specifies the button used when \key Enter is + pressed. \a defaultButton must refer to a button that was given in \a buttons. + If \a defaultButton is QMessageBox::NoButton, QMessageBox + chooses a suitable default automatically. + + Returns the identity of the standard button that was clicked. If + \key Esc was pressed instead, the \l{Default and Escape Keys} + {escape button} is returned. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \sa question(), information(), critical() +*/ +QMessageBox::StandardButton QMessageBox::warning(QWidget *parent, const QString &title, + const QString& text, StandardButtons buttons, + StandardButton defaultButton) +{ + return showNewMessageBox(parent, Warning, title, text, buttons, defaultButton); +} + +/*! + \since 4.2 + + Opens a critical message box with the specified \a title and \a + text. The standard \a buttons are added to the message box. \a + defaultButton specifies the button used when \key Enter is + pressed. \a defaultButton must refer to a button that was given in \a buttons. + If \a defaultButton is QMessageBox::NoButton, QMessageBox + chooses a suitable default automatically. + + Returns the identity of the standard button that was clicked. If + \key Esc was pressed instead, the \l{Default and Escape Keys} + {escape button} is returned. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa question(), warning(), information() +*/ +QMessageBox::StandardButton QMessageBox::critical(QWidget *parent, const QString &title, + const QString& text, StandardButtons buttons, + StandardButton defaultButton) +{ + return showNewMessageBox(parent, Critical, title, text, buttons, defaultButton); +} + +/*! + Displays a simple about box with title \a title and text \a + text. The about box's parent is \a parent. + + about() looks for a suitable icon in four locations: + + \list 1 + \o It prefers \link QWidget::windowIcon() parent->icon() \endlink + if that exists. + \o If not, it tries the top-level widget containing \a parent. + \o If that fails, it tries the \link + QApplication::activeWindow() active window. \endlink + \o As a last resort it uses the Information icon. + \endlist + + The about box has a single button labelled "OK". On Mac OS X, the + about box is popped up as a modeless window; on other platforms, + it is currently a window modal. + + \sa QWidget::windowIcon(), QApplication::activeWindow() +*/ +void QMessageBox::about(QWidget *parent, const QString &title, const QString &text) +{ +#ifdef Q_WS_MAC + static QPointer<QMessageBox> oldMsgBox; + + if (oldMsgBox && oldMsgBox->text() == text) { + oldMsgBox->show(); + oldMsgBox->raise(); + oldMsgBox->activateWindow(); + return; + } +#endif + + QMessageBox *msgBox = new QMessageBox(title, text, Information, 0, 0, 0, parent +#ifdef Q_WS_MAC + , Qt::WindowTitleHint | Qt::WindowSystemMenuHint +#endif + ); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + QIcon icon = msgBox->windowIcon(); + QSize size = icon.actualSize(QSize(64, 64)); + msgBox->setIconPixmap(icon.pixmap(size)); + + // should perhaps be a style hint +#ifdef Q_WS_MAC + oldMsgBox = msgBox; +#if 0 + // ### doesn't work until close button is enabled in title bar + msgBox->d_func()->autoAddOkButton = false; +#else + msgBox->d_func()->buttonBox->setCenterButtons(true); +#endif + msgBox->show(); +#else + msgBox->exec(); +#endif +} + +/*! + Displays a simple message box about Qt, with the given \a title + and centered over \a parent (if \a parent is not 0). The message + includes the version number of Qt being used by the application. + + This is useful for inclusion in the \gui Help menu of an application, + as shown in the \l{mainwindows/menus}{Menus} example. + + QApplication provides this functionality as a slot. + + On Mac OS X, the about box is popped up as a modeless window; on + other platforms, it is currently window modal. + + \sa QApplication::aboutQt() +*/ +void QMessageBox::aboutQt(QWidget *parent, const QString &title) +{ +#ifdef Q_WS_MAC + static QPointer<QMessageBox> oldMsgBox; + + if (oldMsgBox) { + oldMsgBox->show(); + oldMsgBox->raise(); + oldMsgBox->activateWindow(); + return; + } +#endif + + QString translatedTextAboutQt; + translatedTextAboutQt = QMessageBox::tr( + "<h3>About Qt</h3>" + "%1<p>Qt is a C++ toolkit for cross-platform " + "application development.</p>" + "<p>Qt provides single-source " + "portability across MS Windows, Mac OS X, " + "Linux, and all major commercial Unix variants. Qt is also" + " available for embedded devices as Qt for Embedded Linux" + " and Qt for Windows CE.</p>" + "<p>Qt is a Nokia product. See " + "<a href=\"http://qtsoftware.com/qt/\">qtsoftware.com/qt/</a> for more information.</p>" + ) +#if QT_EDITION != QT_EDITION_OPENSOURCE + .arg(QMessageBox::tr("<p>This program uses Qt version %1.</p>")) +#else + .arg(QMessageBox::tr("<p>This program uses Qt Open Source Edition version %1.</p>" + "<p>Qt Open Source Edition is intended for the development " + "of Open Source applications. You need a commercial Qt " + "license for development of proprietary (closed source) " + "applications.</p>" + "<p>Please see <a href=\"http://qtsoftware.com/company/model/\">qtsoftware.com/company/model/</a> " + "for an overview of Qt licensing.</p>")) +#endif + + .arg(QLatin1String(QT_VERSION_STR)); + + QMessageBox *msgBox = new QMessageBox(parent); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setWindowTitle(title.isEmpty() ? tr("About Qt") : title); + msgBox->setText(translatedTextAboutQt); + + QPixmap pm(QLatin1String(":/trolltech/qmessagebox/images/qtlogo-64.png")); + if (!pm.isNull()) + msgBox->setIconPixmap(pm); +#if defined(Q_OS_WINCE) + msgBox->setDefaultButton(msgBox->addButton(QMessageBox::Ok)); +#endif + + // should perhaps be a style hint +#ifdef Q_WS_MAC + oldMsgBox = msgBox; +#if 0 + // ### doesn't work until close button is enabled in title bar + msgBox->d_func()->autoAddOkButton = false; +#else + msgBox->d_func()->buttonBox->setCenterButtons(true); +#endif + msgBox->show(); +#else + msgBox->exec(); +#endif +} + +/*! + \internal +*/ +QSize QMessageBox::sizeHint() const +{ + // ### Qt 5: remove + return QDialog::sizeHint(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Source and binary compatibility routines for 4.0 and 4.1 + +static QMessageBox::StandardButton newButton(int button) +{ + // this is needed for source compatibility with Qt 4.0 and 4.1 + if (button == QMessageBox::NoButton || (button & NewButtonMask)) + return QMessageBox::StandardButton(button & QMessageBox::ButtonMask); + +#if QT_VERSION < 0x050000 + // this is needed for binary compatibility with Qt 4.0 and 4.1 + switch (button & Old_ButtonMask) { + case Old_Ok: + return QMessageBox::Ok; + case Old_Cancel: + return QMessageBox::Cancel; + case Old_Yes: + return QMessageBox::Yes; + case Old_No: + return QMessageBox::No; + case Old_Abort: + return QMessageBox::Abort; + case Old_Retry: + return QMessageBox::Retry; + case Old_Ignore: + return QMessageBox::Ignore; + case Old_YesAll: + return QMessageBox::YesToAll; + case Old_NoAll: + return QMessageBox::NoToAll; + default: + return QMessageBox::NoButton; + } +#endif +} + +static bool detectedCompat(int button0, int button1, int button2) +{ + if (button0 != 0 && !(button0 & NewButtonMask)) + return true; + if (button1 != 0 && !(button1 & NewButtonMask)) + return true; + if (button2 != 0 && !(button2 & NewButtonMask)) + return true; + return false; +} + +QAbstractButton *QMessageBoxPrivate::findButton(int button0, int button1, int button2, int flags) +{ + Q_Q(QMessageBox); + int button = 0; + + if (button0 & flags) { + button = button0; + } else if (button1 & flags) { + button = button1; + } else if (button2 & flags) { + button = button2; + } + return q->button(newButton(button)); +} + +void QMessageBoxPrivate::addOldButtons(int button0, int button1, int button2) +{ + Q_Q(QMessageBox); + q->addButton(newButton(button0)); + q->addButton(newButton(button1)); + q->addButton(newButton(button2)); + q->setDefaultButton( + static_cast<QPushButton *>(findButton(button0, button1, button2, QMessageBox::Default))); + q->setEscapeButton(findButton(button0, button1, button2, QMessageBox::Escape)); + compatMode = detectedCompat(button0, button1, button2); +} + +QAbstractButton *QMessageBoxPrivate::abstractButtonForId(int id) const +{ + Q_Q(const QMessageBox); + QAbstractButton *result = customButtonList.value(id); + if (result) + return result; + if (id & QMessageBox::FlagMask) // for compatibility with Qt 4.0/4.1 (even if it is silly) + return 0; + return q->button(newButton(id)); +} + +int QMessageBoxPrivate::showOldMessageBox(QWidget *parent, QMessageBox::Icon icon, + const QString &title, const QString &text, + int button0, int button1, int button2) +{ + QMessageBox messageBox(icon, title, text, QMessageBox::NoButton, parent); + messageBox.d_func()->addOldButtons(button0, button1, button2); + return messageBox.exec(); +} + +int QMessageBoxPrivate::showOldMessageBox(QWidget *parent, QMessageBox::Icon icon, + const QString &title, const QString &text, + const QString &button0Text, + const QString &button1Text, + const QString &button2Text, + int defaultButtonNumber, + int escapeButtonNumber) +{ + QMessageBox messageBox(icon, title, text, QMessageBox::NoButton, parent); + QString myButton0Text = button0Text; + if (myButton0Text.isEmpty()) + myButton0Text = QDialogButtonBox::tr("OK"); + messageBox.addButton(myButton0Text, QMessageBox::ActionRole); + if (!button1Text.isEmpty()) + messageBox.addButton(button1Text, QMessageBox::ActionRole); + if (!button2Text.isEmpty()) + messageBox.addButton(button2Text, QMessageBox::ActionRole); + + const QList<QAbstractButton *> &buttonList = messageBox.d_func()->customButtonList; + messageBox.setDefaultButton(static_cast<QPushButton *>(buttonList.value(defaultButtonNumber))); + messageBox.setEscapeButton(buttonList.value(escapeButtonNumber)); + + return messageBox.exec(); +} + +void QMessageBoxPrivate::retranslateStrings() +{ +#ifndef QT_NO_TEXTEDIT + if (detailsButton) + detailsButton->setText(detailsText->isHidden() ? detailsText->label(HideLabel) : detailsText->label(ShowLabel)); +#endif +} + +/*! + \obsolete + + Constructs a message box with a \a title, a \a text, an \a icon, + and up to three buttons. + + The \a icon must be one of the following: + \list + \o QMessageBox::NoIcon + \o QMessageBox::Question + \o QMessageBox::Information + \o QMessageBox::Warning + \o QMessageBox::Critical + \endlist + + Each button, \a button0, \a button1 and \a button2, can have one + of the following values: + \list + \o QMessageBox::NoButton + \o QMessageBox::Ok + \o QMessageBox::Cancel + \o QMessageBox::Yes + \o QMessageBox::No + \o QMessageBox::Abort + \o QMessageBox::Retry + \o QMessageBox::Ignore + \o QMessageBox::YesAll + \o QMessageBox::NoAll + \endlist + + Use QMessageBox::NoButton for the later parameters to have fewer + than three buttons in your message box. If you don't specify any + buttons at all, QMessageBox will provide an Ok button. + + One of the buttons can be OR-ed with the QMessageBox::Default + flag to make it the default button (clicked when Enter is + pressed). + + One of the buttons can be OR-ed with the QMessageBox::Escape flag + to make it the cancel or close button (clicked when \key Esc is + pressed). + + \snippet doc/src/snippets/dialogs/dialogs.cpp 2 + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + The \a parent and \a f arguments are passed to + the QDialog constructor. + + \sa setWindowTitle(), setText(), setIcon() +*/ +QMessageBox::QMessageBox(const QString &title, const QString &text, Icon icon, + int button0, int button1, int button2, QWidget *parent, + Qt::WindowFlags f) + : QDialog(*new QMessageBoxPrivate, parent, + f /*| Qt::MSWindowsFixedSizeDialogHint #### */| Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) +{ + Q_D(QMessageBox); + d->init(title, text); + setIcon(icon); + d->addOldButtons(button0, button1, button2); +} + +/*! + \obsolete + + Opens an information message box with the given \a title and the + \a text. The dialog may have up to three buttons. Each of the + buttons, \a button0, \a button1 and \a button2 may be set to one + of the following values: + + \list + \o QMessageBox::NoButton + \o QMessageBox::Ok + \o QMessageBox::Cancel + \o QMessageBox::Yes + \o QMessageBox::No + \o QMessageBox::Abort + \o QMessageBox::Retry + \o QMessageBox::Ignore + \o QMessageBox::YesAll + \o QMessageBox::NoAll + \endlist + + If you don't want all three buttons, set the last button, or last + two buttons to QMessageBox::NoButton. + + One button can be OR-ed with QMessageBox::Default, and one + button can be OR-ed with QMessageBox::Escape. + + Returns the identity (QMessageBox::Ok, or QMessageBox::No, etc.) + of the button that was clicked. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa question(), warning(), critical() +*/ +int QMessageBox::information(QWidget *parent, const QString &title, const QString& text, + int button0, int button1, int button2) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Information, title, text, + button0, button1, button2); +} + +/*! + \obsolete + \overload + + Displays an information message box with the given \a title and + \a text, as well as one, two or three buttons. Returns the index + of the button that was clicked (0, 1 or 2). + + \a button0Text is the text of the first button, and is optional. + If \a button0Text is not supplied, "OK" (translated) will be + used. \a button1Text is the text of the second button, and is + optional. \a button2Text is the text of the third button, and is + optional. \a defaultButtonNumber (0, 1 or 2) is the index of the + default button; pressing Return or Enter is the same as clicking + the default button. It defaults to 0 (the first button). \a + escapeButtonNumber is the index of the escape button; pressing + \key Esc is the same as clicking this button. It defaults to -1; + supply 0, 1 or 2 to make pressing \key Esc equivalent to clicking + the relevant button. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa question(), warning(), critical() +*/ + +int QMessageBox::information(QWidget *parent, const QString &title, const QString& text, + const QString& button0Text, const QString& button1Text, + const QString& button2Text, int defaultButtonNumber, + int escapeButtonNumber) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Information, title, text, + button0Text, button1Text, button2Text, + defaultButtonNumber, escapeButtonNumber); +} + +/*! + \obsolete + + Opens a question message box with the given \a title and \a text. + The dialog may have up to three buttons. Each of the buttons, \a + button0, \a button1 and \a button2 may be set to one of the + following values: + + \list + \o QMessageBox::NoButton + \o QMessageBox::Ok + \o QMessageBox::Cancel + \o QMessageBox::Yes + \o QMessageBox::No + \o QMessageBox::Abort + \o QMessageBox::Retry + \o QMessageBox::Ignore + \o QMessageBox::YesAll + \o QMessageBox::NoAll + \endlist + + If you don't want all three buttons, set the last button, or last + two buttons to QMessageBox::NoButton. + + One button can be OR-ed with QMessageBox::Default, and one + button can be OR-ed with QMessageBox::Escape. + + Returns the identity (QMessageBox::Yes, or QMessageBox::No, etc.) + of the button that was clicked. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa information(), warning(), critical() +*/ +int QMessageBox::question(QWidget *parent, const QString &title, const QString& text, + int button0, int button1, int button2) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Question, title, text, + button0, button1, button2); +} + +/*! + \obsolete + \overload + + Displays a question message box with the given \a title and \a + text, as well as one, two or three buttons. Returns the index of + the button that was clicked (0, 1 or 2). + + \a button0Text is the text of the first button, and is optional. + If \a button0Text is not supplied, "OK" (translated) will be used. + \a button1Text is the text of the second button, and is optional. + \a button2Text is the text of the third button, and is optional. + \a defaultButtonNumber (0, 1 or 2) is the index of the default + button; pressing Return or Enter is the same as clicking the + default button. It defaults to 0 (the first button). \a + escapeButtonNumber is the index of the Escape button; pressing + Escape is the same as clicking this button. It defaults to -1; + supply 0, 1 or 2 to make pressing Escape equivalent to clicking + the relevant button. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa information(), warning(), critical() +*/ +int QMessageBox::question(QWidget *parent, const QString &title, const QString& text, + const QString& button0Text, const QString& button1Text, + const QString& button2Text, int defaultButtonNumber, + int escapeButtonNumber) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Question, title, text, + button0Text, button1Text, button2Text, + defaultButtonNumber, escapeButtonNumber); +} + + +/*! + \obsolete + + Opens a warning message box with the given \a title and \a text. + The dialog may have up to three buttons. Each of the button + parameters, \a button0, \a button1 and \a button2 may be set to + one of the following values: + + \list + \o QMessageBox::NoButton + \o QMessageBox::Ok + \o QMessageBox::Cancel + \o QMessageBox::Yes + \o QMessageBox::No + \o QMessageBox::Abort + \o QMessageBox::Retry + \o QMessageBox::Ignore + \o QMessageBox::YesAll + \o QMessageBox::NoAll + \endlist + + If you don't want all three buttons, set the last button, or last + two buttons to QMessageBox::NoButton. + + One button can be OR-ed with QMessageBox::Default, and one + button can be OR-ed with QMessageBox::Escape. + + Returns the identity (QMessageBox::Ok or QMessageBox::No or ...) + of the button that was clicked. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa information(), question(), critical() +*/ +int QMessageBox::warning(QWidget *parent, const QString &title, const QString& text, + int button0, int button1, int button2) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Warning, title, text, + button0, button1, button2); +} + +/*! + \obsolete + \overload + + Displays a warning message box with the given \a title and \a + text, as well as one, two, or three buttons. Returns the number + of the button that was clicked (0, 1, or 2). + + \a button0Text is the text of the first button, and is optional. + If \a button0Text is not supplied, "OK" (translated) will be used. + \a button1Text is the text of the second button, and is optional, + and \a button2Text is the text of the third button, and is + optional. \a defaultButtonNumber (0, 1 or 2) is the index of the + default button; pressing Return or Enter is the same as clicking + the default button. It defaults to 0 (the first button). \a + escapeButtonNumber is the index of the Escape button; pressing + Escape is the same as clicking this button. It defaults to -1; + supply 0, 1, or 2 to make pressing Escape equivalent to clicking + the relevant button. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa information(), question(), critical() +*/ +int QMessageBox::warning(QWidget *parent, const QString &title, const QString& text, + const QString& button0Text, const QString& button1Text, + const QString& button2Text, int defaultButtonNumber, + int escapeButtonNumber) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Warning, title, text, + button0Text, button1Text, button2Text, + defaultButtonNumber, escapeButtonNumber); +} + +/*! + \obsolete + + Opens a critical message box with the given \a title and \a text. + The dialog may have up to three buttons. Each of the button + parameters, \a button0, \a button1 and \a button2 may be set to + one of the following values: + + \list + \o QMessageBox::NoButton + \o QMessageBox::Ok + \o QMessageBox::Cancel + \o QMessageBox::Yes + \o QMessageBox::No + \o QMessageBox::Abort + \o QMessageBox::Retry + \o QMessageBox::Ignore + \o QMessageBox::YesAll + \o QMessageBox::NoAll + \endlist + + If you don't want all three buttons, set the last button, or last + two buttons to QMessageBox::NoButton. + + One button can be OR-ed with QMessageBox::Default, and one + button can be OR-ed with QMessageBox::Escape. + + Returns the identity (QMessageBox::Ok, or QMessageBox::No, etc.) + of the button that was clicked. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa information(), question(), warning() +*/ + +int QMessageBox::critical(QWidget *parent, const QString &title, const QString& text, + int button0, int button1, int button2) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Critical, title, text, + button0, button1, button2); +} + +/*! + \obsolete + \overload + + Displays a critical error message box with the given \a title and + \a text, as well as one, two, or three buttons. Returns the + number of the button that was clicked (0, 1 or 2). + + \a button0Text is the text of the first button, and is optional. + If \a button0Text is not supplied, "OK" (translated) will be used. + \a button1Text is the text of the second button, and is optional, + and \a button2Text is the text of the third button, and is + optional. \a defaultButtonNumber (0, 1 or 2) is the index of the + default button; pressing Return or Enter is the same as clicking + the default button. It defaults to 0 (the first button). \a + escapeButtonNumber is the index of the Escape button; pressing + Escape is the same as clicking this button. It defaults to -1; + supply 0, 1, or 2 to make pressing Escape equivalent to clicking + the relevant button. + + If \a parent is 0, the message box is an \l{Qt::ApplicationModal} + {application modal} dialog box. If \a parent is a widget, the + message box is \l{Qt::WindowModal} {window modal} relative to \a + parent. + + \warning Do not delete \a parent during the execution of the dialog. + If you want to do this, you should create the dialog + yourself using one of the QMessageBox constructors. + + \sa information(), question(), warning() +*/ +int QMessageBox::critical(QWidget *parent, const QString &title, const QString& text, + const QString& button0Text, const QString& button1Text, + const QString& button2Text, int defaultButtonNumber, + int escapeButtonNumber) +{ + return QMessageBoxPrivate::showOldMessageBox(parent, Critical, title, text, + button0Text, button1Text, button2Text, + defaultButtonNumber, escapeButtonNumber); +} + + +/*! + \obsolete + + Returns the text of the message box button \a button, or + an empty string if the message box does not contain the button. + + Use button() and QPushButton::text() instead. +*/ +QString QMessageBox::buttonText(int button) const +{ + Q_D(const QMessageBox); + + if (QAbstractButton *abstractButton = d->abstractButtonForId(button)) { + return abstractButton->text(); + } else if (d->buttonBox->buttons().isEmpty() && (button == Ok || button == Old_Ok)) { + // for compatibility with Qt 4.0/4.1 + return QDialogButtonBox::tr("OK"); + } + return QString(); +} + +/*! + \obsolete + + Sets the text of the message box button \a button to \a text. + Setting the text of a button that is not in the message box is + silently ignored. + + Use addButton() instead. +*/ +void QMessageBox::setButtonText(int button, const QString &text) +{ + Q_D(QMessageBox); + if (QAbstractButton *abstractButton = d->abstractButtonForId(button)) { + abstractButton->setText(text); + } else if (d->buttonBox->buttons().isEmpty() && (button == Ok || button == Old_Ok)) { + // for compatibility with Qt 4.0/4.1 + addButton(QMessageBox::Ok)->setText(text); + } +} + +#ifndef QT_NO_TEXTEDIT +/*! + \property QMessageBox::detailedText + \brief the text to be displayed in the details area. + \since 4.2 + + The text will be interpreted as a plain text. + + By default, this property contains an empty string. + + \sa QMessageBox::text, QMessageBox::informativeText +*/ +QString QMessageBox::detailedText() const +{ + Q_D(const QMessageBox); + return d->detailsText ? d->detailsText->text() : QString(); +} + +void QMessageBox::setDetailedText(const QString &text) +{ + Q_D(QMessageBox); + if (text.isEmpty()) { + delete d->detailsText; + d->detailsText = 0; + removeButton(d->detailsButton); + delete d->detailsButton; + d->detailsButton = 0; + return; + } + + if (!d->detailsText) { + d->detailsText = new QMessageBoxDetailsText(this); + QGridLayout* grid = qobject_cast<QGridLayout*>(layout()); + if (grid) + grid->addWidget(d->detailsText, grid->rowCount(), 0, 1, grid->columnCount()); + d->detailsText->hide(); + } + if (!d->detailsButton) { + d->detailsButton = new QPushButton(d->detailsText->label(ShowLabel), this); + QPushButton hideDetails(d->detailsText->label(HideLabel)); + d->detailsButton->setFixedSize(d->detailsButton->sizeHint().expandedTo(hideDetails.sizeHint())); + } + d->detailsText->setText(text); +} +#endif // QT_NO_TEXTEDIT + +/*! + \property QMessageBox::informativeText + + \brief the informative text that provides a fuller description for + the message + + \since 4.2 + + Infromative text can be used to expand upon the text() to give more + information to the user. On the Mac, this text appears in small + system font below the text(). On other platforms, it is simply + appended to the existing text. + + By default, this property contains an empty string. + + \sa QMessageBox::text, QMessageBox::detailedText +*/ +QString QMessageBox::informativeText() const +{ + Q_D(const QMessageBox); + return d->informativeLabel ? d->informativeLabel->text() : QString(); +} + +void QMessageBox::setInformativeText(const QString &text) +{ + Q_D(QMessageBox); + if (text.isEmpty()) { + layout()->removeWidget(d->informativeLabel); + delete d->informativeLabel; + d->informativeLabel = 0; +#ifndef Q_WS_MAC + d->label->setContentsMargins(2, 0, 0, 0); +#endif + d->updateSize(); + return; + } + + if (!d->informativeLabel) { + QLabel *label = new QLabel; + label->setObjectName(QLatin1String("qt_msgbox_informativelabel")); + label->setTextInteractionFlags(Qt::TextInteractionFlags(style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, 0, this))); + label->setAlignment(Qt::AlignTop | Qt::AlignLeft); + label->setOpenExternalLinks(true); + label->setWordWrap(true); +#ifndef Q_WS_MAC + d->label->setContentsMargins(2, 0, 0, 0); + label->setContentsMargins(2, 0, 0, 6); + label->setIndent(9); +#else + label->setContentsMargins(16, 0, 0, 0); + // apply a smaller font the information label on the mac + label->setFont(qt_app_fonts_hash()->value("QTipLabel")); +#endif + label->setWordWrap(true); + QGridLayout *grid = static_cast<QGridLayout *>(layout()); + grid->addWidget(label, 1, 1, 1, 1); + d->informativeLabel = label; + } + d->informativeLabel->setText(text); + d->updateSize(); +} + +/*! + \since 4.2 + + This function shadows QWidget::setWindowTitle(). + + Sets the title of the message box to \a title. On Mac OS X, + the window title is ignored (as required by the Mac OS X + Guidelines). +*/ +void QMessageBox::setWindowTitle(const QString &title) +{ + // Message boxes on the mac do not have a title +#ifndef Q_WS_MAC + QDialog::setWindowTitle(title); +#else + Q_UNUSED(title); +#endif +} + + +/*! + \since 4.2 + + This function shadows QWidget::setWindowModality(). + + Sets the modality of the message box to \a windowModality. + + On Mac OS X, if the modality is set to Qt::WindowModal and the message box + has a parent, then the message box will be a Qt::Sheet, otherwise the + message box will be a standard dialog. +*/ +void QMessageBox::setWindowModality(Qt::WindowModality windowModality) +{ + QDialog::setWindowModality(windowModality); + + if (parentWidget() && windowModality == Qt::WindowModal) + setParent(parentWidget(), Qt::Sheet); + else + setParent(parentWidget(), Qt::Dialog); + setDefaultButton(d_func()->defaultButton); +} + +#ifdef QT3_SUPPORT +/*! + \compat + + Constructs a message box with the given \a parent, \a name, and + window flags, \a f. + The window title is specified by \a title, and the message box + displays message text and an icon specified by \a text and \a icon. + + The buttons that the user can access to respond to the message are + defined by \a button0, \a button1, and \a button2. +*/ +QMessageBox::QMessageBox(const QString& title, + const QString &text, Icon icon, + int button0, int button1, int button2, + QWidget *parent, const char *name, + bool modal, Qt::WindowFlags f) + : QDialog(*new QMessageBoxPrivate, parent, + f | Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WindowCloseButtonHint) +{ + Q_D(QMessageBox); + setObjectName(QString::fromAscii(name)); + d->init(title, text); + d->addOldButtons(button0, button1, button2); + setModal(modal); + setIcon(icon); +} + +/*! + \compat + Constructs a message box with the given \a parent and \a name. +*/ +QMessageBox::QMessageBox(QWidget *parent, const char *name) + : QDialog(*new QMessageBoxPrivate, parent, + Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WindowCloseButtonHint) +{ + Q_D(QMessageBox); + setObjectName(QString::fromAscii(name)); + d->init(); +} + +/*! + Returns the pixmap used for a standard icon. This + allows the pixmaps to be used in more complex message boxes. + \a icon specifies the required icon, e.g. QMessageBox::Information, + QMessageBox::Warning or QMessageBox::Critical. + + \a style is unused. +*/ + +QPixmap QMessageBox::standardIcon(Icon icon, Qt::GUIStyle style) +{ + Q_UNUSED(style); + return QMessageBox::standardIcon(icon); +} + +/*! + \fn int QMessageBox::message(const QString &title, const QString &text, + const QString &buttonText, QWidget *parent = 0, + const char *name = 0) + + Opens a modal message box with the given \a title and showing the + given \a text. The message box has a single button which has the + given \a buttonText (or tr("OK")). The message box is centred over + its \a parent and is called \a name. + + Use information(), warning(), question(), or critical() instead. + + \oldcode + QMessageBox::message(tr("My App"), tr("All occurrences replaced."), + tr("Close"), this); + \newcode + QMessageBox::information(this, tr("My App"), + tr("All occurrences replaced."), + QMessageBox::Close); + \endcode +*/ + +/*! + \fn bool QMessageBox::query(const QString &caption, + const QString& text, + const QString& yesButtonText, + const QString& noButtonText, + QWidget *parent, const char *name) + + \obsolete + + Queries the user using a modal message box with up to two buttons. + The message box has the given \a caption (although some window + managers don't show it), and shows the given \a text. The left + button has the \a yesButtonText (or tr("OK")), and the right button + has the \a noButtonText (or isn't shown). The message box is centred + over its \a parent and is called \a name. + + Use information(), question(), warning(), or critical() instead. +*/ + +#endif + +QPixmap QMessageBoxPrivate::standardIcon(QMessageBox::Icon icon, QMessageBox *mb) +{ + QStyle *style = mb ? mb->style() : QApplication::style(); + int iconSize = style->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, mb); + QIcon tmpIcon; + switch (icon) { + case QMessageBox::Information: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxInformation, 0, mb); + break; + case QMessageBox::Warning: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxWarning, 0, mb); + break; + case QMessageBox::Critical: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxCritical, 0, mb); + break; + case QMessageBox::Question: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mb); + default: + break; + } + if (!tmpIcon.isNull()) + return tmpIcon.pixmap(iconSize, iconSize); + return QPixmap(); +} + +/*! + \obsolete + + Returns the pixmap used for a standard icon. This allows the + pixmaps to be used in more complex message boxes. \a icon + specifies the required icon, e.g. QMessageBox::Question, + QMessageBox::Information, QMessageBox::Warning or + QMessageBox::Critical. + + Call QStyle::standardIcon() with QStyle::SP_MessageBoxInformation etc. + instead. +*/ + +QPixmap QMessageBox::standardIcon(Icon icon) +{ + return QMessageBoxPrivate::standardIcon(icon, 0); +} + +/*! + \typedef QMessageBox::Button + \obsolete + + Use QMessageBox::StandardButton instead. +*/ + +/*! + \fn int QMessageBox::information(QWidget *parent, const QString &title, + const QString& text, StandardButton button0, + StandardButton button1) + \fn int QMessageBox::warning(QWidget *parent, const QString &title, + const QString& text, StandardButton button0, + StandardButton button1) + \fn int QMessageBox::critical(QWidget *parent, const QString &title, + const QString& text, StandardButton button0, + StandardButton button1) + \fn int QMessageBox::question(QWidget *parent, const QString &title, + const QString& text, StandardButton button0, + StandardButton button1) + \internal + + ### Needed for Qt 4 source compatibility +*/ + +/*! + \fn int QMessageBox::exec() + + Shows the message box as a \l{QDialog#Modal Dialogs}{modal dialog}, + blocking until the user closes it. + + When using a QMessageBox with standard buttons, this functions returns a + \l StandardButton value indicating the standard button that was clicked. + When using QMessageBox with custom buttons, this function returns an + opaque value; use clickedButton() to determine which button was clicked. + + Users cannot interact with any other window in the same + application until they close the dialog, either by clicking a + button or by using a mechanism provided by the window system. + + \sa show(), result() +*/ + +QT_END_NAMESPACE + +#include "moc_qmessagebox.cpp" + +#endif // QT_NO_MESSAGEBOX diff --git a/src/gui/dialogs/qmessagebox.h b/src/gui/dialogs/qmessagebox.h new file mode 100644 index 0000000000..e1667d6f2c --- /dev/null +++ b/src/gui/dialogs/qmessagebox.h @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMESSAGEBOX_H +#define QMESSAGEBOX_H + +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_MESSAGEBOX + +class QLabel; +class QMessageBoxPrivate; +class QAbstractButton; + +class Q_GUI_EXPORT QMessageBox : public QDialog +{ + Q_OBJECT + Q_ENUMS(Icon) + Q_FLAGS(StandardButtons) + Q_PROPERTY(QString text READ text WRITE setText) + // ### Qt 5: Rename 'icon' 'standardIcon' and 'iconPixmap' 'icon' (and use QIcon?) + Q_PROPERTY(Icon icon READ icon WRITE setIcon) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(Qt::TextFormat textFormat READ textFormat WRITE setTextFormat) + Q_PROPERTY(StandardButtons standardButtons READ standardButtons WRITE setStandardButtons) +#ifndef QT_NO_TEXTEDIT + Q_PROPERTY(QString detailedText READ detailedText WRITE setDetailedText) +#endif + Q_PROPERTY(QString informativeText READ informativeText WRITE setInformativeText) + +public: + enum Icon { + NoIcon = 0, + Information = 1, + Warning = 2, + Critical = 3, + Question = 4 + }; + + enum ButtonRole { + // keep this in sync with QDialogButtonBox::ButtonRole + InvalidRole = -1, + AcceptRole, + RejectRole, + DestructiveRole, + ActionRole, + HelpRole, + YesRole, + NoRole, + ResetRole, + ApplyRole, + + NRoles + }; + + enum StandardButton { + // keep this in sync with QDialogButtonBox::StandardButton + NoButton = 0x00000000, + Ok = 0x00000400, + Save = 0x00000800, + SaveAll = 0x00001000, + Open = 0x00002000, + Yes = 0x00004000, + YesToAll = 0x00008000, + No = 0x00010000, + NoToAll = 0x00020000, + Abort = 0x00040000, + Retry = 0x00080000, + Ignore = 0x00100000, + Close = 0x00200000, + Cancel = 0x00400000, + Discard = 0x00800000, + Help = 0x01000000, + Apply = 0x02000000, + Reset = 0x04000000, + RestoreDefaults = 0x08000000, + + FirstButton = Ok, // internal + LastButton = RestoreDefaults, // internal + + YesAll = YesToAll, // obsolete + NoAll = NoToAll, // obsolete + + Default = 0x00000100, // obsolete + Escape = 0x00000200, // obsolete + FlagMask = 0x00000300, // obsolete + ButtonMask = ~FlagMask // obsolete + }; + typedef StandardButton Button; // obsolete + + Q_DECLARE_FLAGS(StandardButtons, StandardButton) + + explicit QMessageBox(QWidget *parent = 0); + QMessageBox(Icon icon, const QString &title, const QString &text, + StandardButtons buttons = NoButton, QWidget *parent = 0, + Qt::WindowFlags flags = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); + ~QMessageBox(); + + void addButton(QAbstractButton *button, ButtonRole role); + QPushButton *addButton(const QString &text, ButtonRole role); + QPushButton *addButton(StandardButton button); + void removeButton(QAbstractButton *button); + +#ifdef Q_OS_WINCE + void setVisible(bool visible); +#endif + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + + QList<QAbstractButton *> buttons() const; + ButtonRole buttonRole(QAbstractButton *button) const; + + void setStandardButtons(StandardButtons buttons); + StandardButtons standardButtons() const; + StandardButton standardButton(QAbstractButton *button) const; + QAbstractButton *button(StandardButton which) const; + + QPushButton *defaultButton() const; + void setDefaultButton(QPushButton *button); + void setDefaultButton(StandardButton button); + + QAbstractButton *escapeButton() const; + void setEscapeButton(QAbstractButton *button); + void setEscapeButton(StandardButton button); + + QAbstractButton *clickedButton() const; + + QString text() const; + void setText(const QString &text); + + Icon icon() const; + void setIcon(Icon); + + QPixmap iconPixmap() const; + void setIconPixmap(const QPixmap &pixmap); + + Qt::TextFormat textFormat() const; + void setTextFormat(Qt::TextFormat format); + + static StandardButton information(QWidget *parent, const QString &title, + const QString &text, StandardButtons buttons = Ok, + StandardButton defaultButton = NoButton); + static StandardButton question(QWidget *parent, const QString &title, + const QString &text, StandardButtons buttons = Ok, + StandardButton defaultButton = NoButton); + static StandardButton warning(QWidget *parent, const QString &title, + const QString &text, StandardButtons buttons = Ok, + StandardButton defaultButton = NoButton); + static StandardButton critical(QWidget *parent, const QString &title, + const QString &text, StandardButtons buttons = Ok, + StandardButton defaultButton = NoButton); + static void about(QWidget *parent, const QString &title, const QString &text); + static void aboutQt(QWidget *parent, const QString &title = QString()); + + QSize sizeHint() const; + + // the following functions are obsolete: + + QMessageBox(const QString &title, const QString &text, Icon icon, + int button0, int button1, int button2, + QWidget *parent = 0, + Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); + + static int information(QWidget *parent, const QString &title, + const QString& text, + int button0, int button1 = 0, int button2 = 0); + static int information(QWidget *parent, const QString &title, + const QString& text, + const QString& button0Text, + const QString& button1Text = QString(), + const QString& button2Text = QString(), + int defaultButtonNumber = 0, + int escapeButtonNumber = -1); + inline static StandardButton information(QWidget *parent, const QString &title, + const QString& text, + StandardButton button0, StandardButton button1 = NoButton) + { return information(parent, title, text, StandardButtons(button0), button1); } + + static int question(QWidget *parent, const QString &title, + const QString& text, + int button0, int button1 = 0, int button2 = 0); + static int question(QWidget *parent, const QString &title, + const QString& text, + const QString& button0Text, + const QString& button1Text = QString(), + const QString& button2Text = QString(), + int defaultButtonNumber = 0, + int escapeButtonNumber = -1); + inline static int question(QWidget *parent, const QString &title, + const QString& text, + StandardButton button0, StandardButton button1) + { return question(parent, title, text, StandardButtons(button0), button1); } + + static int warning(QWidget *parent, const QString &title, + const QString& text, + int button0, int button1, int button2 = 0); + static int warning(QWidget *parent, const QString &title, + const QString& text, + const QString& button0Text, + const QString& button1Text = QString(), + const QString& button2Text = QString(), + int defaultButtonNumber = 0, + int escapeButtonNumber = -1); + inline static int warning(QWidget *parent, const QString &title, + const QString& text, + StandardButton button0, StandardButton button1) + { return warning(parent, title, text, StandardButtons(button0), button1); } + + static int critical(QWidget *parent, const QString &title, + const QString& text, + int button0, int button1, int button2 = 0); + static int critical(QWidget *parent, const QString &title, + const QString& text, + const QString& button0Text, + const QString& button1Text = QString(), + const QString& button2Text = QString(), + int defaultButtonNumber = 0, + int escapeButtonNumber = -1); + inline static int critical(QWidget *parent, const QString &title, + const QString& text, + StandardButton button0, StandardButton button1) + { return critical(parent, title, text, StandardButtons(button0), button1); } + + QString buttonText(int button) const; + void setButtonText(int button, const QString &text); + + QString informativeText() const; + void setInformativeText(const QString &text); + +#ifndef QT_NO_TEXTEDIT + QString detailedText() const; + void setDetailedText(const QString &text); +#endif + + void setWindowTitle(const QString &title); + void setWindowModality(Qt::WindowModality windowModality); + +#ifdef QT3_SUPPORT + QT3_SUPPORT_CONSTRUCTOR QMessageBox(const QString &title, const QString &text, Icon icon, + int button0, int button1, int button2, + QWidget *parent, const char *name, bool modal, + Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); + QT3_SUPPORT_CONSTRUCTOR QMessageBox(QWidget *parent, const char *name); + + static QT3_SUPPORT QPixmap standardIcon(Icon icon, Qt::GUIStyle); + static QT3_SUPPORT int message(const QString &title, + const QString& text, + const QString& buttonText=QString(), + QWidget *parent = 0, const char * = 0) { + return QMessageBox::information(parent, title, text, + buttonText.isEmpty() ? tr("OK") : buttonText) == 0; + } + static QT3_SUPPORT bool query(const QString &title, + const QString& text, + const QString& yesButtonText = QString(), + const QString& noButtonText = QString(), + QWidget *parent = 0, const char * = 0) { + return QMessageBox::information(parent, title, text, + yesButtonText.isEmpty() ? tr("OK") : yesButtonText, + noButtonText) == 0; + } +#endif + + static QPixmap standardIcon(Icon icon); + +Q_SIGNALS: + void buttonClicked(QAbstractButton *button); + +#ifdef qdoc +public Q_SLOTS: + int exec(); +#endif + +protected: + bool event(QEvent *e); + void resizeEvent(QResizeEvent *event); + void showEvent(QShowEvent *event); + void closeEvent(QCloseEvent *event); + void keyPressEvent(QKeyEvent *event); + void changeEvent(QEvent *event); + +private: + Q_PRIVATE_SLOT(d_func(), void _q_buttonClicked(QAbstractButton *)) + + Q_DISABLE_COPY(QMessageBox) + Q_DECLARE_PRIVATE(QMessageBox) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QMessageBox::StandardButtons) + +#define QT_REQUIRE_VERSION(argc, argv, str) { QString s = QString::fromLatin1(str);\ +QString sq = QString::fromLatin1(qVersion()); \ +if ((sq.section(QChar::fromLatin1('.'),0,0).toInt()<<16)+\ +(sq.section(QChar::fromLatin1('.'),1,1).toInt()<<8)+\ +sq.section(QChar::fromLatin1('.'),2,2).toInt()<(s.section(QChar::fromLatin1('.'),0,0).toInt()<<16)+\ +(s.section(QChar::fromLatin1('.'),1,1).toInt()<<8)+\ +s.section(QChar::fromLatin1('.'),2,2).toInt()) { \ +if (!qApp){ \ + new QApplication(argc,argv); \ +} \ +QString s = QApplication::tr("Executable '%1' requires Qt "\ + "%2, found Qt %3.").arg(qAppName()).arg(QString::fromLatin1(\ +str)).arg(QString::fromLatin1(qVersion())); QMessageBox::critical(0, QApplication::tr(\ +"Incompatible Qt Library Error"), s, QMessageBox::Abort, 0); qFatal(s.toLatin1().data()); }} + +#endif // QT_NO_MESSAGEBOX + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMESSAGEBOX_H diff --git a/src/gui/dialogs/qmessagebox.qrc b/src/gui/dialogs/qmessagebox.qrc new file mode 100644 index 0000000000..8e6d7af671 --- /dev/null +++ b/src/gui/dialogs/qmessagebox.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/trolltech/qmessagebox"> + <file>images/qtlogo-64.png</file> +</qresource> +</RCC> diff --git a/src/gui/dialogs/qnspanelproxy_mac.mm b/src/gui/dialogs/qnspanelproxy_mac.mm new file mode 100644 index 0000000000..e934b49bc2 --- /dev/null +++ b/src/gui/dialogs/qnspanelproxy_mac.mm @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qdialogbuttonbox.h> +#if defined(Q_WS_MAC) +#include <private/qt_mac_p.h> +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> +#import <objc/objc-class.h> + +QT_BEGIN_NAMESPACE +static QWidget *currentWindow = 0; +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +@class QNSPanelProxy; + +@interface QNSPanelProxy : NSWindow { +} +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen; +- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; +- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen; +@end + +@implementation QNSPanelProxy +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation +{ + // remove evil flag + windowStyle &= ~NSUtilityWindowMask; + self = [self qt_fakeInitWithContentRect:contentRect styleMask:windowStyle + backing:bufferingType defer:deferCreation]; + return self; +} + +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen +{ + // remove evil flag + windowStyle &= ~NSUtilityWindowMask; + return [self qt_fakeInitWithContentRect:contentRect styleMask:windowStyle + backing:bufferingType defer:deferCreation screen:screen]; +} + +- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation +{ + Q_UNUSED(contentRect); + Q_UNUSED(windowStyle); + Q_UNUSED(bufferingType); + Q_UNUSED(deferCreation); + return nil; +} + +- (id)qt_fakeInitWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation screen:(NSScreen *)screen +{ + Q_UNUSED(contentRect); + Q_UNUSED(windowStyle); + Q_UNUSED(bufferingType); + Q_UNUSED(deferCreation); + Q_UNUSED(screen); + return nil; +} +@end + +@class QNSWindowProxy; + +@interface QNSWindowProxy : NSWindow { +} +- (void)setTitle:(NSString *)title; +- (void)qt_fakeSetTitle:(NSString *)title; +@end + +@implementation QNSWindowProxy +- (void)setTitle:(NSString *)title +{ + QCFString cftitle(currentWindow->windowTitle()); + + // evil reverse engineering + if ([title isEqualToString:@"Print"] + || [title isEqualToString:@"Page Setup"] + || [[self className] isEqualToString:@"PMPrintingWindow"]) + title = (NSString *)(static_cast<CFStringRef>(cftitle)); + return [self qt_fakeSetTitle:title]; +} + +- (void)qt_fakeSetTitle:(NSString *)title +{ + Q_UNUSED(title); +} +@end + +QT_BEGIN_NAMESPACE + +void macStartIntercept(SEL originalSel, SEL fakeSel, Class baseClass, Class proxyClass) +{ +#ifndef QT_MAC_USE_COCOA + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) +#endif + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + Method originalMethod = class_getInstanceMethod(baseClass, originalSel); + Method newMethod = class_getInstanceMethod(proxyClass, originalSel); + Method fakeMethod = class_getInstanceMethod(proxyClass, fakeSel); + + IMP originalCtorImp = method_setImplementation(originalMethod, + method_getImplementation(newMethod)); + class_addMethod(baseClass, fakeSel, originalCtorImp, + method_getTypeEncoding(fakeMethod)); +#endif + } +} + +void macStopIntercept(SEL originalSel, SEL fakeSel, Class baseClass, Class /* proxyClass */) +{ +#ifndef QT_MAC_USE_COCOA + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) +#endif + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + Method originalMethod = class_getInstanceMethod(baseClass, originalSel); + Method fakeMethodInBaseClass = class_getInstanceMethod(baseClass, fakeSel); + method_setImplementation(originalMethod, method_getImplementation(fakeMethodInBaseClass)); +#endif + } +} + +/* + Intercept the NSColorPanel constructor if the shared + color panel doesn't exist yet. What's going on here is + quite wacky, because we want to override the NSPanel + constructor and at the same time call the old NSPanel + constructor. So what we do is we effectively rename the + old NSPanel constructor qt_fakeInitWithContentRect:... + and have the new one call the old one. +*/ +void macStartInterceptNSPanelCtor() +{ + macStartIntercept(@selector(initWithContentRect:styleMask:backing:defer:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:), + [NSPanel class], [QNSPanelProxy class]); + macStartIntercept(@selector(initWithContentRect:styleMask:backing:defer:screen:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:), + [NSPanel class], [QNSPanelProxy class]); +} + +/* + Restore things as they were. +*/ +void macStopInterceptNSPanelCtor() +{ + macStopIntercept(@selector(initWithContentRect:styleMask:backing:defer:screen:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:), + [NSPanel class], [QNSPanelProxy class]); + macStopIntercept(@selector(initWithContentRect:styleMask:backing:defer:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:), + [NSPanel class], [QNSPanelProxy class]); +} + +/* + Intercept the NSPrintPanel and NSPageLayout setTitle: calls. The + hack is similar as for NSColorPanel above. +*/ +void macStartInterceptWindowTitle(QWidget *window) +{ + currentWindow = window; + macStartIntercept(@selector(setTitle:), @selector(qt_fakeSetTitle:), + [NSWindow class], [QNSWindowProxy class]); +} + +/* + Restore things as they were. +*/ +void macStopInterceptWindowTitle() +{ + currentWindow = 0; + macStopIntercept(@selector(setTitle:), @selector(qt_fakeSetTitle:), + [NSWindow class], [QNSWindowProxy class]); +} + +/* + Doesn't really belong in here. +*/ +NSButton *macCreateButton(const char *text, NSView *superview) +{ + static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + + NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect]; + [button setButtonType:NSMomentaryLightButton]; + [button setBezelStyle:NSRoundedBezelStyle]; + [button setTitle:(NSString*)(CFStringRef)QCFString(QDialogButtonBox::tr(text) + .remove(QLatin1Char('&')))]; + [[button cell] setFont:[NSFont systemFontOfSize: + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; + [superview addSubview:button]; + return button; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/dialogs/qpagesetupdialog.cpp b/src/gui/dialogs/qpagesetupdialog.cpp new file mode 100644 index 0000000000..63775d2065 --- /dev/null +++ b/src/gui/dialogs/qpagesetupdialog.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qabstractpagesetupdialog_p.h> + +#ifndef QT_NO_PRINTDIALOG + +QT_BEGIN_NAMESPACE + +// hack +class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate +{ +}; + +/*! + \enum QPageSetupDialog::PageSetupDialogOption + \since 4.4 + + Used to specify options to the page setup dialog + + This value is obsolete and does nothing since Qt 4.5: + + \value DontUseSheet In previous versions of Qt, exec() the page setup dialog + would create a sheet by default if the dialog was given a parent. + This is no longer supported in Qt 4.5. If you want to use sheets, use + QPageSetupDialog::open() instead. + + \omitvalue None + \omitvalue OwnsPrinter +*/ + +/*! + Sets the given \a option to be enabled if \a on is true; + otherwise, clears the given \a option. + + \sa options, testOption() +*/ +void QPageSetupDialog::setOption(PageSetupDialogOption option, bool on) +{ + Q_D(QPageSetupDialog); + if (!(d->opts & option) != !on) + setOptions(d->opts ^ option); +} + +/*! + Returns true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption() +*/ +bool QPageSetupDialog::testOption(PageSetupDialogOption option) const +{ + Q_D(const QPageSetupDialog); + return (d->opts & option) != 0; +} + +/*! + \property QPageSetupDialog::options + \brief the various options that affect the look and feel of the dialog + \since 4.5 + + By default, all options are disabled. + + Options should be set before showing the dialog. Setting them while the + dialog is visible is not guaranteed to have an immediate effect on the + dialog (depending on the option and on the platform). + + \sa setOption(), testOption() +*/ +void QPageSetupDialog::setOptions(PageSetupDialogOptions options) +{ + Q_D(QPageSetupDialog); + + PageSetupDialogOptions changed = (options ^ d->opts); + if (!changed) + return; + + d->opts = options; +} + +QPageSetupDialog::PageSetupDialogOptions QPageSetupDialog::options() const +{ + Q_D(const QPageSetupDialog); + return d->opts; +} + +/*! + \obsolete + + Use setOption(\a option, true) instead. +*/ +void QPageSetupDialog::addEnabledOption(PageSetupDialogOption option) +{ + setOption(option, true); +} + +/*! + \obsolete + + Use setOptions(\a options) instead. +*/ +void QPageSetupDialog::setEnabledOptions(PageSetupDialogOptions options) +{ + setOptions(options); +} + +/*! + \obsolete + + Use options() instead. +*/ +QPageSetupDialog::PageSetupDialogOptions QPageSetupDialog::enabledOptions() const +{ + return options(); +} + +/*! + \obsolete + + Use testOption(\a option) instead. +*/ +bool QPageSetupDialog::isOptionEnabled(PageSetupDialogOption option) const +{ + return testOption(option); +} + +/*! + \overload + \since 4.5 + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QPageSetupDialog::open(QObject *receiver, const char *member) +{ + Q_D(QPageSetupDialog); + connect(this, SIGNAL(accepted()), receiver, member); + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/dialogs/qpagesetupdialog.h b/src/gui/dialogs/qpagesetupdialog.h new file mode 100644 index 0000000000..03420ee5d1 --- /dev/null +++ b/src/gui/dialogs/qpagesetupdialog.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPAGESETUPDIALOG_H +#define QPAGESETUPDIALOG_H + +#include <QtGui/qabstractpagesetupdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_PRINTDIALOG + +class QPageSetupDialogPrivate; + +class Q_GUI_EXPORT QPageSetupDialog : public QAbstractPageSetupDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPageSetupDialog) + Q_ENUMS(PageSetupDialogOption) + Q_PROPERTY(PageSetupDialogOptions options READ options WRITE setOptions) + +public: + enum PageSetupDialogOption { + None = 0x00000000, // internal + DontUseSheet = 0x00000001, + OwnsPrinter = 0x80000000 // internal + }; + + Q_DECLARE_FLAGS(PageSetupDialogOptions, PageSetupDialogOption) + + explicit QPageSetupDialog(QPrinter *printer, QWidget *parent = 0); + explicit QPageSetupDialog(QWidget *parent = 0); + + // obsolete + void addEnabledOption(PageSetupDialogOption option); + void setEnabledOptions(PageSetupDialogOptions options); + PageSetupDialogOptions enabledOptions() const; + bool isOptionEnabled(PageSetupDialogOption option) const; + + void setOption(PageSetupDialogOption option, bool on = true); + bool testOption(PageSetupDialogOption option) const; + void setOptions(PageSetupDialogOptions options); + PageSetupDialogOptions options() const; + +#if defined(Q_WS_MAC) || defined(Q_OS_WIN) + virtual void setVisible(bool visible); +#endif + virtual int exec(); + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + +#ifdef qdoc + QPrinter *printer(); +#endif +}; + +#endif // QT_NO_PRINTDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPAGESETUPDIALOG_H diff --git a/src/gui/dialogs/qpagesetupdialog_mac.mm b/src/gui/dialogs/qpagesetupdialog_mac.mm new file mode 100644 index 0000000000..401d95f817 --- /dev/null +++ b/src/gui/dialogs/qpagesetupdialog_mac.mm @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpagesetupdialog.h" + +#include <qhash.h> +#include <private/qapplication_p.h> +#include <private/qprintengine_mac_p.h> +#include <private/qabstractpagesetupdialog_p.h> + +#ifndef QT_NO_PRINTDIALOG + +QT_USE_NAMESPACE + +@class QCocoaPageLayoutDelegate; + +@interface QCocoaPageLayoutDelegate : NSObject { + QMacPrintEnginePrivate *pe; +} +- (id)initWithMacPrintEngine:(QMacPrintEnginePrivate *)printEngine; +- (void)pageLayoutDidEnd:(NSPageLayout *)pageLayout + returnCode:(int)returnCode contextInfo:(void *)contextInfo; +@end + +@implementation QCocoaPageLayoutDelegate +- (id)initWithMacPrintEngine:(QMacPrintEnginePrivate *)printEngine; +{ + self = [super init]; + if (self) { + pe = printEngine; + } + return self; + +} +- (void)pageLayoutDidEnd:(NSPageLayout *)pageLayout + returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + Q_UNUSED(pageLayout); + QPageSetupDialog *dialog = static_cast<QPageSetupDialog *>(contextInfo); + if (returnCode == NSOKButton) { + PMRect paperRect; + PMGetUnadjustedPaperRect(pe->format, &paperRect); + pe->customSize = QSizeF(paperRect.right - paperRect.left, + paperRect.bottom - paperRect.top); + } + dialog->done((returnCode == NSOKButton) ? QDialog::Accepted : QDialog::Rejected); +} +@end + +QT_BEGIN_NAMESPACE + +extern void macStartInterceptWindowTitle(QWidget *window); +extern void macStopInterceptWindowTitle(); + +class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate +{ + Q_DECLARE_PUBLIC(QPageSetupDialog) + +public: + QPageSetupDialogPrivate() : ep(0) +#ifndef QT_MAC_USE_COCOA + ,upp(0) +#else + ,pageLayout(0) +#endif + {} + + ~QPageSetupDialogPrivate() { +#ifndef QT_MAC_USE_COCOA + if (upp) { + DisposePMSheetDoneUPP(upp); + upp = 0; + } + QHash<PMPrintSession, QPageSetupDialogPrivate *>::iterator it = sheetCallbackMap.begin(); + while (it != sheetCallbackMap.end()) { + if (it.value() == this) { + it = sheetCallbackMap.erase(it); + } else { + ++it; + } + } +#endif + } + +#ifndef QT_MAC_USE_COCOA + void openCarbonPageLayout(Qt::WindowModality modality); + void closeCarbonPageLayout(); + static void pageSetupDialogSheetDoneCallback(PMPrintSession printSession, WindowRef /*documentWindow*/, Boolean accepted) { + QPageSetupDialogPrivate *priv = sheetCallbackMap.value(printSession); + if (!priv) { + qWarning("%s:%d: QPageSetupDialog::exec: Could not retrieve data structure, " + "you most likely now have an infinite modal loop", __FILE__, __LINE__); + return; + } + priv->q_func()->done(accepted ? QDialog::Accepted : QDialog::Rejected); + } +#else + void openCocoaPageLayout(Qt::WindowModality modality); + void closeCocoaPageLayout(); +#endif + + QMacPrintEnginePrivate *ep; +#ifndef QT_MAC_USE_COCOA + PMSheetDoneUPP upp; + static QHash<PMPrintSession, QPageSetupDialogPrivate*> sheetCallbackMap; +#else + NSPageLayout *pageLayout; +#endif +}; + +#ifndef QT_MAC_USE_COCOA +QHash<PMPrintSession, QPageSetupDialogPrivate*> QPageSetupDialogPrivate::sheetCallbackMap; +void QPageSetupDialogPrivate::openCarbonPageLayout(Qt::WindowModality modality) +{ + Q_Q(QPageSetupDialog); + // If someone is reusing a QPrinter object, the end released all our old + // information. In this case, we must reinitialize. + if (ep->session == 0) + ep->initialize(); + + sheetCallbackMap.insert(ep->session, this); + if (modality == Qt::ApplicationModal) { + QWidget modal_widg(0, Qt::Window); + modal_widg.setObjectName(QLatin1String(__FILE__ "__modal_dlg")); + modal_widg.createWinId(); + QApplicationPrivate::enterModal(&modal_widg); + QApplicationPrivate::native_modal_dialog_active = true; + Boolean accepted; + PMSessionPageSetupDialog(ep->session, ep->format, &accepted); + QApplicationPrivate::leaveModal(&modal_widg); + QApplicationPrivate::native_modal_dialog_active = false; + pageSetupDialogSheetDoneCallback(ep->session, 0, accepted); + } else { + // Window Modal means that we use a sheet at the moment, there's no other way to do it correctly. + if (!upp) + upp = NewPMSheetDoneUPP(QPageSetupDialogPrivate::pageSetupDialogSheetDoneCallback); + PMSessionUseSheets(ep->session, qt_mac_window_for(q->parentWidget()), upp); + Boolean unused; + PMSessionPageSetupDialog(ep->session, ep->format, &unused); + } +} + +void QPageSetupDialogPrivate::closeCarbonPageLayout() +{ + // if the margins have changed, we have to use the margins from the new + // PMFormat object + if (q_func()->result() == QDialog::Accepted) { + PMPaper paper; + PMPaperMargins margins; + PMGetPageFormatPaper(ep->format, &paper); + PMPaperGetMargins(paper, &margins); + ep->leftMargin = margins.left; + ep->topMargin = margins.top; + ep->rightMargin = margins.right; + ep->bottomMargin = margins.bottom; + + PMRect paperRect; + PMGetUnadjustedPaperRect(ep->format, &paperRect); + ep->customSize = QSizeF(paperRect.right - paperRect.left, + paperRect.bottom - paperRect.top); + } + sheetCallbackMap.remove(ep->session); +} +#else +void QPageSetupDialogPrivate::openCocoaPageLayout(Qt::WindowModality modality) +{ + Q_Q(QPageSetupDialog); + + // If someone is reusing a QPrinter object, the end released all our old + // information. In this case, we must reinitialize. + if (ep->session == 0) + ep->initialize(); + + macStartInterceptWindowTitle(q); + pageLayout = [NSPageLayout pageLayout]; + // Keep a copy to this since we plan on using it for a bit. + [pageLayout retain]; + QCocoaPageLayoutDelegate *delegate = [[QCocoaPageLayoutDelegate alloc] initWithMacPrintEngine:ep]; + + if (modality == Qt::ApplicationModal) { + int rval = [pageLayout runModalWithPrintInfo:ep->printInfo]; + [delegate pageLayoutDidEnd:pageLayout returnCode:rval contextInfo:q]; + } else { + Q_ASSERT(q->parentWidget()); + [pageLayout beginSheetWithPrintInfo:ep->printInfo + modalForWindow:qt_mac_window_for(q->parentWidget()) + delegate:delegate + didEndSelector:@selector(pageLayoutDidEnd:returnCode:contextInfo:) + contextInfo:q]; + } + + macStopInterceptWindowTitle(); +} + +void QPageSetupDialogPrivate::closeCocoaPageLayout() +{ + [pageLayout release]; + pageLayout = 0; +} +#endif + +QPageSetupDialog::QPageSetupDialog(QPrinter *printer, QWidget *parent) + : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), printer, parent) +{ + Q_D(QPageSetupDialog); + d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func(); +} + +QPageSetupDialog::QPageSetupDialog(QWidget *parent) + : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), 0, parent) +{ + Q_D(QPageSetupDialog); + d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func(); +} + +/*! + \reimp +*/ +void QPageSetupDialog::setVisible(bool visible) +{ + Q_D(QPageSetupDialog); + + if (d->printer->outputFormat() != QPrinter::NativeFormat) + return; + +#ifndef QT_MAC_USE_COCOA + bool isCurrentlyVisible = d->sheetCallbackMap.contains(d->ep->session); +#else + bool isCurrentlyVisible = (d->pageLayout != 0); +#endif + if (!visible == !isCurrentlyVisible) + return; + + if (visible) { +#ifndef QT_MAC_USE_COCOA + d->openCarbonPageLayout(parentWidget() ? Qt::WindowModal + : Qt::ApplicationModal); +#else + d->openCocoaPageLayout(parentWidget() ? Qt::WindowModal + : Qt::ApplicationModal); +#endif + return; + } else { +#ifndef QT_MAC_USE_COCOA + d->closeCarbonPageLayout(); +#else + if (d->pageLayout) { + d->closeCocoaPageLayout(); + return; + } +#endif + } +} + +int QPageSetupDialog::exec() +{ + Q_D(QPageSetupDialog); + + if (d->printer->outputFormat() != QPrinter::NativeFormat) + return Rejected; + +#ifndef QT_MAC_USE_COCOA + d->openCarbonPageLayout(Qt::ApplicationModal); + d->closeCarbonPageLayout(); +#else + QMacCocoaAutoReleasePool pool; + d->openCocoaPageLayout(Qt::ApplicationModal); + d->closeCocoaPageLayout(); +#endif + return result(); +} + +QT_END_NAMESPACE + +#endif QT_NO_PRINTDIALOG diff --git a/src/gui/dialogs/qpagesetupdialog_unix.cpp b/src/gui/dialogs/qpagesetupdialog_unix.cpp new file mode 100644 index 0000000000..fc1f8b625e --- /dev/null +++ b/src/gui/dialogs/qpagesetupdialog_unix.cpp @@ -0,0 +1,620 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpagesetupdialog.h" + +#ifndef QT_NO_PRINTDIALOG +#include "qpagesetupdialog_unix_p.h" + +#include "qpainter.h" +#include "qprintdialog.h" +#include "qdialogbuttonbox.h" +#include <ui_qpagesetupwidget.h> + +#include <QtGui/qprinter.h> +#include <private/qabstractpagesetupdialog_p.h> +#include <private/qprinter_p.h> + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) +# include <private/qcups_p.h> +# include <cups/cups.h> +# include <private/qpdf_p.h> +#endif + + +QT_BEGIN_NAMESPACE + +QSizeF qt_printerPaperSize(QPrinter::Orientation, QPrinter::PaperSize, QPrinter::Unit, int); + +// Disabled until we have support for papersources on unix +// #define PSD_ENABLE_PAPERSOURCE + +static void populatePaperSizes(QComboBox* cb) +{ + cb->addItem(QPrintDialog::tr("A0"), QPrinter::A0); + cb->addItem(QPrintDialog::tr("A1"), QPrinter::A1); + cb->addItem(QPrintDialog::tr("A2"), QPrinter::A2); + cb->addItem(QPrintDialog::tr("A3"), QPrinter::A3); + cb->addItem(QPrintDialog::tr("A4"), QPrinter::A4); + cb->addItem(QPrintDialog::tr("A5"), QPrinter::A5); + cb->addItem(QPrintDialog::tr("A6"), QPrinter::A6); + cb->addItem(QPrintDialog::tr("A7"), QPrinter::A7); + cb->addItem(QPrintDialog::tr("A8"), QPrinter::A8); + cb->addItem(QPrintDialog::tr("A9"), QPrinter::A9); + cb->addItem(QPrintDialog::tr("B0"), QPrinter::B0); + cb->addItem(QPrintDialog::tr("B1"), QPrinter::B1); + cb->addItem(QPrintDialog::tr("B2"), QPrinter::B2); + cb->addItem(QPrintDialog::tr("B3"), QPrinter::B3); + cb->addItem(QPrintDialog::tr("B4"), QPrinter::B4); + cb->addItem(QPrintDialog::tr("B5"), QPrinter::B5); + cb->addItem(QPrintDialog::tr("B6"), QPrinter::B6); + cb->addItem(QPrintDialog::tr("B7"), QPrinter::B7); + cb->addItem(QPrintDialog::tr("B8"), QPrinter::B8); + cb->addItem(QPrintDialog::tr("B9"), QPrinter::B9); + cb->addItem(QPrintDialog::tr("B10"), QPrinter::B10); + cb->addItem(QPrintDialog::tr("C5E"), QPrinter::C5E); + cb->addItem(QPrintDialog::tr("DLE"), QPrinter::DLE); + cb->addItem(QPrintDialog::tr("Executive"), QPrinter::Executive); + cb->addItem(QPrintDialog::tr("Folio"), QPrinter::Folio); + cb->addItem(QPrintDialog::tr("Ledger"), QPrinter::Ledger); + cb->addItem(QPrintDialog::tr("Legal"), QPrinter::Legal); + cb->addItem(QPrintDialog::tr("Letter"), QPrinter::Letter); + cb->addItem(QPrintDialog::tr("Tabloid"), QPrinter::Tabloid); + cb->addItem(QPrintDialog::tr("US Common #10 Envelope"), QPrinter::Comm10E); + cb->addItem(QPrintDialog::tr("Custom"), QPrinter::Custom); +} + + +static QSizeF sizeForOrientation(QPrinter::Orientation orientation, const QSizeF &size) +{ + return (orientation == QPrinter::Portrait) ? size : QSizeF(size.height(), size.width()); +} + +#ifdef PSD_ENABLE_PAPERSOURCE +static const char *paperSourceNames[] = { + "Only One", + "Lower", + "Middle", + "Manual", + "Envelope", + "Envelope manual", + "Auto", + "Tractor", + "Small format", + "Large format", + "Large capacity", + "Cassette", + "Form source", + 0 +}; + +struct PaperSourceNames +{ + PaperSourceNames(const char *nam, QPrinter::PaperSource ps) + : paperSource(ps), name(nam) {} + QPrinter::PaperSource paperSource; + const char *name; +}; +#endif + + +class QPagePreview : public QWidget +{ +public: + QPagePreview(QWidget *parent) : QWidget(parent) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setMinimumSize(50, 50); + } + + void setPaperSize(const QSizeF& size) + { + m_size = size; + update(); + } + + void setMargins(qreal left, qreal top, qreal right, qreal bottom) + { + m_left = left; + m_top = top; + m_right = right; + m_bottom = bottom; + update(); + } + +protected: + void paintEvent(QPaintEvent *) + { + QRect pageRect; + QSizeF adjustedSize(m_size); + adjustedSize.scale(width()-10, height()-10, Qt::KeepAspectRatio); + pageRect = QRect(QPoint(0,0), adjustedSize.toSize()); + pageRect.moveCenter(rect().center()); + + qreal width_factor = pageRect.width() / m_size.width(); + qreal height_factor = pageRect.height() / m_size.height(); + int leftSize = qRound(m_left*width_factor); + int topSize = qRound(m_top*height_factor); + int rightSize = qRound(m_right*width_factor); + int bottomSize = qRound(m_bottom * height_factor); + QRect marginRect(pageRect.x()+leftSize, + pageRect.y()+topSize, + pageRect.width() - (leftSize+rightSize+1), + pageRect.height() - (topSize+bottomSize+1)); + + QPainter p(this); + QColor shadow(palette().mid().color()); + for (int i=1; i<6; ++i) { + shadow.setAlpha(180-i*30); + QRect offset(pageRect.adjusted(i, i, i, i)); + p.setPen(shadow); + p.drawLine(offset.left(), offset.bottom(), offset.right(), offset.bottom()); + p.drawLine(offset.right(), offset.top(), offset.right(), offset.bottom()-1); + } + p.fillRect(pageRect, palette().light()); + + if (marginRect.isValid()) { + p.setPen(QPen(palette().color(QPalette::Dark), 0, Qt::DotLine)); + p.drawRect(marginRect); + + marginRect.adjust(2, 2, -1, -1); + p.setClipRect(marginRect); + QFont font; + font.setPointSizeF(font.pointSizeF()*0.25); + p.setFont(font); + p.setPen(palette().color(QPalette::Dark)); + QString text(QLatin1String("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.")); + for (int i=0; i<3; ++i) + text += text; + p.drawText(marginRect, Qt::TextWordWrap|Qt::AlignVCenter, text); + } + } + +private: + // all these are in points + qreal m_left, m_top, m_right, m_bottom; + QSizeF m_size; +}; + + +class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate +{ + Q_DECLARE_PUBLIC(QPageSetupDialog) + +public: + ~QPageSetupDialogPrivate(); + void init(); + + QPageSetupWidget *widget; +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + QCUPSSupport *cups; +#endif +}; + +QPageSetupDialogPrivate::~QPageSetupDialogPrivate() +{ +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + delete cups; +#endif +} + +void QPageSetupDialogPrivate::init() +{ + Q_Q(QPageSetupDialog); + + widget = new QPageSetupWidget(q); + widget->setPrinter(printer); +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (printer->outputFormat() == QPrinter::NativeFormat && QCUPSSupport::isAvailable()) { + cups = new QCUPSSupport; + widget->selectPrinter(cups); + } else { + cups = 0; + } +#endif + + QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel, + Qt::Horizontal, q); + QObject::connect(buttons, SIGNAL(accepted()), q, SLOT(accept())); + QObject::connect(buttons, SIGNAL(rejected()), q, SLOT(reject())); + + QVBoxLayout *lay = new QVBoxLayout(q); + lay->addWidget(widget); + lay->addWidget(buttons); +} + +QPageSetupWidget::QPageSetupWidget(QWidget *parent) + : QWidget(parent), + m_printer(0), + m_blockSignals(false), + m_cups(0) +{ + widget.setupUi(this); + + QString suffix = (QLocale::system().measurementSystem() == QLocale::ImperialSystem) + ? QString::fromLatin1(" in") + : QString::fromLatin1(" mm"); + widget.topMargin->setSuffix(suffix); + widget.bottomMargin->setSuffix(suffix); + widget.leftMargin->setSuffix(suffix); + widget.rightMargin->setSuffix(suffix); + widget.paperWidth->setSuffix(suffix); + widget.paperHeight->setSuffix(suffix); + + QVBoxLayout *lay = new QVBoxLayout(widget.preview); + widget.preview->setLayout(lay); + m_pagePreview = new QPagePreview(widget.preview); + lay->addWidget(m_pagePreview); + + setAttribute(Qt::WA_WState_Polished, false); + +#ifdef PSD_ENABLE_PAPERSOURCE + for (int i=0; paperSourceNames[i]; ++i) + widget.paperSource->insertItem(paperSourceNames[i]); +#else + widget.paperSourceLabel->setVisible(false); + widget.paperSource->setVisible(false); +#endif + + widget.reverseLandscape->setVisible(false); + widget.reversePortrait->setVisible(false); + + populatePaperSizes(widget.paperSize); + + QStringList units; + units << tr("Centimeters (cm)") << tr("Millimeters (mm)") << tr("Inches (in)") << tr("Points (pt)"); + widget.unit->addItems(units); + connect(widget.unit, SIGNAL(activated(int)), this, SLOT(unitChanged(int))); + widget.unit->setCurrentIndex((QLocale::system().measurementSystem() == QLocale::ImperialSystem) ? 2 : 1); + + connect(widget.paperSize, SIGNAL(currentIndexChanged(int)), this, SLOT(_q_paperSizeChanged())); + connect(widget.paperWidth, SIGNAL(valueChanged(double)), this, SLOT(_q_paperSizeChanged())); + connect(widget.paperHeight, SIGNAL(valueChanged(double)), this, SLOT(_q_paperSizeChanged())); + + connect(widget.leftMargin, SIGNAL(valueChanged(double)), this, SLOT(setLeftMargin(double))); + connect(widget.topMargin, SIGNAL(valueChanged(double)), this, SLOT(setTopMargin(double))); + connect(widget.rightMargin, SIGNAL(valueChanged(double)), this, SLOT(setRightMargin(double))); + connect(widget.bottomMargin, SIGNAL(valueChanged(double)), this, SLOT(setBottomMargin(double))); + + connect(widget.portrait, SIGNAL(clicked()), this, SLOT(_q_pageOrientationChanged())); + connect(widget.landscape, SIGNAL(clicked()), this, SLOT(_q_pageOrientationChanged())); +} + +void QPageSetupWidget::setPrinter(QPrinter *printer) +{ + m_printer = printer; + m_blockSignals = true; + selectPdfPsPrinter(printer); + printer->getPageMargins(&m_leftMargin, &m_topMargin, &m_rightMargin, &m_bottomMargin, QPrinter::Point); + unitChanged(widget.unit->currentIndex()); + m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin); + m_paperSize = printer->paperSize(QPrinter::Point); + widget.paperWidth->setValue(m_paperSize.width() / m_currentMultiplier); + widget.paperHeight->setValue(m_paperSize.height() / m_currentMultiplier); + + widget.landscape->setChecked(printer->orientation() == QPrinter::Landscape); + +#ifdef PSD_ENABLE_PAPERSOURCE + widget.paperSource->setCurrentItem(printer->paperSource()); +#endif + Q_ASSERT(m_blockSignals); + m_blockSignals = false; + _q_paperSizeChanged(); +} + +// set gui data on printer +void QPageSetupWidget::setupPrinter() const +{ + QPrinter::Orientation orientation = widget.portrait->isChecked() + ? QPrinter::Portrait + : QPrinter::Landscape; + m_printer->setOrientation(orientation); + // paper format + QVariant val = widget.paperSize->itemData(widget.paperSize->currentIndex()); + int ps = m_printer->pageSize(); + if (val.type() == QVariant::Int) { + ps = val.toInt(); + } +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + else if (m_cups && QCUPSSupport::isAvailable() && m_cups->currentPPD()) { + QByteArray cupsPageSize = val.toByteArray(); + QPrintEngine *engine = m_printer->printEngine(); + engine->setProperty(PPK_CupsStringPageSize, QString::fromLatin1(cupsPageSize)); + engine->setProperty(PPK_CupsOptions, m_cups->options()); + + QRect pageRect = m_cups->pageRect(cupsPageSize); + engine->setProperty(PPK_CupsPageRect, pageRect); + + QRect paperRect = m_cups->paperRect(cupsPageSize); + engine->setProperty(PPK_CupsPaperRect, paperRect); + + for(ps = 0; ps < QPrinter::NPaperSize; ++ps) { + QPdf::PaperSize size = QPdf::paperSize(QPrinter::PaperSize(ps)); + if (size.width == paperRect.width() && size.height == paperRect.height()) + break; + } + } +#endif + if (ps == QPrinter::Custom) + m_printer->setPaperSize(sizeForOrientation(orientation, m_paperSize), QPrinter::Point); + else + m_printer->setPaperSize(static_cast<QPrinter::PaperSize>(ps)); + +#ifdef PSD_ENABLE_PAPERSOURCE + m_printer->setPaperSource((QPrinter::PaperSource)widget.paperSource->currentIndex()); +#endif + m_printer->setPageMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin, QPrinter::Point); + +} + +void QPageSetupWidget::selectPrinter(QCUPSSupport *cups) +{ + m_cups = cups; + widget.paperSize->clear(); +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (m_cups && QCUPSSupport::isAvailable()) { + const ppd_option_t* pageSizes = m_cups->pageSizes(); + const int numChoices = pageSizes ? pageSizes->num_choices : 0; + + int cupsDefaultSize = 0; + QSize qtPreferredSize = m_printer->paperSize(QPrinter::Point).toSize(); + bool preferredSizeMatched = false; + for (int i = 0; i < numChoices; ++i) { + widget.paperSize->addItem(QString::fromLocal8Bit(pageSizes->choices[i].text), QByteArray(pageSizes->choices[i].choice)); + if (static_cast<int>(pageSizes->choices[i].marked) == 1) + cupsDefaultSize = i; + if (m_printer->d_func()->hasUserSetPageSize) { + QRect cupsPaperSize = m_cups->paperRect(pageSizes->choices[i].choice); + QSize diff = cupsPaperSize.size() - qtPreferredSize; + if (qAbs(diff.width()) < 5 && qAbs(diff.height()) < 5) { + widget.paperSize->setCurrentIndex(i); + preferredSizeMatched = true; + } + } + } + if (!preferredSizeMatched) + widget.paperSize->setCurrentIndex(cupsDefaultSize); + if (m_printer->d_func()->hasCustomPageMargins) { + m_printer->getPageMargins(&m_leftMargin, &m_topMargin, &m_rightMargin, &m_bottomMargin, QPrinter::Point); + } else { + QByteArray cupsPaperSizeChoice = widget.paperSize->itemData(widget.paperSize->currentIndex()).toByteArray(); + QRect paper = m_cups->paperRect(cupsPaperSizeChoice); + QRect content = m_cups->pageRect(cupsPaperSizeChoice); + + m_leftMargin = content.x() - paper.x(); + m_topMargin = content.y() - paper.y(); + m_rightMargin = paper.right() - content.right(); + m_bottomMargin = paper.bottom() - content.bottom(); + } + } +#endif + if (widget.paperSize->count() == 0) { + populatePaperSizes(widget.paperSize); + widget.paperSize->setCurrentIndex(widget.paperSize->findData( + QLocale::system().measurementSystem() == QLocale::ImperialSystem ? QPrinter::Letter : QPrinter::A4)); + } + + unitChanged(widget.unit->currentIndex()); + m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin); +} + +void QPageSetupWidget::selectPdfPsPrinter(const QPrinter *p) +{ + m_cups = 0; + widget.paperSize->clear(); + populatePaperSizes(widget.paperSize); + widget.paperSize->setCurrentIndex(widget.paperSize->findData(p->paperSize())); + + m_leftMargin = 90; + m_topMargin = 72; + m_bottomMargin = 72; + m_rightMargin = 90; + unitChanged(widget.unit->currentIndex()); + m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin); +} + +// Updates size/preview after the combobox has been changed. +void QPageSetupWidget::_q_paperSizeChanged() +{ + QVariant val = widget.paperSize->itemData(widget.paperSize->currentIndex()); + int index = m_printer->pageSize(); + if (val.type() == QVariant::Int) { + index = val.toInt(); + } + + if (m_blockSignals) return; + m_blockSignals = true; + + QPrinter::PaperSize size = QPrinter::PaperSize(index); + QPrinter::Orientation orientation = widget.portrait->isChecked() + ? QPrinter::Portrait + : QPrinter::Landscape; + + bool custom = size == QPrinter::Custom; + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + custom = custom ? !m_cups : custom; +#endif + + widget.paperWidth->setEnabled(custom); + widget.paperHeight->setEnabled(custom); + widget.widthLabel->setEnabled(custom); + widget.heightLabel->setEnabled(custom); + if (custom) { + m_paperSize.setWidth( widget.paperWidth->value() * m_currentMultiplier); + m_paperSize.setHeight( widget.paperHeight->value() * m_currentMultiplier); + m_pagePreview->setPaperSize(m_paperSize); + } else { + Q_ASSERT(m_printer); +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (m_cups) { // combobox is filled with cups based data + QByteArray cupsPageSize = widget.paperSize->itemData(widget.paperSize->currentIndex()).toByteArray(); + m_paperSize = m_cups->paperRect(cupsPageSize).size(); + if (orientation == QPrinter::Landscape) + m_paperSize = QSizeF(m_paperSize.height(), m_paperSize.width()); // swap + } + else +#endif + m_paperSize = qt_printerPaperSize(orientation, size, QPrinter::Point, 1); + + m_pagePreview->setPaperSize(m_paperSize); + widget.paperWidth->setValue(m_paperSize.width() / m_currentMultiplier); + widget.paperHeight->setValue(m_paperSize.height() / m_currentMultiplier); + } + m_blockSignals = false; +} + +void QPageSetupWidget::_q_pageOrientationChanged() +{ + if (QPrinter::PaperSize(widget.paperSize->currentIndex()) == QPrinter::Custom) { + double tmp = widget.paperWidth->value(); + widget.paperWidth->setValue(widget.paperHeight->value()); + widget.paperHeight->setValue(tmp); + } + _q_paperSizeChanged(); +} + +extern double qt_multiplierForUnit(QPrinter::Unit unit, int resolution); + +void QPageSetupWidget::unitChanged(int item) +{ + QString suffix; + switch(item) { + case 0: + m_currentMultiplier = 10 * qt_multiplierForUnit(QPrinter::Millimeter, 1); + suffix = QString::fromLatin1(" cm"); + break; + case 2: + m_currentMultiplier = qt_multiplierForUnit(QPrinter::Inch, 1); + suffix = QString::fromLatin1(" in"); + break; + case 3: + m_currentMultiplier = qt_multiplierForUnit(QPrinter::Point, 1); + suffix = QString::fromLatin1(" pt"); + break; + case 1: + default: + m_currentMultiplier = qt_multiplierForUnit(QPrinter::Millimeter, 1); + suffix = QString::fromLatin1(" mm"); + break; + } + const bool old = m_blockSignals; + m_blockSignals = true; + widget.topMargin->setSuffix(suffix); + widget.leftMargin->setSuffix(suffix); + widget.rightMargin->setSuffix(suffix); + widget.bottomMargin->setSuffix(suffix); + widget.paperWidth->setSuffix(suffix); + widget.paperHeight->setSuffix(suffix); + widget.topMargin->setValue(m_topMargin / m_currentMultiplier); + widget.leftMargin->setValue(m_leftMargin / m_currentMultiplier); + widget.rightMargin->setValue(m_rightMargin / m_currentMultiplier); + widget.bottomMargin->setValue(m_bottomMargin / m_currentMultiplier); + widget.paperWidth->setValue(m_paperSize.width() / m_currentMultiplier); + widget.paperHeight->setValue(m_paperSize.height() / m_currentMultiplier); + m_blockSignals = old; +} + +void QPageSetupWidget::setTopMargin(double newValue) +{ + if (m_blockSignals) return; + m_topMargin = newValue * m_currentMultiplier; + m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin); +} + +void QPageSetupWidget::setBottomMargin(double newValue) +{ + if (m_blockSignals) return; + m_bottomMargin = newValue * m_currentMultiplier; + m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin); +} + +void QPageSetupWidget::setLeftMargin(double newValue) +{ + if (m_blockSignals) return; + m_leftMargin = newValue * m_currentMultiplier; + m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin); +} + +void QPageSetupWidget::setRightMargin(double newValue) +{ + if (m_blockSignals) return; + m_rightMargin = newValue * m_currentMultiplier; + m_pagePreview->setMargins(m_leftMargin, m_topMargin, m_rightMargin, m_bottomMargin); +} + + + +QPageSetupDialog::QPageSetupDialog(QPrinter *printer, QWidget *parent) + : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), printer, parent) +{ + Q_D(QPageSetupDialog); + d->init(); +} + + +QPageSetupDialog::QPageSetupDialog(QWidget *parent) + : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), 0, parent) +{ + Q_D(QPageSetupDialog); + d->init(); +} + +/*! + \internal +*/ +int QPageSetupDialog::exec() +{ + Q_D(QPageSetupDialog); + + int ret = QDialog::exec(); + if (ret == Accepted) + d->widget->setupPrinter(); + return ret; +} + + +QT_END_NAMESPACE + +#include "moc_qpagesetupdialog.cpp" + +#endif // QT_NO_PRINTDIALOG diff --git a/src/gui/dialogs/qpagesetupdialog_unix_p.h b/src/gui/dialogs/qpagesetupdialog_unix_p.h new file mode 100644 index 0000000000..08f0f4d3c6 --- /dev/null +++ b/src/gui/dialogs/qpagesetupdialog_unix_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPAGESETUPWIDGET_H +#define QPAGESETUPWIDGET_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// to version without notice, or even be removed. +// +// We mean it. +// +// + +#include "qglobal.h" + +#ifndef QT_NO_PRINTDIALOG + +#include <ui_qpagesetupwidget.h> + +QT_BEGIN_NAMESPACE + +class QPrinter; +class QPagePreview; +class QCUPSSupport; + +class QPageSetupWidget : public QWidget { + Q_OBJECT +public: + QPageSetupWidget(QWidget *parent = 0); + QPageSetupWidget(QPrinter *printer, QWidget *parent = 0); + void setPrinter(QPrinter *printer); + /// copy information from the widget and apply that to the printer. + void setupPrinter() const; + void selectPrinter(QCUPSSupport *m_cups); + void selectPdfPsPrinter(const QPrinter *p); + +private slots: + void _q_pageOrientationChanged(); + void _q_paperSizeChanged(); + void unitChanged(int item); + void setTopMargin(double newValue); + void setBottomMargin(double newValue); + void setLeftMargin(double newValue); + void setRightMargin(double newValue); + +private: + Ui::QPageSetupWidget widget; + QPagePreview *m_pagePreview; + QPrinter *m_printer; + qreal m_leftMargin; + qreal m_topMargin; + qreal m_rightMargin; + qreal m_bottomMargin; + QSizeF m_paperSize; + qreal m_currentMultiplier; + bool m_blockSignals; + QCUPSSupport *m_cups; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTDIALOG +#endif diff --git a/src/gui/dialogs/qpagesetupdialog_win.cpp b/src/gui/dialogs/qpagesetupdialog_win.cpp new file mode 100644 index 0000000000..4bb571c6bf --- /dev/null +++ b/src/gui/dialogs/qpagesetupdialog_win.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpagesetupdialog.h" + +#ifndef QT_NO_PRINTDIALOG +#include <qapplication.h> + +#include <private/qprintengine_win_p.h> +#include <private/qabstractpagesetupdialog_p.h> + +QT_BEGIN_NAMESPACE + +class QPageSetupDialogPrivate : public QAbstractPageSetupDialogPrivate +{ +}; + +QPageSetupDialog::QPageSetupDialog(QPrinter *printer, QWidget *parent) + : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), printer, parent) +{ +} + +QPageSetupDialog::QPageSetupDialog(QWidget *parent) + : QAbstractPageSetupDialog(*(new QPageSetupDialogPrivate), 0, parent) +{ +} + +int QPageSetupDialog::exec() +{ + Q_D(QPageSetupDialog); + + if (d->printer->outputFormat() != QPrinter::NativeFormat) + return Rejected; + + QWin32PrintEngine *engine = static_cast<QWin32PrintEngine*>(d->printer->paintEngine()); + QWin32PrintEnginePrivate *ep = static_cast<QWin32PrintEnginePrivate *>(engine->d_ptr); + + PAGESETUPDLG psd; + memset(&psd, 0, sizeof(PAGESETUPDLG)); + psd.lStructSize = sizeof(PAGESETUPDLG); + + // we need a temp DEVMODE struct if we don't have a global DEVMODE + HGLOBAL hDevMode; + int devModeSize; + if (!ep->globalDevMode) { + QT_WA( { devModeSize = sizeof(DEVMODEW) + ((DEVMODEW *) ep->devMode)->dmDriverExtra; }, + { devModeSize = sizeof(DEVMODEA) + ((DEVMODEA *) ep->devMode)->dmDriverExtra; }); + hDevMode = GlobalAlloc(GHND, devModeSize); + if (hDevMode) { + void *dest = GlobalLock(hDevMode); + memcpy(dest, ep->devMode, devModeSize); + GlobalUnlock(hDevMode); + } + psd.hDevMode = hDevMode; + } else { + psd.hDevMode = ep->devMode; + } + + HGLOBAL *tempDevNames = ep->createDevNames(); + psd.hDevNames = tempDevNames; + + QWidget *parent = parentWidget(); + parent = parent ? parent->window() : qApp->activeWindow(); + Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); + psd.hwndOwner = parent ? parent->winId() : 0; + + QRect paperRect = d->printer->paperRect(); + QRect pageRect = d->printer->pageRect(); + + psd.Flags = PSD_MARGINS; + double multiplier = 1; + switch (QLocale::system().measurementSystem()) { + case QLocale::MetricSystem: + psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS; + multiplier = 1; + break; + case QLocale::ImperialSystem: + psd.Flags |= PSD_INTHOUSANDTHSOFINCHES; + multiplier = 25.4/10; + break; + } + + QRect marginRect = ep->getPageMargins(); + psd.rtMargin.left = marginRect.left() / multiplier; + psd.rtMargin.top = marginRect.top() / multiplier; + psd.rtMargin.right = marginRect.width() / multiplier;; + psd.rtMargin.bottom = marginRect.height() / multiplier;; + + bool result = PageSetupDlg(&psd); + if (result) { + ep->readDevnames(psd.hDevNames); + ep->readDevmode(psd.hDevMode); + + QRect theseMargins = QRect(psd.rtMargin.left * multiplier, + psd.rtMargin.top * multiplier, + psd.rtMargin.right * multiplier, + psd.rtMargin.bottom * multiplier); + + if (theseMargins != marginRect) { + ep->setPageMargins(psd.rtMargin.left * multiplier, + psd.rtMargin.top * multiplier, + psd.rtMargin.right * multiplier, + psd.rtMargin.bottom * multiplier); + } + + ep->updateCustomPaperSize(); + + // copy from our temp DEVMODE struct + if (!ep->globalDevMode && hDevMode) { + void *src = GlobalLock(hDevMode); + memcpy(ep->devMode, src, devModeSize); + GlobalUnlock(hDevMode); + } + } + + if (!ep->globalDevMode && hDevMode) + GlobalFree(hDevMode); + GlobalFree(tempDevNames); + done(result); + return result; +} + +void QPageSetupDialog::setVisible(bool visible) +{ + if (!visible) + return; + exec(); +} + +QT_END_NAMESPACE +#endif diff --git a/src/gui/dialogs/qpagesetupwidget.ui b/src/gui/dialogs/qpagesetupwidget.ui new file mode 100644 index 0000000000..ace2ab8f44 --- /dev/null +++ b/src/gui/dialogs/qpagesetupwidget.ui @@ -0,0 +1,353 @@ +<ui version="4.0" > + <class>QPageSetupWidget</class> + <widget class="QWidget" name="QPageSetupWidget" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>416</width> + <height>488</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout_3" > + <property name="margin" > + <number>0</number> + </property> + <item row="0" column="0" colspan="2" > + <layout class="QHBoxLayout" name="horizontalLayout_4" > + <item> + <widget class="QComboBox" name="unit" /> + </item> + <item> + <spacer name="horizontalSpacer_3" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Paper</string> + </property> + <layout class="QGridLayout" name="gridLayout_2" > + <item row="0" column="0" > + <widget class="QLabel" name="pageSizeLabel" > + <property name="text" > + <string>Page size:</string> + </property> + <property name="buddy" > + <cstring>paperSize</cstring> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QComboBox" name="paperSize" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="widthLabel" > + <property name="text" > + <string>Width:</string> + </property> + <property name="buddy" > + <cstring>paperWidth</cstring> + </property> + </widget> + </item> + <item row="1" column="1" > + <layout class="QHBoxLayout" name="horizontalLayout_3" > + <item> + <widget class="QDoubleSpinBox" name="paperWidth" > + <property name="maximum" > + <double>9999.989999999999782</double> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="heightLabel" > + <property name="text" > + <string>Height:</string> + </property> + <property name="buddy" > + <cstring>paperHeight</cstring> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="paperHeight" > + <property name="maximum" > + <double>9999.989999999999782</double> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="paperSourceLabel" > + <property name="text" > + <string>Paper source:</string> + </property> + <property name="buddy" > + <cstring>paperSource</cstring> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QComboBox" name="paperSource" /> + </item> + <item row="1" column="2" > + <spacer name="horizontalSpacer_4" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" > + <widget class="QGroupBox" name="groupBox_3" > + <property name="title" > + <string>Orientation</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QRadioButton" name="portrait" > + <property name="text" > + <string>Portrait</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="landscape" > + <property name="text" > + <string>Landscape</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="reverseLandscape" > + <property name="text" > + <string>Reverse landscape</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="reversePortrait" > + <property name="text" > + <string>Reverse portrait</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_5" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item rowspan="2" row="2" column="1" > + <widget class="QWidget" native="1" name="preview" /> + </item> + <item row="3" column="0" > + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Margins</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2" > + <item> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="1" > + <widget class="QDoubleSpinBox" name="topMargin" > + <property name="toolTip" > + <string>top margin</string> + </property> + <property name="accessibleName" > + <string>top margin</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="maximum" > + <double>999.990000000000009</double> + </property> + </widget> + </item> + <item row="1" column="0" colspan="3" > + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <spacer name="horizontalSpacer_7" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDoubleSpinBox" name="leftMargin" > + <property name="toolTip" > + <string>left margin</string> + </property> + <property name="accessibleName" > + <string>left margin</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="maximum" > + <double>999.990000000000009</double> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDoubleSpinBox" name="rightMargin" > + <property name="toolTip" > + <string>right margin</string> + </property> + <property name="accessibleName" > + <string>right margin</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="maximum" > + <double>999.990000000000009</double> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_8" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="2" column="1" > + <widget class="QDoubleSpinBox" name="bottomMargin" > + <property name="toolTip" > + <string>bottom margin</string> + </property> + <property name="accessibleName" > + <string>bottom margin</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="maximum" > + <double>999.990000000000009</double> + </property> + </widget> + </item> + <item row="0" column="2" > + <spacer name="horizontalSpacer_2" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0" > + <spacer name="horizontalSpacer_5" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="4" column="0" > + <spacer name="verticalSpacer" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/gui/dialogs/qprintdialog.h b/src/gui/dialogs/qprintdialog.h new file mode 100644 index 0000000000..b3492266db --- /dev/null +++ b/src/gui/dialogs/qprintdialog.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPRINTDIALOG_H +#define QPRINTDIALOG_H + +#include <QtGui/qabstractprintdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_PRINTDIALOG + +class QPrintDialogPrivate; +class QPushButton; +class QPrinter; + +#if defined (Q_OS_UNIX) && !defined(QTOPIA_PRINTDIALOG) && !defined(Q_WS_MAC) +class QUnixPrintWidgetPrivate; + +class Q_GUI_EXPORT QUnixPrintWidget : public QWidget +{ + Q_OBJECT + +public: + QUnixPrintWidget(QPrinter *printer, QWidget *parent = 0); + ~QUnixPrintWidget(); + void updatePrinter(); + +private: + 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()) +}; +#endif + +class Q_GUI_EXPORT QPrintDialog : public QAbstractPrintDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPrintDialog) + Q_ENUMS(PrintDialogOption) + Q_PROPERTY(PrintDialogOptions options READ options WRITE setOptions) + +public: + explicit QPrintDialog(QPrinter *printer, QWidget *parent = 0); + explicit QPrintDialog(QWidget *parent = 0); + ~QPrintDialog(); + + int exec(); +#if defined (Q_OS_UNIX) && !defined(QTOPIA_PRINTDIALOG) && !defined(Q_WS_MAC) + virtual void accept(); +#endif + void done(int result); + +#if defined (Q_OS_UNIX) && defined (QT3_SUPPORT) + void setPrinter(QPrinter *, bool = false); + QPrinter *printer() const; + void addButton(QPushButton *button); +#endif + + void setOption(PrintDialogOption option, bool on = true); + bool testOption(PrintDialogOption option) const; + void setOptions(PrintDialogOptions options); + PrintDialogOptions options() const; + +#if defined(Q_OS_UNIX) || defined(Q_WS_MAC) || defined(Q_OS_WIN) + void setVisible(bool visible); +#endif + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + +#ifdef qdoc + QPrinter *printer(); +#endif + +#ifdef QTOPIA_PRINTDIALOG +public: + bool eventFilter(QObject *, QEvent *); +#endif + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void accepted() { QDialog::accepted(); } +#endif +#else + using QDialog::accepted; +#endif + +Q_SIGNALS: + void accepted(QPrinter *printer); + +private: +#ifndef QTOPIA_PRINTDIALOG + Q_PRIVATE_SLOT(d_func(), void _q_chbPrintLastFirstToggled(bool)) +#if defined (Q_OS_UNIX) && !defined (Q_OS_MAC) + Q_PRIVATE_SLOT(d_func(), void _q_collapseOrExpandDialog()) +#endif +# if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) && !defined(QT_NO_MESSAGEBOX) + Q_PRIVATE_SLOT(d_func(), void _q_checkFields()) +# endif +#else // QTOPIA_PRINTDIALOG + Q_PRIVATE_SLOT(d_func(), void _q_okClicked()) + Q_PRIVATE_SLOT(d_func(),void _q_printerOrFileSelected(QAbstractButton *b)) + Q_PRIVATE_SLOT(d_func(),void _q_paperSizeSelected(int)) + Q_PRIVATE_SLOT(d_func(), void _q_orientSelected(int)) + Q_PRIVATE_SLOT(d_func(), void _q_pageOrderSelected(int)) + Q_PRIVATE_SLOT(d_func(), void _q_colorModeSelected(QAbstractButton *)) + Q_PRIVATE_SLOT(d_func(), void _q_setNumCopies(int)) + Q_PRIVATE_SLOT(d_func(), void _q_printRangeSelected(int)) + Q_PRIVATE_SLOT(d_func(), void _q_setFirstPage(int)) + Q_PRIVATE_SLOT(d_func(), void _q_setLastPage(int)) + Q_PRIVATE_SLOT(d_func(), void _q_fileNameEditChanged(const QString &text)) +#endif // QTOPIA_PRINTDIALOG + friend class QUnixPrintWidget; +}; + +#endif // QT_NO_PRINTDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPRINTDIALOG_H diff --git a/src/gui/dialogs/qprintdialog.qrc b/src/gui/dialogs/qprintdialog.qrc new file mode 100644 index 0000000000..f54eb6b5ee --- /dev/null +++ b/src/gui/dialogs/qprintdialog.qrc @@ -0,0 +1,38 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/trolltech/dialogs/qprintpreviewdialog"> +<file>images/fit-page-24.png</file> +<file>images/fit-page-32.png</file> +<file>images/fit-width-24.png</file> +<file>images/fit-width-32.png</file> +<file>images/go-first-24.png</file> +<file>images/go-first-32.png</file> +<file>images/go-last-24.png</file> +<file>images/go-last-32.png</file> +<file>images/go-next-24.png</file> +<file>images/go-next-32.png</file> +<file>images/go-previous-24.png</file> +<file>images/go-previous-32.png</file> +<file>images/layout-landscape-24.png</file> +<file>images/layout-landscape-32.png</file> +<file>images/layout-portrait-24.png</file> +<file>images/layout-portrait-32.png</file> +<file>images/page-setup-24.png</file> +<file>images/page-setup-32.png</file> +<file>images/print-24.png</file> +<file>images/print-32.png</file> +<file>images/view-page-multi-24.png</file> +<file>images/view-page-multi-32.png</file> +<file>images/view-page-one-24.png</file> +<file>images/view-page-one-32.png</file> +<file>images/view-page-sided-24.png</file> +<file>images/view-page-sided-32.png</file> +<file>images/zoom-in-24.png</file> +<file>images/zoom-in-32.png</file> +<file>images/zoom-out-24.png</file> +<file>images/zoom-out-32.png</file> +</qresource> +<qresource prefix="/trolltech/dialogs/qprintdialog"> +<file>images/status-color.png</file> +<file>images/status-gray-scale.png</file> +</qresource> +</RCC> diff --git a/src/gui/dialogs/qprintdialog_mac.mm b/src/gui/dialogs/qprintdialog_mac.mm new file mode 100644 index 0000000000..362dcb0df1 --- /dev/null +++ b/src/gui/dialogs/qprintdialog_mac.mm @@ -0,0 +1,428 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_PRINTDIALOG + +#include <private/qt_mac_p.h> + +#include <qhash.h> +#include <qprintdialog.h> +#include <private/qapplication_p.h> +#include <private/qabstractprintdialog_p.h> +#include <private/qprintengine_mac_p.h> + +QT_BEGIN_NAMESPACE + +class QPrintDialogPrivate : public QAbstractPrintDialogPrivate +{ + Q_DECLARE_PUBLIC(QPrintDialog) + +public: + QPrintDialogPrivate() : ep(0), printPanel(0) +#ifndef QT_MAC_USE_COCOA + ,upp(0) +#endif + {} +#ifndef QT_MAC_USE_COCOA + ~QPrintDialogPrivate() { + if (upp) { + DisposePMSheetDoneUPP(upp); + upp = 0; + } + QHash<PMPrintSession, QPrintDialogPrivate *>::iterator it = sheetCallbackMap.begin(); + while (it != sheetCallbackMap.end()) { + if (it.value() == this) { + it = sheetCallbackMap.erase(it); + } else { + ++it; + } + } + } +#endif + +#ifndef QT_MAC_USE_COCOA + void openCarbonPrintPanel(Qt::WindowModality modality); + void closeCarbonPrintPanel(); + static void printDialogSheetDoneCallback(PMPrintSession printSession, WindowRef /*documentWindow*/, Boolean accepted) { + QPrintDialogPrivate *priv = sheetCallbackMap.value(printSession); + if (!priv) { + qWarning("%s:%d: QPrintDialog::exec: Could not retrieve data structure, " + "you most likely now have an infinite loop", __FILE__, __LINE__); + return; + } + priv->q_func()->done(accepted ? QDialog::Accepted : QDialog::Rejected); + priv->closeCarbonPrintPanel(); + } +#else + void openCocoaPrintPanel(Qt::WindowModality modality); + void closeCocoaPrintPanel(); +#endif + void initBeforeRun(); + + inline QPrintDialog *printDialog() { return q_func(); } + + inline void _q_printToFileChanged(int) {} + inline void _q_rbPrintRangeToggled(bool) {} + inline void _q_printerChanged(int) {} +#ifndef QT_NO_MESSAGEBOX + inline void _q_checkFields() {} +#endif + inline void _q_chbPrintLastFirstToggled(bool) {} + inline void _q_paperSizeChanged(int) {} + inline void _q_btnBrowseClicked() {} + inline void _q_btnPropertiesClicked() {} + + QMacPrintEnginePrivate *ep; + NSPrintPanel *printPanel; +#ifndef QT_MAC_USE_COCOA + PMSheetDoneUPP upp; + static QHash<PMPrintSession, QPrintDialogPrivate *> sheetCallbackMap; +#endif +}; + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +@class QCocoaPrintPanelDelegate; + +@interface QCocoaPrintPanelDelegate : NSObject { +} +- (void)printPanelDidEnd:(NSPrintPanel *)printPanel + returnCode:(int)returnCode contextInfo:(void *)contextInfo; +@end + +@implementation QCocoaPrintPanelDelegate +- (void)printPanelDidEnd:(NSPrintPanel *)printPanel + returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + Q_UNUSED(printPanel); + + QPrintDialogPrivate *d = static_cast<QPrintDialogPrivate *>(contextInfo); + QPrintDialog *dialog = d->printDialog(); + // temporary hack to work around bug in deleteLater() in Qt/Mac Cocoa +#if 1 + bool deleteDialog = dialog->testAttribute(Qt::WA_DeleteOnClose); + dialog->setAttribute(Qt::WA_DeleteOnClose, false); +#endif + + if (returnCode == NSOKButton) { + UInt32 frompage, topage; + PMGetFirstPage(d->ep->settings, &frompage); + PMGetLastPage(d->ep->settings, &topage); + topage = qMin(UInt32(INT_MAX), topage); + dialog->setFromTo(frompage, topage); + + // OK, I need to map these values back let's see + // If from is 1 and to is INT_MAX, then print it all + // (Apologies to the folks with more than INT_MAX pages) + if (dialog->fromPage() == 1 && dialog->toPage() == INT_MAX) { + dialog->setPrintRange(QPrintDialog::AllPages); + dialog->setFromTo(0, 0); + } else { + dialog->setPrintRange(QPrintDialog::PageRange); // In a way a lie, but it shouldn't hurt. + // Carbon hands us back a very large number here even for ALL, set it to max + // in that case to follow the behavior of the other print dialogs. + if (dialog->maxPage() < dialog->toPage()) + dialog->setFromTo(dialog->fromPage(), dialog->maxPage()); + } + // Keep us in sync with file output + PMDestinationType dest; + PMSessionGetDestinationType(d->ep->session, d->ep->settings, &dest); + if (dest == kPMDestinationFile) { + QCFType<CFURLRef> file; + PMSessionCopyDestinationLocation(d->ep->session, d->ep->settings, &file); + UInt8 localFile[2048]; // Assuming there's a POSIX file system here. + CFURLGetFileSystemRepresentation(file, true, localFile, sizeof(localFile)); + d->ep->outputFilename + = QString::fromUtf8(reinterpret_cast<const char *>(localFile)); + } else { + // Keep output format. + QPrinter::OutputFormat format; + format = d->printer->outputFormat(); + d->printer->setOutputFileName(QString()); + d->printer->setOutputFormat(format); + } + } + + dialog->done((returnCode == NSOKButton) ? QDialog::Accepted : QDialog::Rejected); +#if 1 + if (deleteDialog) + delete dialog; +#endif +} +@end + +QT_BEGIN_NAMESPACE + +extern void macStartInterceptWindowTitle(QWidget *window); +extern void macStopInterceptWindowTitle(); + + +void QPrintDialogPrivate::initBeforeRun() +{ + Q_Q(QPrintDialog); + // If someone is reusing a QPrinter object, the end released all our old + // information. In this case, we must reinitialize. + if (ep->session == 0) + ep->initialize(); + + + // It seems the only way that PM lets you use all is if the minimum + // for the page range is 1. This _kind of_ makes sense if you think about + // it. However, calling PMSetFirstPage() or PMSetLastPage() always enforces + // the range. + PMSetPageRange(ep->settings, q->minPage(), q->maxPage()); + if (q->printRange() == QAbstractPrintDialog::PageRange) { + PMSetFirstPage(ep->settings, q->fromPage(), false); + PMSetLastPage(ep->settings, q->toPage(), false); + } +} + +#ifndef QT_MAC_USE_COCOA +QHash<PMPrintSession, QPrintDialogPrivate *> QPrintDialogPrivate::sheetCallbackMap; +void QPrintDialogPrivate::openCarbonPrintPanel(Qt::WindowModality modality) +{ + Q_Q(QPrintDialog); + initBeforeRun(); + sheetCallbackMap.insert(ep->session, this); + if (modality == Qt::ApplicationModal) { + QWidget modal_widg(0, Qt::Window); + modal_widg.setObjectName(QLatin1String(__FILE__ "__modal_dlg")); + modal_widg.createWinId(); + QApplicationPrivate::enterModal(&modal_widg); + QApplicationPrivate::native_modal_dialog_active = true; + Boolean acceptStatus; + PMSessionPrintDialog(ep->session, ep->settings, ep->format, &acceptStatus); + QApplicationPrivate::leaveModal(&modal_widg); + QApplicationPrivate::native_modal_dialog_active = false; + printDialogSheetDoneCallback(ep->session, 0, acceptStatus); + } else { + // Window Modal means that we use a sheet at the moment, there's no other way to do it correctly. + if (!upp) + upp = NewPMSheetDoneUPP(QPrintDialogPrivate::printDialogSheetDoneCallback); + PMSessionUseSheets(ep->session, qt_mac_window_for(q->parentWidget()), upp); + QApplicationPrivate::native_modal_dialog_active = true; + Boolean unused; + PMSessionPrintDialog(ep->session, ep->settings, ep->format, &unused); + } +} + +void QPrintDialogPrivate::closeCarbonPrintPanel() +{ + Q_Q(QPrintDialog); + QApplicationPrivate::native_modal_dialog_active = false; + if (q->result() == QDialog::Accepted) { + UInt32 frompage, topage; + PMGetFirstPage(ep->settings, &frompage); + PMGetLastPage(ep->settings, &topage); + topage = qMin(UInt32(INT_MAX), topage); + q->setFromTo(frompage, topage); + + // OK, I need to map these values back let's see + // If from is 1 and to is INT_MAX, then print it all + // (Apologies to the folks with more than INT_MAX pages) + // ...that's a joke. + if (q->fromPage() == 1 && q->toPage() == INT_MAX) { + q->setPrintRange(QAbstractPrintDialog::AllPages); + q->setFromTo(0,0); + } else { + q->setPrintRange(QAbstractPrintDialog::PageRange); // In a way a lie, but it shouldn't hurt. + // Carbon hands us back a very large number here even for ALL, set it to max + // in that case to follow the behavior of the other print dialogs. + if (q->maxPage() < q->toPage()) + q->setFromTo(q->fromPage(), q->maxPage()); + } + // Keep us in sync with file output + PMDestinationType dest; + PMSessionGetDestinationType(ep->session, ep->settings, &dest); + if (dest == kPMDestinationFile) { + QCFType<CFURLRef> file; + PMSessionCopyDestinationLocation(ep->session, ep->settings, &file); + UInt8 localFile[2048]; // Assuming there's a POSIX file system here. + CFURLGetFileSystemRepresentation(file, true, localFile, sizeof(localFile)); + ep->outputFilename = QString::fromUtf8(reinterpret_cast<const char *>(localFile)); + } else { + ep->outputFilename = QString(); + } + } + sheetCallbackMap.remove(ep->session); +} +#else +void QPrintDialogPrivate::openCocoaPrintPanel(Qt::WindowModality modality) +{ + Q_Q(QPrintDialog); + + initBeforeRun(); + + QPrintDialog::PrintDialogOptions qtOptions = q->options(); + NSPrintPanelOptions macOptions = NSPrintPanelShowsCopies; + if (qtOptions & QPrintDialog::PrintPageRange) + macOptions |= NSPrintPanelShowsPageRange; + if (qtOptions & QPrintDialog::PrintShowPageSize) + macOptions |= NSPrintPanelShowsPaperSize | NSPrintPanelShowsPageSetupAccessory + | NSPrintPanelShowsOrientation; + + macStartInterceptWindowTitle(q); + printPanel = [NSPrintPanel printPanel]; + QCocoaPrintPanelDelegate *delegate = [[QCocoaPrintPanelDelegate alloc] init]; + [printPanel setOptions:macOptions]; + + if (modality == Qt::ApplicationModal) { + int rval = [printPanel runModalWithPrintInfo:ep->printInfo]; + [delegate printPanelDidEnd:printPanel returnCode:rval contextInfo:this]; + } else { + Q_ASSERT(q->parentWidget()); + NSWindow *windowRef = qt_mac_window_for(q->parentWidget()); + [printPanel beginSheetWithPrintInfo:ep->printInfo + modalForWindow:windowRef + delegate:delegate + didEndSelector:@selector(printPanelDidEnd:returnCode:contextInfo:) + contextInfo:this]; + } + + macStopInterceptWindowTitle(); +} + +void QPrintDialogPrivate::closeCocoaPrintPanel() +{ + // ### +} +#endif + +static bool warnIfNotNative(QPrinter *printer) +{ + if (printer->outputFormat() != QPrinter::NativeFormat) { + qWarning("QPrintDialog: Cannot be used on non-native printers"); + return false; + } + return true; +} + + +QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent) + : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent) +{ + Q_D(QPrintDialog); + if (!warnIfNotNative(d->printer)) + return; + d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func(); +} + +QPrintDialog::QPrintDialog(QWidget *parent) + : QAbstractPrintDialog(*(new QPrintDialogPrivate), 0, parent) +{ + Q_D(QPrintDialog); + if (!warnIfNotNative(d->printer)) + return; + d->ep = static_cast<QMacPrintEngine *>(d->printer->paintEngine())->d_func(); +} + +QPrintDialog::~QPrintDialog() +{ +} + +int QPrintDialog::exec() +{ + Q_D(QPrintDialog); + if (!warnIfNotNative(d->printer)) + return QDialog::Rejected; + +#ifndef QT_MAC_USE_COCOA + d->openCarbonPrintPanel(Qt::ApplicationModal); +#else + QMacCocoaAutoReleasePool pool; + + d->openCocoaPrintPanel(Qt::ApplicationModal); + d->closeCocoaPrintPanel(); +#endif + return result(); +} + +#ifdef QT3_SUPPORT +QPrinter *QPrintDialog::printer() const +{ + Q_D(const QPrintDialog); + return d->printer; +} +#endif + +/*! + \reimp +*/ +void QPrintDialog::setVisible(bool visible) +{ + Q_D(QPrintDialog); + + bool isCurrentlyVisible = (d->printPanel != 0); + + if (!visible == !isCurrentlyVisible) + return; + + if (d->printer->outputFormat() != QPrinter::NativeFormat) + return; + + if (visible) { +#ifndef QT_MAC_USE_COCOA + d->openCarbonPrintPanel(parentWidget() ? Qt::WindowModal + : Qt::ApplicationModal); +#else + d->openCocoaPrintPanel(parentWidget() ? Qt::WindowModal + : Qt::ApplicationModal); +#endif + return; + } else { + if (d->printPanel) { +#ifndef QT_MAC_USE_COCOA + d->closeCarbonPrintPanel(); +#else + d->closeCocoaPrintPanel(); +#endif + return; + } + } +} + +QT_END_NAMESPACE + +#include "moc_qprintdialog.cpp" + +#endif // QT_NO_PRINTDIALOG diff --git a/src/gui/dialogs/qprintdialog_qws.cpp b/src/gui/dialogs/qprintdialog_qws.cpp new file mode 100644 index 0000000000..70eeffa4b5 --- /dev/null +++ b/src/gui/dialogs/qprintdialog_qws.cpp @@ -0,0 +1,556 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include <private/qabstractprintdialog_p.h> +#include "qprintdialog.h" + +#ifndef QT_NO_PRINTDIALOG + +#include "qapplication.h" +#include "qbuttongroup.h" +#include "qradiobutton.h" +#include "qcombobox.h" +#include "qspinbox.h" +#include "qprinter.h" +#include "qlineedit.h" +#include "qdir.h" +#include "qmessagebox.h" +#include "qinputdialog.h" +#include "qlayout.h" +#include "qlabel.h" + +#include "qlibrary.h" + +#ifndef QT_NO_NIS + +#ifndef BOOL_DEFINED +#define BOOL_DEFINED +#endif + +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/yp_prot.h> + +#endif //QT_NO_NIS + +#include <ctype.h> +#include <stdlib.h> + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +typedef void (*QPrintDialogCreator)(QPrintDialog *parent); +Q_GUI_EXPORT QPrintDialogCreator _qt_print_dialog_creator; + +class QPrintDialogPrivate : public QAbstractPrintDialogPrivate +{ + Q_DECLARE_PUBLIC(QPrintDialog) +public: + QButtonGroup *printerOrFile; + bool outputToFile; + QRadioButton *printToPrinterButton; + QRadioButton *printToFileButton; + QLineEdit *fileName; + + QButtonGroup *colorMode; + QRadioButton *printColor; + QRadioButton *printGray; + QPrinter::ColorMode colorMode2; + + QComboBox *orientationCombo, *sizeCombo; + QPrinter::PaperSize pageSize; + QPrinter::Orientation orientation; + + QSpinBox *copies; + int numCopies; + QPrinter::PaperSize indexToPaperSize[QPrinter::NPaperSize]; + + QComboBox *rangeCombo; + QSpinBox *firstPage; + QSpinBox *lastPage; + + QComboBox *pageOrderCombo; + QPrinter::PageOrder pageOrder2; + + QString faxNum; + + void init(); + + void _q_okClicked(); + void _q_printerOrFileSelected(QAbstractButton *b); + void _q_paperSizeSelected(int); + void _q_orientSelected(int); + void _q_pageOrderSelected(int); + void _q_colorModeSelected(QAbstractButton *); + void _q_setNumCopies(int); + void _q_printRangeSelected(int); + void _q_setFirstPage(int); + void _q_setLastPage(int); + void _q_fileNameEditChanged(const QString &text); + + void setupDestination(); + void setupPrinterSettings(); + void setupPaper(); + void setupOptions(); + + void setPrinter(QPrinter *p, bool pickUpSettings); +}; + +static void isc(QPrintDialogPrivate *d, const QString & text, + QPrinter::PaperSize ps); + +void QPrintDialogPrivate::_q_okClicked() +{ + Q_Q(QPrintDialog); +#ifndef QT_NO_MESSAGEBOX + if (outputToFile && fileName->isModified() && QFileInfo(fileName->text()).exists()) { + int confirm = QMessageBox::warning( + q, QPrintDialog::tr("File exists"), + QPrintDialog::tr("<qt>Do you want to overwrite it?</qt>"), + QMessageBox::Yes, QMessageBox::No); + if (confirm == QMessageBox::No) + return; + } +#endif // QT_NO_MESSAGEBOX + + lastPage->interpretText(); + firstPage->interpretText(); + copies->interpretText(); + if (outputToFile) { + printer->setOutputFileName(fileName->text()); + } + printer->setOrientation(orientation); + printer->setPaperSize(pageSize); + printer->setPageOrder(pageOrder2); + printer->setColorMode(colorMode2); + printer->setNumCopies(numCopies); + + switch ((rangeCombo->itemData(rangeCombo->currentIndex())).toInt()){ + case (int)QPrintDialog::AllPages: + q->setPrintRange(QPrintDialog::AllPages); + q->setFromTo(0, 0); + break; + case (int)QPrintDialog::Selection: + q->setPrintRange(QPrintDialog::Selection); + q->setFromTo(0, 0); + break; + case (int)QPrintDialog::PageRange: + q->setPrintRange(QPrintDialog::PageRange); + q->setFromTo(firstPage->value(), lastPage->value()); + break; + } + q->accept(); +} + +void QPrintDialogPrivate::_q_printerOrFileSelected(QAbstractButton *b) +{ + outputToFile = (b == printToFileButton); + if (outputToFile) { + _q_fileNameEditChanged(fileName->text()); + if (!fileName->isModified() && fileName->text().isEmpty()) { + QString file = "print.tiff"; + fileName->setText(file); + fileName->setCursorPosition(file.length()); + fileName->selectAll(); + fileName->setModified(true); // confirm overwrite when OK clicked + + } + fileName->setEnabled(true); + fileName->setFocus(); + } else { + fileName->setText(QString()); + if (fileName->isEnabled()) + fileName->setEnabled(false); + } +} + +void QPrintDialogPrivate::_q_paperSizeSelected(int id) +{ + if (id < QPrinter::NPaperSize) + pageSize = QPrinter::PaperSize(indexToPaperSize[id]); +} + +void QPrintDialogPrivate::_q_orientSelected(int id) +{ + orientation = (QPrinter::Orientation)id; +} + +void QPrintDialogPrivate::_q_pageOrderSelected(int id) +{ + pageOrder2 = (QPrinter::PageOrder)id; +} + +void QPrintDialogPrivate::_q_colorModeSelected(QAbstractButton *b) +{ + colorMode2 = (b == printColor) ? QPrinter::Color : QPrinter::GrayScale; +} + +void QPrintDialogPrivate::_q_setNumCopies(int copies) +{ + numCopies = copies; +} + +void QPrintDialogPrivate::_q_printRangeSelected(int id) +{ + bool enable = (rangeCombo->itemData(id).toInt() == (int)QPrintDialog::PageRange); + firstPage->setEnabled(enable); + lastPage->setEnabled(enable); +} + +void QPrintDialogPrivate::_q_setFirstPage(int fp) +{ + Q_Q(QPrintDialog); + if (printer) { + lastPage->setMinimum(fp); + lastPage->setMaximum(qMax(fp, q->maxPage())); + } +} + +void QPrintDialogPrivate::_q_setLastPage(int lp) +{ + Q_Q(QPrintDialog); + if (printer) { + firstPage->setMinimum(qMin(lp, q->minPage())); + firstPage->setMaximum(lp); + } +} + +void QPrintDialogPrivate::_q_fileNameEditChanged(const QString &text) +{ + Q_UNUSED(text); +} + +void QPrintDialogPrivate::setupDestination() +{ + Q_Q(QPrintDialog); + + // print destinations + printerOrFile = new QButtonGroup(q); + QObject::connect(printerOrFile, SIGNAL(buttonClicked(QAbstractButton *)), + q, SLOT(_q_printerOrFileSelected(QAbstractButton *))); + + printToPrinterButton = q->findChild<QRadioButton *>("printToPrinterButton"); + printerOrFile->addButton(printToPrinterButton); + printToFileButton = q->findChild<QRadioButton *>("printToFileButton"); + printerOrFile->addButton(printToFileButton); + + // file name + fileName = q->findChild<QLineEdit *>("fileName"); + QObject::connect(fileName, SIGNAL(textChanged(QString)), + q, SLOT(_q_fileNameEditChanged(QString))); + + outputToFile = false; +} + +void QPrintDialogPrivate::setupPrinterSettings() +{ + Q_Q(QPrintDialog); + + // color mode + colorMode = new QButtonGroup(q); + QObject::connect(colorMode, SIGNAL(buttonClicked(QAbstractButton *)), + q, SLOT(_q_colorModeSelected(QAbstractButton *))); + + printColor = q->findChild<QRadioButton *>("printColor"); + colorMode->addButton(printColor); + printGray = q->findChild<QRadioButton *>("printGray"); + colorMode->addButton(printGray); +} + +void isc(QPrintDialogPrivate *ptr, const QString & text, QPrinter::PaperSize ps) +{ + if (ptr && !text.isEmpty() && ps < QPrinter::NPaperSize) { + ptr->sizeCombo->addItem(text); + int index = ptr->sizeCombo->count()-1; + if (index >= 0 && index < QPrinter::NPaperSize) + ptr->indexToPaperSize[index] = ps; + } +} + +void QPrintDialogPrivate::setupPaper() +{ + Q_Q(QPrintDialog); + + pageSize = QPrinter::A4; + + // paper orientation + orientationCombo = q->findChild<QComboBox *>("orientationCombo"); + orientation = QPrinter::Portrait; + QObject::connect(orientationCombo, SIGNAL(activated(int)), + q, SLOT(_q_orientSelected(int))); + + // paper size + sizeCombo = q->findChild<QComboBox *>("sizeCombo"); + + int n; + for(n=0; n<QPrinter::NPaperSize; n++) + indexToPaperSize[n] = QPrinter::A4; + + isc(this, QPrintDialog::tr("A0 (841 x 1189 mm)"), QPrinter::A0); + isc(this, QPrintDialog::tr("A1 (594 x 841 mm)"), QPrinter::A1); + isc(this, QPrintDialog::tr("A2 (420 x 594 mm)"), QPrinter::A2); + isc(this, QPrintDialog::tr("A3 (297 x 420 mm)"), QPrinter::A3); + isc(this, QPrintDialog::tr("A4 (210 x 297 mm, 8.26 x 11.7 inches)"), QPrinter::A4); + isc(this, QPrintDialog::tr("A5 (148 x 210 mm)"), QPrinter::A5); + isc(this, QPrintDialog::tr("A6 (105 x 148 mm)"), QPrinter::A6); + isc(this, QPrintDialog::tr("A7 (74 x 105 mm)"), QPrinter::A7); + isc(this, QPrintDialog::tr("A8 (52 x 74 mm)"), QPrinter::A8); + isc(this, QPrintDialog::tr("A9 (37 x 52 mm)"), QPrinter::A9); + isc(this, QPrintDialog::tr("B0 (1000 x 1414 mm)"), QPrinter::B0); + isc(this, QPrintDialog::tr("B1 (707 x 1000 mm)"), QPrinter::B1); + isc(this, QPrintDialog::tr("B2 (500 x 707 mm)"), QPrinter::B2); + isc(this, QPrintDialog::tr("B3 (353 x 500 mm)"), QPrinter::B3); + isc(this, QPrintDialog::tr("B4 (250 x 353 mm)"), QPrinter::B4); + isc(this, QPrintDialog::tr("B5 (176 x 250 mm, 6.93 x 9.84 inches)"), QPrinter::B5); + isc(this, QPrintDialog::tr("B6 (125 x 176 mm)"), QPrinter::B6); + isc(this, QPrintDialog::tr("B7 (88 x 125 mm)"), QPrinter::B7); + isc(this, QPrintDialog::tr("B8 (62 x 88 mm)"), QPrinter::B8); + isc(this, QPrintDialog::tr("B9 (44 x 62 mm)"), QPrinter::B9); + isc(this, QPrintDialog::tr("B10 (31 x 44 mm)"), QPrinter::B10); + isc(this, QPrintDialog::tr("C5E (163 x 229 mm)"), QPrinter::C5E); + isc(this, QPrintDialog::tr("DLE (110 x 220 mm)"), QPrinter::DLE); + isc(this, QPrintDialog::tr("Executive (7.5 x 10 inches, 191 x 254 mm)"), QPrinter::Executive); + isc(this, QPrintDialog::tr("Folio (210 x 330 mm)"), QPrinter::Folio); + isc(this, QPrintDialog::tr("Ledger (432 x 279 mm)"), QPrinter::Ledger); + isc(this, QPrintDialog::tr("Legal (8.5 x 14 inches, 216 x 356 mm)"), QPrinter::Legal); + isc(this, QPrintDialog::tr("Letter (8.5 x 11 inches, 216 x 279 mm)"), QPrinter::Letter); + isc(this, QPrintDialog::tr("Tabloid (279 x 432 mm)"), QPrinter::Tabloid); + isc(this, QPrintDialog::tr("US Common #10 Envelope (105 x 241 mm)"), QPrinter::Comm10E); + + QObject::connect(sizeCombo, SIGNAL(activated(int)), + q, SLOT(_q_paperSizeSelected(int))); +} + +void QPrintDialogPrivate::setupOptions() +{ + Q_Q(QPrintDialog); + + // no. of copies + copies = q->findChild<QSpinBox *>("copies"); + QObject::connect(copies, SIGNAL(valueChanged(int)), + q, SLOT(_q_setNumCopies(int))); + + // print range + rangeCombo = q->findChild<QComboBox *>("rangeCombo"); + rangeCombo->addItem(QPrintDialog::tr("Print all"), QPrintDialog::AllPages); + rangeCombo->addItem(QPrintDialog::tr("Print selection"), QPrintDialog::Selection); + rangeCombo->addItem(QPrintDialog::tr("Print range"), QPrintDialog::PageRange); + QObject::connect(rangeCombo, SIGNAL(activated(int)), + q, SLOT(_q_printRangeSelected(int))); + + // page range + firstPage = q->findChild<QSpinBox *>("firstPage"); + firstPage->setRange(1, 9999); + firstPage->setValue(1); + QObject::connect(firstPage, SIGNAL(valueChanged(int)), + q, SLOT(_q_setFirstPage(int))); + + lastPage = q->findChild<QSpinBox *>("lastPage"); + lastPage->setRange(1, 9999); + lastPage->setValue(1); + QObject::connect(lastPage, SIGNAL(valueChanged(int)), + q, SLOT(_q_setLastPage(int))); + + // print order + pageOrderCombo = q->findChild<QComboBox *>("pageOrderCombo"); + QObject::connect(pageOrderCombo, SIGNAL(activated(int)), + q, SLOT(_q_pageOrderSelected(int))); +} + +bool QPrintDialog::eventFilter(QObject *o, QEvent *e) +{ + Q_UNUSED(o); + + Q_D(QPrintDialog); + switch (e->type()){ + case QEvent::KeyPress: + switch (static_cast<QKeyEvent*>(e)->key()) { + case Qt::Key_Back: + d->_q_okClicked(); + return true; + } + break; + default: + break; + } + return false; +} + +QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent) + : QAbstractPrintDialog(*(new QPrintDialogPrivate), printer, parent) +{ + d_func()->init(); +} + +QPrintDialog::QPrintDialog(QWidget *parent) + : QAbstractPrintDialog(*(new QPrintDialogPrivate), 0, parent) +{ + d_func()->init(); +} + +QPrintDialog::~QPrintDialog() +{ +} + +void QPrintDialogPrivate::setPrinter(QPrinter *p, bool pickUpSettings) +{ + Q_Q(QPrintDialog); + printer = p; + + if (p && pickUpSettings) { + // top to bottom in the old dialog. + // printer or file + outputToFile = !p->outputFileName().isEmpty() && q->isOptionEnabled(QPrintDialog::PrintToFile); + if (outputToFile) + printToFileButton->setChecked(true); + else + printToPrinterButton->setChecked(true); + fileName->setEnabled(outputToFile); + + // file name + if (q->isOptionEnabled(QPrintDialog::PrintToFile)) { + fileName->setText(p->outputFileName()); + fileName->setModified(!fileName->text().isEmpty()); + } else { + printToFileButton->setEnabled(false); + } + + // orientation + orientationCombo->setCurrentIndex((int)p->orientation()); + _q_orientSelected(p->orientation()); + + // page size + int n = 0; + while (n < QPrinter::NPaperSize && + indexToPaperSize[n] != p->pageSize()) + n++; + sizeCombo->setCurrentIndex(n); + _q_paperSizeSelected(n); + + // page order + pageOrder2 = p->pageOrder(); + pageOrderCombo->setCurrentIndex((int)pageOrder2); + + // color mode + colorMode2 = p->colorMode(); + if (colorMode2 == QPrinter::Color) + printColor->setChecked(true); + else + printGray->setChecked(true); + + // number of copies + copies->setValue(p->numCopies()); + _q_setNumCopies(p->numCopies()); + } + + if (p) { + if (!q->isOptionEnabled(QPrintDialog::PrintSelection) + && rangeCombo->findData(QPrintDialog::Selection) > 0) + rangeCombo->removeItem(rangeCombo->findData(QPrintDialog::Selection)); + if (!q->isOptionEnabled(QPrintDialog::PrintPageRange) + && rangeCombo->findData(QPrintDialog::PageRange) > 0) + rangeCombo->removeItem(rangeCombo->findData(QPrintDialog::PageRange)); + + switch (q->printRange()) { + case QPrintDialog::AllPages: + rangeCombo->setCurrentIndex((int)(QPrintDialog::AllPages)); + break; + case QPrintDialog::Selection: + rangeCombo->setCurrentIndex((int)(QPrintDialog::Selection)); + break; + case QPrintDialog::PageRange: + rangeCombo->setCurrentIndex((int)(QPrintDialog::PageRange)); + break; + } + } + + if (p && q->maxPage()) { + int from = q->minPage(); + int to = q->maxPage(); + if (q->printRange() == QPrintDialog::PageRange) { + from = q->fromPage(); + to = q->toPage(); + } + firstPage->setRange(q->minPage(), to); + lastPage->setRange(from, q->maxPage()); + firstPage->setValue(from); + lastPage->setValue(to); + } +} + +int QPrintDialog::exec() +{ + Q_D(QPrintDialog); + d->setPrinter(d->printer, true); + return QDialog::exec(); +} + +void QPrintDialogPrivate::init() +{ + Q_Q(QPrintDialog); + numCopies = 1; + + if (_qt_print_dialog_creator) + (*_qt_print_dialog_creator)(q); + + setupDestination(); + setupPrinterSettings(); + setupPaper(); + setupOptions(); + + setPrinter(printer, true); + + q->installEventFilter(q); +} + +void QPrintDialog::setVisible(bool visible) +{ + QAbstractPrintDialog::setVisible(visible); +} + +QT_END_NAMESPACE + +#include "moc_qprintdialog.cpp" +#include "qrc_qprintdialog.cpp" + +#endif // QT_NO_PRINTDIALOG diff --git a/src/gui/dialogs/qprintdialog_unix.cpp b/src/gui/dialogs/qprintdialog_unix.cpp new file mode 100644 index 0000000000..76c22d0347 --- /dev/null +++ b/src/gui/dialogs/qprintdialog_unix.cpp @@ -0,0 +1,1266 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#ifndef QT_NO_PRINTDIALOG + +#include "private/qabstractprintdialog_p.h" +#include "qfiledialog_p.h" +#include <QtGui/qmessagebox.h> +#include "qprintdialog.h" +#include "qfiledialog.h" +#include <QtCore/qdir.h> +#include <QtGui/qfilesystemmodel.h> +#include <QtGui/qstyleditemdelegate.h> +#include <QtGui/qprinter.h> + +#include <QtGui/qdialogbuttonbox.h> + +#include "ui_qprintpropertieswidget.h" +#include "ui_qprintsettingsoutput.h" +#include "ui_qprintwidget.h" + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) +# include <private/qcups_p.h> +# include <cups/cups.h> +# include <private/qpdf_p.h> +#else +# include <QtCore/qlibrary.h> +#endif + +#include <private/qprinterinfo_unix_p.h> + +QT_BEGIN_NAMESPACE + +extern int qt_printerRealNumCopies(QPaintEngine *); + +class QOptionTreeItem; +class QPPDOptionsModel; + +class QPrintPropertiesDialog : public QDialog +{ + Q_OBJECT +public: + QPrintPropertiesDialog(QAbstractPrintDialog *parent = 0); + ~QPrintPropertiesDialog(); + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + void setCups(QCUPSSupport *cups) { m_cups = cups; } + void addItemToOptions(QOptionTreeItem *parent, QList<const ppd_option_t*>& options, QList<const char*>& markedOptions) const; +#endif + + void selectPrinter(); + void selectPdfPsPrinter(const QPrinter *p); + + /// copy printer properties to the widget + void applyPrinterProperties(QPrinter *p); + void setupPrinter() const; + +protected: + void showEvent(QShowEvent* event); + +private: + Ui::QPrintPropertiesWidget widget; + QDialogButtonBox *m_buttons; +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + QCUPSSupport *m_cups; + QPPDOptionsModel *m_cupsOptionsModel; +#endif +}; + +class QPrintDialogPrivate : public QAbstractPrintDialogPrivate +{ + Q_DECLARE_PUBLIC(QPrintDialog) + Q_DECLARE_TR_FUNCTIONS(QPrintDialog) +public: + QPrintDialogPrivate(); + ~QPrintDialogPrivate(); + + void init(); + /// copy printer properties to the widget + void applyPrinterProperties(QPrinter *p); + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + void selectPrinter(QCUPSSupport *cups); +#endif + + void _q_chbPrintLastFirstToggled(bool); +#ifndef QT_NO_MESSAGEBOX + void _q_checkFields(); +#endif + void _q_collapseOrExpandDialog(); + + void setupPrinter(); + void updateWidgets(); + + virtual void setTabs(const QList<QWidget*> &tabs); + + Ui::QPrintSettingsOutput options; + QUnixPrintWidget *top; + QWidget *bottom; + QDialogButtonBox *buttons; + QPushButton *collapseButton; +}; + +#if defined (Q_OS_UNIX) +class QUnixPrintWidgetPrivate +{ +public: + QUnixPrintWidgetPrivate(QUnixPrintWidget *q); + ~QUnixPrintWidgetPrivate(); + + /// copy printer properties to the widget + void applyPrinterProperties(QPrinter *p); + bool checkFields(); + void setupPrinter(); + void setOptionsPane(QPrintDialogPrivate *pane); + +// slots + void _q_printerChanged(int index); + void _q_btnPropertiesClicked(); + void _q_btnBrowseClicked(); + + QUnixPrintWidget * const parent; + QPrintPropertiesDialog *propertiesDialog; + Ui::QPrintWidget widget; + QAbstractPrintDialog * q; + QPrinter *printer; + QList<QPrinterDescription> lprPrinters; + void updateWidget(); + +private: + QPrintDialogPrivate *optionsPane; + bool filePrintersAdded; +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + QCUPSSupport* cups; + int cupsPrinterCount; + const cups_dest_t* cupsPrinters; + const ppd_file_t* cupsPPD; +#endif +}; +#endif + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) +class QOptionTreeItem +{ +public: + enum ItemType { Root, Group, Option, Choice }; + + QOptionTreeItem(ItemType t, int i, const void* p, const char* desc, QOptionTreeItem* pi) + : type(t), + index(i), + ptr(p), + description(desc), + selected(-1), + selDescription(0), + parentItem(pi) {}; + + ~QOptionTreeItem() { + while (!childItems.isEmpty()) + delete childItems.takeFirst(); + }; + + ItemType type; + int index; + const void* ptr; + const char* description; + int selected; + const char* selDescription; + QOptionTreeItem* parentItem; + QList<QOptionTreeItem*> childItems; +}; + +class QPPDOptionsModel : public QAbstractItemModel +{ + friend class QPPDOptionsEditor; +public: + QPPDOptionsModel(QCUPSSupport *cups, QObject *parent = 0); + ~QPPDOptionsModel(); + + int columnCount(const QModelIndex& parent = QModelIndex()) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index) const; + Qt::ItemFlags flags(const QModelIndex& index) const; + QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + + QOptionTreeItem* rootItem; + QCUPSSupport *cups; + const ppd_file_t* ppd; + void parseItems(); + void parseGroups(QOptionTreeItem* parent); + void parseOptions(QOptionTreeItem* parent); + void parseChoices(QOptionTreeItem* parent); +}; + +class QPPDOptionsEditor : public QStyledItemDelegate +{ + Q_OBJECT +public: + QPPDOptionsEditor(QObject* parent = 0) : QStyledItemDelegate(parent) {}; + ~QPPDOptionsEditor() {}; + + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void setEditorData(QWidget* editor, const QModelIndex& index) const; + void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + +private slots: + void cbChanged(int index); + +}; + +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +QPrintPropertiesDialog::QPrintPropertiesDialog(QAbstractPrintDialog *parent) + : QDialog(parent) +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + , m_cups(0), m_cupsOptionsModel(0) +#endif +{ + QVBoxLayout *lay = new QVBoxLayout(this); + this->setLayout(lay); + 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), SIGNAL(clicked()), this, SLOT(accept())); + connect(m_buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject())); +} + +QPrintPropertiesDialog::~QPrintPropertiesDialog() +{ +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + delete m_cupsOptionsModel; +#else + delete widget.cupsPropertiesPage; +#endif +} + +void QPrintPropertiesDialog::applyPrinterProperties(QPrinter *p) +{ + widget.pageSetup->setPrinter(p); +} + +void QPrintPropertiesDialog::setupPrinter() const +{ + widget.pageSetup->setupPrinter(); + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + QPPDOptionsModel* model = static_cast<QPPDOptionsModel*>(widget.treeView->model()); + if (model) { + QOptionTreeItem* rootItem = model->rootItem; + QList<const ppd_option_t*> options; + QList<const char*> markedOptions; + + addItemToOptions(rootItem, options, markedOptions); + model->cups->saveOptions(options, markedOptions); + } +#endif +} + +void QPrintPropertiesDialog::selectPrinter() +{ +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + widget.pageSetup->selectPrinter(m_cups); + widget.treeView->setModel(0); + if (m_cups && QCUPSSupport::isAvailable()) { + + if (m_cupsOptionsModel == 0) { + m_cupsOptionsModel = new QPPDOptionsModel(m_cups); + + widget.treeView->setItemDelegate(new QPPDOptionsEditor(this)); + } else { + // update the model + m_cupsOptionsModel->parseItems(); + } + + if (m_cupsOptionsModel->rowCount() > 0) { + widget.treeView->setModel(m_cupsOptionsModel); + + for (int i = 0; i < m_cupsOptionsModel->rowCount(); ++i) + widget.treeView->expand(m_cupsOptionsModel->index(i,0)); + + widget.tabs->setTabEnabled(1, true); // enable the advanced tab + } else { + widget.tabs->setTabEnabled(1, false); + } + + } else +#endif + { + widget.cupsPropertiesPage->setEnabled(false); + widget.pageSetup->selectPrinter(0); + } +} + +void QPrintPropertiesDialog::selectPdfPsPrinter(const QPrinter *p) +{ + widget.treeView->setModel(0); + widget.pageSetup->selectPdfPsPrinter(p); + widget.tabs->setTabEnabled(1, false); // disable the advanced tab +} + +void QPrintPropertiesDialog::showEvent(QShowEvent* event) +{ + widget.treeView->resizeColumnToContents(0); + event->accept(); +} + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) +void QPrintPropertiesDialog::addItemToOptions(QOptionTreeItem *parent, QList<const ppd_option_t*>& options, QList<const char*>& markedOptions) const +{ + for (int i = 0; i < parent->childItems.count(); ++i) { + QOptionTreeItem *itm = parent->childItems.at(i); + if (itm->type == QOptionTreeItem::Option) { + const ppd_option_t* opt = reinterpret_cast<const ppd_option_t*>(itm->ptr); + options << opt; + if (qstrcmp(opt->defchoice, opt->choices[itm->selected].choice) != 0) { + markedOptions << opt->keyword << opt->choices[itm->selected].choice; + } + } else { + addItemToOptions(itm, options, markedOptions); + } + } +} +#endif + +QPrintDialogPrivate::QPrintDialogPrivate() + : top(0), bottom(0), buttons(0), collapseButton(0) +{ +} + +QPrintDialogPrivate::~QPrintDialogPrivate() +{ +} + +void QPrintDialogPrivate::init() +{ + Q_Q(QPrintDialog); + + top = new QUnixPrintWidget(0, q); + bottom = new QWidget(q); + options.setupUi(bottom); + options.color->setIconSize(QSize(32, 32)); + options.color->setIcon(QIcon(QLatin1String(":/trolltech/dialogs/qprintdialog/images/status-color.png"))); + options.grayscale->setIconSize(QSize(32, 32)); + options.grayscale->setIcon(QIcon(QLatin1String(":/trolltech/dialogs/qprintdialog/images/status-gray-scale.png"))); + 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); + q->setLayout(lay); + lay->addWidget(top); + lay->addWidget(bottom); + lay->addWidget(buttons); + + QPrinter* p = q->printer(); + + applyPrinterProperties(p); + +#ifdef QT_NO_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.reverse, SIGNAL(toggled(bool)), + q, SLOT(_q_chbPrintLastFirstToggled(bool))); + + QObject::connect(collapseButton, SIGNAL(released()), q, SLOT(_q_collapseOrExpandDialog())); +} + +void QPrintDialogPrivate::applyPrinterProperties(QPrinter *p) +{ + if (p->colorMode() == QPrinter::Color) + options.color->setChecked(true); + else + options.grayscale->setChecked(true); + + switch(p->duplex()) { + case QPrinter::DuplexNone: + options.noDuplex->setChecked(true); break; + case QPrinter::DuplexLongSide: + case QPrinter::DuplexAuto: + options.duplexLong->setChecked(true); break; + case QPrinter::DuplexShortSide: + options.duplexShort->setChecked(true); break; + } + options.copies->setValue(qt_printerRealNumCopies(p->paintEngine())); + options.collate->setChecked(p->collateCopies()); + options.reverse->setChecked(p->pageOrder() == QPrinter::LastPageFirst); + top->d->applyPrinterProperties(p); +} + +void QPrintDialogPrivate::_q_chbPrintLastFirstToggled(bool checked) +{ + Q_Q(QPrintDialog); + if (checked) + q->printer()->setPageOrder(QPrinter::LastPageFirst); + else + q->printer()->setPageOrder(QPrinter::FirstPageFirst); +} + +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) ); + } +} + +#ifndef QT_NO_MESSAGEBOX +void QPrintDialogPrivate::_q_checkFields() +{ + Q_Q(QPrintDialog); + if (top->d->checkFields()) + q->accept(); +} +#endif // QT_NO_MESSAGEBOX + +void QPrintDialogPrivate::setupPrinter() +{ + 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); + } + + p->setColorMode( options.color->isChecked() ? QPrinter::Color : QPrinter::GrayScale ); + + // 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.printRange->isChecked()) { + p->setPrintRange(QPrinter::PageRange); + p->setFromTo(options.from->value(), qMax(options.from->value(), options.to->value())); + } + + // copies + p->setNumCopies(options.copies->value()); + p->setCollateCopies(options.collate->isChecked()); + + top->d->setupPrinter(); +} + +void QPrintDialogPrivate::updateWidgets() +{ + Q_Q(QPrintDialog); + options.gbPrintRange->setVisible(q->isOptionEnabled(QPrintDialog::PrintPageRange) || + q->isOptionEnabled(QPrintDialog::PrintSelection)); + + options.printRange->setEnabled(q->isOptionEnabled(QPrintDialog::PrintPageRange)); + options.printSelection->setVisible(q->isOptionEnabled(QPrintDialog::PrintSelection)); + options.collate->setVisible(q->isOptionEnabled(QPrintDialog::PrintCollateCopies)); + + switch (q->printRange()) { + case QPrintDialog::AllPages: + options.printAll->setChecked(true); + break; + case QPrintDialog::Selection: + options.printSelection->setChecked(true); + break; + case QPrintDialog::PageRange: + options.printRange->setChecked(true); + 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) +{ + while(options.tabs->count() > 2) + delete options.tabs->widget(2); + + QList<QWidget*>::ConstIterator iter = tabWidgets.begin(); + while(iter != tabWidgets.constEnd()) { + QWidget *tab = *iter; + options.tabs->addTab(tab, tab->windowTitle()); + ++iter; + } +} + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) +void QPrintDialogPrivate::selectPrinter(QCUPSSupport *cups) +{ + options.duplex->setEnabled(cups && cups->ppdOption("Duplex")); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// + +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), 0, 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); + d->setupPrinter(); + QDialog::accept(); +} + +#ifdef QT3_SUPPORT +QPrinter *QPrintDialog::printer() const +{ + Q_D(const QPrintDialog); + return d->printer; +} + +void QPrintDialog::setPrinter(QPrinter *printer, bool pickupSettings) +{ + if (!printer) + return; + + Q_D(QPrintDialog); + d->printer = printer; + + if (pickupSettings) + d->applyPrinterProperties(printer); +} + +void QPrintDialog::addButton(QPushButton *button) +{ + Q_D(QPrintDialog); + d->buttons->addButton(button, QDialogButtonBox::HelpRole); +} +#endif // QT3_SUPPORT + +#if defined (Q_OS_UNIX) + +/*! \internal +*/ +QUnixPrintWidgetPrivate::QUnixPrintWidgetPrivate(QUnixPrintWidget *p) + : parent(p), propertiesDialog(0), printer(0), optionsPane(0), filePrintersAdded(false) +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + , cups(0), cupsPrinterCount(0), cupsPrinters(0), cupsPPD(0) +#endif +{ + q = 0; + if (parent) + q = qobject_cast<QAbstractPrintDialog*> (parent->parent()); + + widget.setupUi(parent); + + int currentPrinterIndex = 0; +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + cups = new QCUPSSupport; + if (QCUPSSupport::isAvailable()) { + cupsPPD = cups->currentPPD(); + cupsPrinterCount = cups->availablePrintersCount(); + cupsPrinters = cups->availablePrinters(); + + for (int i = 0; i < cupsPrinterCount; ++i) { + QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name)); + if (cupsPrinters[i].instance) + printerName += QLatin1String("/") + QString::fromLocal8Bit(cupsPrinters[i].instance); + + widget.printers->addItem(printerName); + if (cupsPrinters[i].is_default) + widget.printers->setCurrentIndex(i); + } + // the model depends on valid ppd. so before enabling the + // properties button we make sure the ppd is in fact valid. + if (cupsPrinterCount && cups->currentPPD()) { + widget.properties->setEnabled(true); + } + currentPrinterIndex = cups->currentPrinterIndex(); + } else { +#endif + currentPrinterIndex = qt_getLprPrinters(lprPrinters); + // populating printer combo + QList<QPrinterDescription>::const_iterator i = lprPrinters.constBegin(); + for(; i != lprPrinters.constEnd(); ++i) + widget.printers->addItem((*i).name); +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + } +#endif + +#ifndef QT_NO_FILESYSTEMMODEL + QFileSystemModel *fsm = new QFileSystemModel(widget.filename); + fsm->setRootPath(QDir::homePath()); +#if !defined(QT_NO_COMPLETER) && !defined(QT_NO_FILEDIALOG) + widget.filename->setCompleter(new QFSCompletor(fsm, widget.filename)); +#endif +#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)")); + widget.printers->addItem(QPrintDialog::tr("Print to File (Postscript)")); + 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->printerName().isEmpty()) { + if (printer->outputFormat() == QPrinter::PdfFormat) + widget.printers->setCurrentIndex(widget.printers->count() - 2); + else if (printer->outputFormat() == QPrinter::PostScriptFormat) + 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() +{ +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + delete cups; +#endif +} + +void QUnixPrintWidgetPrivate::_q_printerChanged(int index) +{ + if (index < 0) + return; + const int printerCount = widget.printers->count(); + widget.filename->setEnabled(false); + widget.lOutput->setEnabled(false); + + if (filePrintersAdded) { + Q_ASSERT(index != printerCount - 3); // separator + if (index > printerCount - 3) { // PDF or postscript + bool pdfPrinter = (index == printerCount - 2); + widget.location->setText(QPrintDialog::tr("Local file")); + widget.type->setText(QPrintDialog::tr("Write %1 file").arg(pdfPrinter ? QString::fromLatin1("PDF") + : QString::fromLatin1("PostScript"))); + widget.properties->setEnabled(true); + widget.filename->setEnabled(true); + QString filename = widget.filename->text(); + QString suffix = QFileInfo(filename).suffix(); + if (pdfPrinter && suffix == QLatin1String("ps")) + filename = filename.replace(QLatin1String(".ps"), QLatin1String(".pdf")); + if (!pdfPrinter && suffix == QLatin1String("pdf")) + filename = filename.replace(QLatin1String(".pdf"), QLatin1String(".ps")); + widget.filename->setText(filename); + widget.lOutput->setEnabled(true); + if (propertiesDialog) + propertiesDialog->selectPdfPsPrinter(printer); +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (optionsPane) + optionsPane->selectPrinter(0); +#endif + return; + } + } + + widget.location->setText(QString()); +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (QCUPSSupport::isAvailable()) { + cups->setCurrentPrinter(index); + + const cups_option_t *opt = cups->printerOption(QString::fromLatin1("printer-location")); + QString location; + if (opt) + location = QString::fromLocal8Bit(opt->value); + widget.location->setText(location); + + cupsPPD = cups->currentPPD(); + // set printer type line + QString type; + if (cupsPPD) + type = QString::fromLocal8Bit(cupsPPD->manufacturer) + QLatin1String(" - ") + QString::fromLocal8Bit(cupsPPD->modelname); + widget.type->setText(type); + if (propertiesDialog) + propertiesDialog->selectPrinter(); + if (optionsPane) + optionsPane->selectPrinter(cups); + } else { + if (optionsPane) + optionsPane->selectPrinter(0); +#endif + if (lprPrinters.count() > 0) { + QString type = lprPrinters.at(index).name + QLatin1String("@") + lprPrinters.at(index).host; + if (!lprPrinters.at(index).comment.isEmpty()) + type += QLatin1String(", ") + lprPrinters.at(index).comment; + widget.type->setText(type); + if (propertiesDialog) + propertiesDialog->selectPrinter(); + } +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + } +#endif +} + +void QUnixPrintWidgetPrivate::setOptionsPane(QPrintDialogPrivate *pane) +{ + optionsPane = pane; + if (optionsPane) + _q_printerChanged(widget.printers->currentIndex()); +} + +void QUnixPrintWidgetPrivate::_q_btnBrowseClicked() +{ + const int prevPrinter = widget.printers->currentIndex(); + widget.printers->setCurrentIndex(widget.printers->count() - 2); // the pdf one + + QString filename = widget.filename->text(); +#ifndef QT_NO_FILEDIALOG + filename = QFileDialog::getSaveFileName(parent, QPrintDialog::tr("Print To File ..."), filename, + QString(), 0, QFileDialog::DontConfirmOverwrite); +#else + filename.clear(); +#endif + if (!filename.isEmpty()) { + widget.filename->setText(filename); + if (filename.endsWith(QString::fromLatin1(".ps"), Qt::CaseInsensitive)) + widget.printers->setCurrentIndex(widget.printers->count() - 1); // the postscript one + } + else + widget.printers->setCurrentIndex(prevPrinter); +} + +void QUnixPrintWidgetPrivate::applyPrinterProperties(QPrinter *p) +{ + if (p == 0) + return; + printer = p; + if (p->outputFileName().isEmpty()) { + QString home = QString::fromLocal8Bit(qgetenv("HOME").constData()); + QString cur = QDir::currentPath(); + if (home.at(home.length()-1) != QLatin1Char('/')) + home += QLatin1Char('/'); + if (cur.at(cur.length()-1) != QLatin1Char('/')) + cur += QLatin1Char('/'); + if (cur.left(home.length()) != home) + cur = home; +#ifdef Q_WS_X11 + if (p->docName().isEmpty()) { + if (p->outputFormat() == QPrinter::PostScriptFormat) + cur += QLatin1String("print.ps"); + else + cur += QLatin1String("print.pdf"); + } else { + QRegExp re(QString::fromLatin1("(.*)\\.\\S+")); + if (re.exactMatch(p->docName())) + cur += re.cap(1); + else + cur += p->docName(); + if (p->outputFormat() == QPrinter::PostScriptFormat) + cur += QLatin1String(".ps"); + else + cur += QLatin1String(".pdf"); + } +#endif + widget.filename->setText(cur); + } + else + widget.filename->setText( p->outputFileName() ); + QString printer = p->printerName(); + if (!printer.isEmpty()) { + for (int i = 0; i < widget.printers->count(); ++i) { + if (widget.printers->itemText(i) == printer) { + widget.printers->setCurrentIndex(i); + break; + } + } + } + // PDF and PS printers are not added to the dialog yet, we'll handle those cases in QUnixPrintWidgetPrivate::updateWidget + + if (propertiesDialog) + propertiesDialog->applyPrinterProperties(p); +} + +#ifndef QT_NO_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(); + } + } + + // Every test passed. Accept the dialog. + return true; +} +#endif // QT_NO_MESSAGEBOX + +void QUnixPrintWidgetPrivate::_q_btnPropertiesClicked() +{ + if (propertiesDialog == 0) { + propertiesDialog = new QPrintPropertiesDialog(q); + propertiesDialog->setResult(QDialog::Rejected); + } + + if (propertiesDialog->result() == QDialog::Rejected) { +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + propertiesDialog->setCups(cups); +#endif + propertiesDialog->applyPrinterProperties(q->printer()); + + if (q->isOptionEnabled(QPrintDialog::PrintToFile) + && (widget.printers->currentIndex() > widget.printers->count() - 3)) // PDF or postscript + propertiesDialog->selectPdfPsPrinter(q->printer()); + else + propertiesDialog->selectPrinter(); + } + propertiesDialog->exec(); +} + +void QUnixPrintWidgetPrivate::setupPrinter() +{ + const int printerCount = widget.printers->count(); + const int index = widget.printers->currentIndex(); + + if (filePrintersAdded && index > printerCount - 3) { // PDF or postscript + printer->setPrinterName(QString()); + Q_ASSERT(index != printerCount - 3); // separator + if (index == printerCount - 2) + printer->setOutputFormat(QPrinter::PdfFormat); + else + printer->setOutputFormat(QPrinter::PostScriptFormat); + 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 && propertiesDialog->result() == QDialog::Accepted) + propertiesDialog->setupPrinter(); +} + + +/*! \internal +*/ +QUnixPrintWidget::QUnixPrintWidget(QPrinter *printer, QWidget *parent) + : QWidget(parent), d(new QUnixPrintWidgetPrivate(this)) +{ + d->applyPrinterProperties(printer); +} + +/*! \internal +*/ +QUnixPrintWidget::~QUnixPrintWidget() +{ + delete d; +} + +/*! \internal + + Updates the printer with the states held in the QUnixPrintWidget. +*/ +void QUnixPrintWidget::updatePrinter() +{ + d->setupPrinter(); +} + +#endif + +//////////////////////////////////////////////////////////////////////////////// +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + +QPPDOptionsModel::QPPDOptionsModel(QCUPSSupport *c, QObject *parent) + : QAbstractItemModel(parent), rootItem(0), cups(c), ppd(c->currentPPD()) +{ + parseItems(); +} + +QPPDOptionsModel::~QPPDOptionsModel() +{ +} + +int QPPDOptionsModel::columnCount(const QModelIndex&) const +{ + return 2; +} + +int QPPDOptionsModel::rowCount(const QModelIndex& parent) const +{ + QOptionTreeItem* itm; + if (!parent.isValid()) + itm = rootItem; + else + itm = reinterpret_cast<QOptionTreeItem*>(parent.internalPointer()); + + if (itm->type == QOptionTreeItem::Option) + return 0; + + return itm->childItems.count(); +} + +QVariant QPPDOptionsModel::data(const QModelIndex& index, int role) const +{ + switch(role) { + case Qt::FontRole: { + QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer()); + if (itm && itm->type == QOptionTreeItem::Group){ + QFont font = QApplication::font(); + font.setBold(true); + return QVariant(font); + } + return QVariant(); + } + break; + + case Qt::DisplayRole: { + QOptionTreeItem* itm; + if (!index.isValid()) + itm = rootItem; + else + itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer()); + + if (index.column() == 0) + return cups->unicodeString(itm->description); + else if (itm->type == QOptionTreeItem::Option && itm->selected > -1) + return cups->unicodeString(itm->selDescription); + else + return QVariant(); + } + break; + + default: + return QVariant(); + } + if (role != Qt::DisplayRole) + return QVariant(); +} + +QModelIndex QPPDOptionsModel::index(int row, int column, const QModelIndex& parent) const +{ + QOptionTreeItem* itm; + if (!parent.isValid()) + itm = rootItem; + else + itm = reinterpret_cast<QOptionTreeItem*>(parent.internalPointer()); + + return createIndex(row, column, itm->childItems.at(row)); +} + + +QModelIndex QPPDOptionsModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer()); + + if (itm->parentItem && itm->parentItem != rootItem) + return createIndex(itm->parentItem->index, 0, itm->parentItem); + else + return QModelIndex(); +} + +Qt::ItemFlags QPPDOptionsModel::flags(const QModelIndex& index) const +{ + if (!index.isValid() || reinterpret_cast<QOptionTreeItem*>(index.internalPointer())->type == QOptionTreeItem::Group) + return Qt::ItemIsEnabled; + + if (index.column() == 1) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +void QPPDOptionsModel::parseItems() +{ + emit layoutAboutToBeChanged(); + ppd = cups->currentPPD(); + delete rootItem; + rootItem = new QOptionTreeItem(QOptionTreeItem::Root, 0, ppd, "Root Item", 0); + parseGroups(rootItem); + emit layoutChanged(); +} + +void QPPDOptionsModel::parseGroups(QOptionTreeItem* parent) +{ + if (parent->type == QOptionTreeItem::Root) { + + const ppd_file_t* ppdFile = reinterpret_cast<const ppd_file_t*>(parent->ptr); + + if (ppdFile) { + for (int i = 0; i < ppdFile->num_groups; ++i) { + QOptionTreeItem* group = new QOptionTreeItem(QOptionTreeItem::Group, i, &ppdFile->groups[i], ppdFile->groups[i].text, parent); + parent->childItems.append(group); + parseGroups(group); // parse possible subgroups + parseOptions(group); // parse options + } + } + } else if (parent->type == QOptionTreeItem::Group) { + + const ppd_group_t* group = reinterpret_cast<const ppd_group_t*>(parent->ptr); + + if (group) { + for (int i = 0; i < group->num_subgroups; ++i) { + QOptionTreeItem* subgroup = new QOptionTreeItem(QOptionTreeItem::Group, i, &group->subgroups[i], group->subgroups[i].text, parent); + parent->childItems.append(subgroup); + parseGroups(subgroup); // parse possible subgroups + parseOptions(subgroup); // parse options + } + } + } +} + +void QPPDOptionsModel::parseOptions(QOptionTreeItem* parent) +{ + const ppd_group_t* group = reinterpret_cast<const ppd_group_t*>(parent->ptr); + for (int i = 0; i < group->num_options; ++i) { + QOptionTreeItem* opt = new QOptionTreeItem(QOptionTreeItem::Option, i, &group->options[i], group->options[i].text, parent); + parent->childItems.append(opt); + parseChoices(opt); + } +} + +void QPPDOptionsModel::parseChoices(QOptionTreeItem* parent) +{ + const ppd_option_t* option = reinterpret_cast<const ppd_option_t*>(parent->ptr); + bool marked = false; + for (int i = 0; i < option->num_choices; ++i) { + QOptionTreeItem* choice = new QOptionTreeItem(QOptionTreeItem::Choice, i, &option->choices[i], option->choices[i].text, parent); + if (static_cast<int>(option->choices[i].marked) == 1) { + parent->selected = i; + parent->selDescription = option->choices[i].text; + marked = true; + } else if (!marked && qstrcmp(option->choices[i].choice, option->defchoice) == 0) { + parent->selected = i; + parent->selDescription = option->choices[i].text; + } + parent->childItems.append(choice); + } +} + +QVariant QPPDOptionsModel::headerData(int section, Qt::Orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + switch(section){ + case 0: + return QVariant(QApplication::translate("QPPDOptionsModel","Name")); + case 1: + return QVariant(QApplication::translate("QPPDOptionsModel","Value")); + default: + return QVariant(); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +QWidget* QPPDOptionsEditor::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const +{ + if (index.column() == 1 && reinterpret_cast<QOptionTreeItem*>(index.internalPointer())->type == QOptionTreeItem::Option) + return new QComboBox(parent); + else + return 0; +} + +void QPPDOptionsEditor::setEditorData(QWidget* editor, const QModelIndex& index) const +{ + if (index.column() != 1) + return; + + QComboBox* cb = static_cast<QComboBox*>(editor); + QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer()); + + if (itm->selected == -1) + cb->addItem(QString()); + + for (int i = 0; i < itm->childItems.count(); ++i) + cb->addItem(QString::fromLocal8Bit(itm->childItems.at(i)->description)); + + if (itm->selected > -1) + cb->setCurrentIndex(itm->selected); + + connect(cb, SIGNAL(currentIndexChanged(int)), this, SLOT(cbChanged(int))); +} + +void QPPDOptionsEditor::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +{ + QComboBox* cb = static_cast<QComboBox*>(editor); + QOptionTreeItem* itm = reinterpret_cast<QOptionTreeItem*>(index.internalPointer()); + + if (itm->selected == cb->currentIndex()) + return; + + const ppd_option_t* opt = reinterpret_cast<const ppd_option_t*>(itm->ptr); + QPPDOptionsModel* m = static_cast<QPPDOptionsModel*>(model); + + if (m->cups->markOption(opt->keyword, opt->choices[cb->currentIndex()].choice) == 0) { + itm->selected = cb->currentIndex(); + itm->selDescription = reinterpret_cast<const ppd_option_t*>(itm->ptr)->choices[itm->selected].text; + } +} + +void QPPDOptionsEditor::cbChanged(int) +{ +/* + emit commitData(static_cast<QWidget*>(sender())); +*/ +} + +#endif + +QT_END_NAMESPACE + +#include "moc_qprintdialog.cpp" +#include "qprintdialog_unix.moc" +#include "qrc_qprintdialog.cpp" + +#endif // QT_NO_PRINTDIALOG + diff --git a/src/gui/dialogs/qprintdialog_win.cpp b/src/gui/dialogs/qprintdialog_win.cpp new file mode 100644 index 0000000000..936d1ed9e8 --- /dev/null +++ b/src/gui/dialogs/qprintdialog_win.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_PRINTDIALOG + +#include "qprintdialog.h" + +#include <qwidget.h> +#include <qapplication.h> +#include <qmessagebox.h> +#include <private/qapplication_p.h> + +#include <private/qabstractprintdialog_p.h> +#include <private/qprintengine_win_p.h> +#include <private/qprinter_p.h> + +QT_BEGIN_NAMESPACE + +extern void qt_win_eatMouseMove(); + +class QPrintDialogPrivate : public QAbstractPrintDialogPrivate +{ + Q_DECLARE_PUBLIC(QPrintDialog) +public: + QPrintDialogPrivate() + : ep(0) + { + } + + inline void _q_printToFileChanged(int) {} + inline void _q_rbPrintRangeToggled(bool) {} + inline void _q_printerChanged(int) {} + inline void _q_chbPrintLastFirstToggled(bool) {} + inline void _q_paperSizeChanged(int) {} + inline void _q_btnBrowseClicked() {} + inline void _q_btnPropertiesClicked() {} + int openWindowsPrintDialogModally(); + + QWin32PrintEnginePrivate *ep; +}; + +#ifndef Q_OS_WINCE +// If you change this function, make sure you also change the unicode equivalent +template <class PrintDialog, class DeviceMode> +static PrintDialog *qt_win_make_PRINTDLG(QWidget *parent, + QPrintDialog *pdlg, + QPrintDialogPrivate *d, HGLOBAL *tempDevNames) +{ + PrintDialog *pd = new PrintDialog; + memset(pd, 0, sizeof(PrintDialog)); + pd->lStructSize = sizeof(PrintDialog); + + void *devMode = sizeof(DeviceMode) == sizeof(DEVMODEA) + ? (void *) d->ep->devModeA() + : (void *) d->ep->devModeW(); + + if (devMode) { + int size = sizeof(DeviceMode) + ((DeviceMode *) devMode)->dmDriverExtra; + pd->hDevMode = GlobalAlloc(GHND, size); + { + void *dest = GlobalLock(pd->hDevMode); + memcpy(dest, d->ep->devMode, size); + GlobalUnlock(pd->hDevMode); + } + } else { + pd->hDevMode = NULL; + } + pd->hDevNames = tempDevNames; + + pd->Flags = PD_RETURNDC; + pd->Flags |= PD_USEDEVMODECOPIESANDCOLLATE; + + if (!pdlg->isOptionEnabled(QPrintDialog::PrintSelection)) + pd->Flags |= PD_NOSELECTION; + if (pdlg->isOptionEnabled(QPrintDialog::PrintPageRange)) { + pd->nMinPage = pdlg->minPage(); + pd->nMaxPage = pdlg->maxPage(); + } + + if(!pdlg->isOptionEnabled(QPrintDialog::PrintToFile)) + pd->Flags |= PD_DISABLEPRINTTOFILE; + + if (pdlg->printRange() == QPrintDialog::Selection) + pd->Flags |= PD_SELECTION; + else if (pdlg->printRange() == QPrintDialog::PageRange) + pd->Flags |= PD_PAGENUMS; + else + pd->Flags |= PD_ALLPAGES; + + // As stated by MSDN, to enable collate option when minpage==maxpage==0 + // set the PD_NOPAGENUMS flag + if (pd->nMinPage==0 && pd->nMaxPage==0) + pd->Flags |= PD_NOPAGENUMS; + + if (d->ep->printToFile) + pd->Flags |= PD_PRINTTOFILE; + Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); + pd->hwndOwner = parent ? parent->winId() : 0; + pd->nFromPage = qMax(pdlg->fromPage(), pdlg->minPage()); + pd->nToPage = (pdlg->toPage() > 0) ? qMin(pdlg->toPage(), pdlg->maxPage()) : 1; + pd->nCopies = d->ep->num_copies; + + return pd; +} +#endif // Q_OS_WINCE + +// If you change this function, make sure you also change the ansi equivalent +template <typename T> +static void qt_win_clean_up_PRINTDLG(T **pd) +{ + delete *pd; + *pd = 0; +} + + +// If you change this function, make sure you also change the ansi equivalent +template <typename T> +static void qt_win_read_back_PRINTDLG(T *pd, QPrintDialog *pdlg, QPrintDialogPrivate *d) +{ + if (pd->Flags & PD_SELECTION) { + pdlg->setPrintRange(QPrintDialog::Selection); + pdlg->setFromTo(0, 0); + } else if (pd->Flags & PD_PAGENUMS) { + pdlg->setPrintRange(QPrintDialog::PageRange); + pdlg->setFromTo(pd->nFromPage, pd->nToPage); + } else { + pdlg->setPrintRange(QPrintDialog::AllPages); + pdlg->setFromTo(0, 0); + } + + d->ep->printToFile = (pd->Flags & PD_PRINTTOFILE) != 0; + + d->ep->readDevnames(pd->hDevNames); + d->ep->readDevmode(pd->hDevMode); + d->ep->updateCustomPaperSize(); + + if (d->ep->printToFile && d->ep->fileName.isEmpty()) + d->ep->fileName = d->ep->port; + else if (!d->ep->printToFile && d->ep->fileName == QLatin1String("FILE:")) + d->ep->fileName.clear(); +} + +static bool warnIfNotNative(QPrinter *printer) +{ + if (printer->outputFormat() != QPrinter::NativeFormat) { + qWarning("QPrintDialog: Cannot be used on non-native printers"); + return false; + } + return true; +} + +QPrintDialog::QPrintDialog(QPrinter *printer, QWidget *parent) + : QAbstractPrintDialog( *(new QPrintDialogPrivate), printer, parent) +{ + Q_D(QPrintDialog); + if (!warnIfNotNative(d->printer)) + return; + d->ep = static_cast<QWin32PrintEngine *>(d->printer->paintEngine())->d_func(); +} + +QPrintDialog::QPrintDialog(QWidget *parent) + : QAbstractPrintDialog( *(new QPrintDialogPrivate), 0, parent) +{ + Q_D(QPrintDialog); + if (!warnIfNotNative(d->printer)) + return; + d->ep = static_cast<QWin32PrintEngine *>(d->printer->paintEngine())->d_func(); +} + +QPrintDialog::~QPrintDialog() +{ +} + +int QPrintDialog::exec() +{ + if (!warnIfNotNative(printer())) + return 0; + + Q_D(QPrintDialog); + return d->openWindowsPrintDialogModally(); +} + +int QPrintDialogPrivate::openWindowsPrintDialogModally() +{ + Q_Q(QPrintDialog); + QWidget *parent = q->parentWidget(); + if (parent) + parent = parent->window(); + else + parent = qApp->activeWindow(); + + QWidget modal_widget; + modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); + modal_widget.setParent(parent, Qt::Window); + QApplicationPrivate::enterModal(&modal_widget); + + HGLOBAL *tempDevNames = ep->createDevNames(); + + bool result; + bool done; + void *pd = QT_WA_INLINE( + (void*)(qt_win_make_PRINTDLG<PRINTDLGW, DEVMODEW>(parent, q, this, tempDevNames)), + (void*)(qt_win_make_PRINTDLG<PRINTDLGA, DEVMODEA>(parent, q, this, tempDevNames)) + ); + + do { + done = true; + QT_WA({ + PRINTDLGW *pdw = reinterpret_cast<PRINTDLGW *>(pd); + result = PrintDlgW(pdw); + if ((pdw->Flags & PD_PAGENUMS) && (pdw->nFromPage > pdw->nToPage)) + done = false; + if (result && pdw->hDC == 0) + result = false; + else if (!result) + done = true; + }, { + PRINTDLGA *pda = reinterpret_cast<PRINTDLGA *>(pd); + result = PrintDlgA(pda); + if ((pda->Flags & PD_PAGENUMS) && (pda->nFromPage > pda->nToPage)) + done = false; + if (result && pda->hDC == 0) + result = false; + else if (!result) + done = true; + }); + if (!done) { + QMessageBox::warning(0, QPrintDialog::tr("Print"), + QPrintDialog::tr("The 'From' value cannot be greater than the 'To' value."), + QPrintDialog::tr("OK")); + } + } while (!done); + + QApplicationPrivate::leaveModal(&modal_widget); + + qt_win_eatMouseMove(); + + // write values back... + if (result) { + QT_WA({ + PRINTDLGW *pdw = reinterpret_cast<PRINTDLGW *>(pd); + qt_win_read_back_PRINTDLG(pdw, q, this); + qt_win_clean_up_PRINTDLG(&pdw); + }, { + PRINTDLGA *pda = reinterpret_cast<PRINTDLGA *>(pd); + qt_win_read_back_PRINTDLG(pda, q, this); + qt_win_clean_up_PRINTDLG(&pda); + }); + // update printer validity + printer->d_func()->validPrinter = !ep->name.isEmpty(); + } + + // Cleanup... + GlobalFree(tempDevNames); + + q->done(result); + + return result; +} + +void QPrintDialog::setVisible(bool visible) +{ + Q_D(QPrintDialog); + + // its always modal, so we cannot hide a native print dialog + if (!visible) + return; + + if (!warnIfNotNative(d->printer)) + return; + + (void)d->openWindowsPrintDialogModally(); + return; +} + +QT_END_NAMESPACE + +#include "moc_qprintdialog.cpp" + +#endif // QT_NO_PRINTDIALOG diff --git a/src/gui/dialogs/qprintpreviewdialog.cpp b/src/gui/dialogs/qprintpreviewdialog.cpp new file mode 100644 index 0000000000..c00bd14339 --- /dev/null +++ b/src/gui/dialogs/qprintpreviewdialog.cpp @@ -0,0 +1,793 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qprintpreviewdialog.h" +#include "qprintpreviewwidget.h" +#include <private/qprinter_p.h> + +#include <QtGui/qaction.h> +#include <QtGui/qboxlayout.h> +#include <QtGui/qcombobox.h> +#include <QtGui/qlabel.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qpagesetupdialog.h> +#include <QtGui/qprinter.h> +#include <QtGui/qstyle.h> +#include <QtGui/qtoolbutton.h> +#include <QtGui/qvalidator.h> +#include <QtGui/qfiledialog.h> +#include <QtCore/QCoreApplication> + +#include <math.h> + +#ifndef QT_NO_PRINTPREVIEWDIALOG + +QT_BEGIN_NAMESPACE + +namespace { +class ZoomFactorValidator : public QDoubleValidator +{ +public: + ZoomFactorValidator(QObject* parent) + : QDoubleValidator(parent) {} + ZoomFactorValidator(qreal bottom, qreal top, int decimals, QObject *parent) + : QDoubleValidator(bottom, top, decimals, parent) {} + + State validate(QString &input, int &pos) const + { + bool replacePercent = false; + if (input.endsWith(QLatin1Char('%'))) { + input = input.left(input.length() - 1); + replacePercent = true; + } + State state = QDoubleValidator::validate(input, pos); + if (replacePercent) + input += QLatin1Char('%'); + const int num_size = 4; + if (state == Intermediate) { + int i = input.indexOf(QLocale::system().decimalPoint()); + if ((i == -1 && input.size() > num_size) + || (i != -1 && i > num_size)) + return Invalid; + } + return state; + } +}; + +class LineEdit : public QLineEdit +{ + Q_OBJECT +public: + LineEdit(QWidget* parent = 0) + : QLineEdit(parent) + { + setContextMenuPolicy(Qt::NoContextMenu); + connect(this, SIGNAL(returnPressed()), SLOT(handleReturnPressed())); + } + +protected: + void focusInEvent(QFocusEvent *e) + { + origText = text(); + QLineEdit::focusInEvent(e); + } + + void focusOutEvent(QFocusEvent *e) + { + if (isModified() && !hasAcceptableInput()) + setText(origText); + QLineEdit::focusOutEvent(e); + } + +private slots: + void handleReturnPressed() + { + origText = text(); + } + +private: + QString origText; +}; +} // anonymous namespace + +class QPrintPreviewDialogPrivate +{ + Q_DECLARE_PUBLIC(QPrintPreviewDialog) +public: + QPrintPreviewDialogPrivate(QPrintPreviewDialog *q) + : q_ptr(q), printDialog(0), ownPrinter(false), + initialized(false) {} + + // private slots + void _q_fit(QAction *action); + void _q_zoomIn(); + void _q_zoomOut(); + void _q_navigate(QAction *action); + void _q_setMode(QAction *action); + void _q_pageNumEdited(); + void _q_print(); + void _q_pageSetup(); + void _q_previewChanged(); + void _q_zoomFactorChanged(); + + void init(QPrinter *printer = 0); + void populateScene(); + void layoutPages(); + void setupActions(); + void updateNavActions(); + void setFitting(bool on); + bool isFitting(); + void updatePageNumLabel(); + void updateZoomFactor(); + + QPrintPreviewDialog *q_ptr; + QPrintDialog *printDialog; + QPrintPreviewWidget *preview; + QPrinter *printer; + bool ownPrinter; + bool initialized; + + // widgets: + QLineEdit *pageNumEdit; + QLabel *pageNumLabel; + QComboBox *zoomFactor; + + // actions: + QActionGroup* navGroup; + QAction *nextPageAction; + QAction *prevPageAction; + QAction *firstPageAction; + QAction *lastPageAction; + + QActionGroup* fitGroup; + QAction *fitWidthAction; + QAction *fitPageAction; + + QActionGroup* zoomGroup; + QAction *zoomInAction; + QAction *zoomOutAction; + + QActionGroup* orientationGroup; + QAction *portraitAction; + QAction *landscapeAction; + + QActionGroup* modeGroup; + QAction *singleModeAction; + QAction *facingModeAction; + QAction *overviewModeAction; + + QActionGroup *printerGroup; + QAction *printAction; + QAction *pageSetupAction; + QAction *closeAction; + + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; +}; + +void QPrintPreviewDialogPrivate::init(QPrinter *_printer) +{ + Q_Q(QPrintPreviewDialog); + + if (_printer) { + preview = new QPrintPreviewWidget(_printer, q); + printer = _printer; + } else { + ownPrinter = true; + printer = new QPrinter; + preview = new QPrintPreviewWidget(printer, q); + } + QObject::connect(preview, SIGNAL(paintRequested(QPrinter *)), q, SIGNAL(paintRequested(QPrinter *))); + QObject::connect(preview, SIGNAL(previewChanged()), q, SLOT(_q_previewChanged())); + setupActions(); + + // Navigation + QToolButton* nextPageButton = new QToolButton; + nextPageButton->setDefaultAction(nextPageAction); + QToolButton* prevPageButton = new QToolButton; + prevPageButton->setDefaultAction(prevPageAction); + QToolButton* firstPageButton = new QToolButton; + firstPageButton->setDefaultAction(firstPageAction); + QToolButton* lastPageButton = new QToolButton; + lastPageButton->setDefaultAction(lastPageAction); + + pageNumEdit = new LineEdit; + pageNumEdit->setAlignment(Qt::AlignRight); + pageNumEdit->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); + pageNumLabel = new QLabel; + QObject::connect(pageNumEdit, SIGNAL(editingFinished()), q, SLOT(_q_pageNumEdited())); + + QToolButton* fitWidthButton = new QToolButton; + fitWidthButton->setDefaultAction(fitWidthAction); + QToolButton* fitPageButton = new QToolButton; + fitPageButton->setDefaultAction(fitPageAction); + + zoomFactor = new QComboBox; + zoomFactor->setEditable(true); + zoomFactor->setMinimumContentsLength(7); + zoomFactor->setInsertPolicy(QComboBox::NoInsert); + LineEdit *zoomEditor = new LineEdit; + zoomEditor->setValidator(new ZoomFactorValidator(1, 1000, 1, zoomEditor)); + zoomFactor->setLineEdit(zoomEditor); + static const short factorsX2[] = { 25, 50, 100, 200, 250, 300, 400, 800, 1600 }; + for (int i = 0; i < int(sizeof(factorsX2) / sizeof(factorsX2[0])); ++i) + zoomFactor->addItem(QPrintPreviewDialog::tr("%1%").arg(factorsX2[i] / 2.0)); + QObject::connect(zoomFactor->lineEdit(), SIGNAL(editingFinished()), + q, SLOT(_q_zoomFactorChanged())); + QObject::connect(zoomFactor, SIGNAL(currentIndexChanged(int)), + q, SLOT(_q_zoomFactorChanged())); + + QToolButton* zoomInButton = new QToolButton; + zoomInButton->setDefaultAction(zoomInAction); + zoomInButton->setAutoRepeat(true); + zoomInButton->setAutoRepeatInterval(200); + zoomInButton->setAutoRepeatDelay(200); + + QToolButton* zoomOutButton = new QToolButton; + zoomOutButton->setDefaultAction(zoomOutAction); + zoomOutButton->setAutoRepeat(true); + zoomOutButton->setAutoRepeatInterval(200); + zoomOutButton->setAutoRepeatDelay(200); + + //Cannot use the actions' triggered signal here, since it doesnt autorepeat + QObject::connect(zoomInButton, SIGNAL(clicked()), q, SLOT(_q_zoomIn())); + QObject::connect(zoomOutButton, SIGNAL(clicked()), q, SLOT(_q_zoomOut())); + + QToolButton* portraitButton = new QToolButton; + portraitButton->setDefaultAction(portraitAction); + QToolButton* landscapeButton = new QToolButton; + landscapeButton->setDefaultAction(landscapeAction); + + QToolButton* singleModeButton = new QToolButton; + singleModeButton->setDefaultAction(singleModeAction); + QToolButton* facingModeButton = new QToolButton; + facingModeButton->setDefaultAction(facingModeAction); + QToolButton* overviewModeButton = new QToolButton; + overviewModeButton->setDefaultAction(overviewModeAction); + + QToolButton *printButton = new QToolButton; + printButton->setDefaultAction(printAction); + QToolButton *pageSetupButton = new QToolButton; + pageSetupButton->setDefaultAction(pageSetupAction); + QToolButton *closeButton = new QToolButton; + closeButton->setDefaultAction(closeAction); + + QHBoxLayout* modeLayout = new QHBoxLayout; + modeLayout->setSpacing(0); + modeLayout->addWidget(singleModeButton); + modeLayout->addWidget(facingModeButton); + modeLayout->addWidget(overviewModeButton); + + QHBoxLayout *barLayout = new QHBoxLayout; + barLayout->addWidget(fitWidthButton); + barLayout->addWidget(fitPageButton); + barLayout->addWidget(zoomFactor); + barLayout->addWidget(zoomOutButton); + barLayout->addWidget(zoomInButton); + barLayout->addWidget(portraitButton); + barLayout->addWidget(landscapeButton); + barLayout->addStretch(); + barLayout->addWidget(firstPageButton); + barLayout->addWidget(prevPageButton); + barLayout->addWidget(pageNumEdit); + barLayout->addWidget(pageNumLabel); + barLayout->addWidget(nextPageButton); + barLayout->addWidget(lastPageButton); + barLayout->addStretch(); + barLayout->addLayout(modeLayout); + barLayout->addStretch(); + barLayout->addWidget(pageSetupButton); + barLayout->addWidget(printButton); + barLayout->addWidget(closeButton); + + QWidget* buttonBar = new QWidget; + buttonBar->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); + barLayout->setMargin(0); + buttonBar->setLayout(barLayout); + + QVBoxLayout *topLayout = new QVBoxLayout; + topLayout->addWidget(buttonBar); + topLayout->addWidget(preview); + q->setLayout(topLayout); + + QString caption = QCoreApplication::translate("QPrintPreviewDialog", "Print Preview"); + if (!printer->docName().isEmpty()) + caption += QString::fromLatin1(": ") + printer->docName(); + q->setWindowTitle(caption); + + if (!printer->isValid() +#if defined(Q_WS_WIN) || defined(Q_WS_MAC) + || printer->outputFormat() != QPrinter::NativeFormat +#endif + ) + pageSetupButton->setEnabled(false); +} + +static inline void qt_setupActionIcon(QAction *action, const QLatin1String &name) +{ + QLatin1String imagePrefix(":/trolltech/dialogs/qprintpreviewdialog/images/"); + QIcon icon; + icon.addFile(imagePrefix + name + QLatin1String("-24.png"), QSize(24, 24)); + icon.addFile(imagePrefix + name + QLatin1String("-32.png"), QSize(32, 32)); + action->setIcon(icon); +} + +void QPrintPreviewDialogPrivate::setupActions() +{ + Q_Q(QPrintPreviewDialog); + + // Navigation + navGroup = new QActionGroup(q); + navGroup->setExclusive(false); + nextPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Next page")); + prevPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Previous page")); + firstPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "First page")); + lastPageAction = navGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Last page")); + qt_setupActionIcon(nextPageAction, QLatin1String("go-next")); + qt_setupActionIcon(prevPageAction, QLatin1String("go-previous")); + qt_setupActionIcon(firstPageAction, QLatin1String("go-first")); + qt_setupActionIcon(lastPageAction, QLatin1String("go-last")); + QObject::connect(navGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_navigate(QAction*))); + + + fitGroup = new QActionGroup(q); + fitWidthAction = fitGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Fit width")); + fitPageAction = fitGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Fit page")); + fitWidthAction->setObjectName(QLatin1String("fitWidthAction")); + fitPageAction->setObjectName(QLatin1String("fitPageAction")); + fitWidthAction->setCheckable(true); + fitPageAction->setCheckable(true); + qt_setupActionIcon(fitWidthAction, QLatin1String("fit-width")); + qt_setupActionIcon(fitPageAction, QLatin1String("fit-page")); + QObject::connect(fitGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_fit(QAction*))); + + // Zoom + zoomGroup = new QActionGroup(q); + zoomInAction = zoomGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Zoom in")); + zoomOutAction = zoomGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Zoom out")); + qt_setupActionIcon(zoomInAction, QLatin1String("zoom-in")); + qt_setupActionIcon(zoomOutAction, QLatin1String("zoom-out")); + + // Portrait/Landscape + orientationGroup = new QActionGroup(q); + portraitAction = orientationGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Portrait")); + landscapeAction = orientationGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Landscape")); + portraitAction->setCheckable(true); + landscapeAction->setCheckable(true); + qt_setupActionIcon(portraitAction, QLatin1String("layout-portrait")); + qt_setupActionIcon(landscapeAction, QLatin1String("layout-landscape")); + QObject::connect(portraitAction, SIGNAL(triggered(bool)), preview, SLOT(setPortraitOrientation())); + QObject::connect(landscapeAction, SIGNAL(triggered(bool)), preview, SLOT(setLandscapeOrientation())); + + // Display mode + modeGroup = new QActionGroup(q); + singleModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show single page")); + facingModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show facing pages")); + overviewModeAction = modeGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Show overview of all pages")); + qt_setupActionIcon(singleModeAction, QLatin1String("view-page-one")); + qt_setupActionIcon(facingModeAction, QLatin1String("view-page-sided")); + qt_setupActionIcon(overviewModeAction, QLatin1String("view-page-multi")); + singleModeAction->setObjectName(QLatin1String("singleModeAction")); + facingModeAction->setObjectName(QLatin1String("facingModeAction")); + overviewModeAction->setObjectName(QLatin1String("overviewModeAction")); + + singleModeAction->setCheckable(true); + facingModeAction->setCheckable(true); + overviewModeAction->setCheckable(true); + QObject::connect(modeGroup, SIGNAL(triggered(QAction*)), q, SLOT(_q_setMode(QAction*))); + + // Print + printerGroup = new QActionGroup(q); + printAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Print")); + pageSetupAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Page setup")); + closeAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Close")); + qt_setupActionIcon(printAction, QLatin1String("print")); + qt_setupActionIcon(pageSetupAction, QLatin1String("page-setup")); + QObject::connect(printAction, SIGNAL(triggered(bool)), q, SLOT(_q_print())); + QObject::connect(pageSetupAction, SIGNAL(triggered(bool)), q, SLOT(_q_pageSetup())); + QObject::connect(closeAction, SIGNAL(triggered(bool)), q, SLOT(reject())); + + // Initial state: + fitPageAction->setChecked(true); + singleModeAction->setChecked(true); + if (preview->orientation() == QPrinter::Portrait) + portraitAction->setChecked(true); + else + landscapeAction->setChecked(true); +} + + +bool QPrintPreviewDialogPrivate::isFitting() +{ + return (fitGroup->isExclusive() + && (fitWidthAction->isChecked() || fitPageAction->isChecked())); +} + + +void QPrintPreviewDialogPrivate::setFitting(bool on) +{ + if (isFitting() == on) + return; + fitGroup->setExclusive(on); + if (on) { + QAction* action = fitWidthAction->isChecked() ? fitWidthAction : fitPageAction; + action->setChecked(true); + if (fitGroup->checkedAction() != action) { + // work around exclusitivity problem + fitGroup->removeAction(action); + fitGroup->addAction(action); + } + } else { + fitWidthAction->setChecked(false); + fitPageAction->setChecked(false); + } +} + +void QPrintPreviewDialogPrivate::updateNavActions() +{ + int curPage = preview->currentPage(); + int numPages = preview->numPages(); + nextPageAction->setEnabled(curPage < numPages); + prevPageAction->setEnabled(curPage > 1); + firstPageAction->setEnabled(curPage > 1); + lastPageAction->setEnabled(curPage < numPages); + pageNumEdit->setText(QString::number(curPage)); +} + +void QPrintPreviewDialogPrivate::updatePageNumLabel() +{ + Q_Q(QPrintPreviewDialog); + + int numPages = preview->numPages(); + int maxChars = QString::number(numPages).length(); + pageNumLabel->setText(QString(QLatin1String("/ %1")).arg(numPages)); + int cyphersWidth = q->fontMetrics().width(QString().fill(QLatin1Char('8'), maxChars)); + int maxWidth = pageNumEdit->minimumSizeHint().width() + cyphersWidth; + pageNumEdit->setMinimumWidth(maxWidth); + pageNumEdit->setMaximumWidth(maxWidth); + pageNumEdit->setValidator(new QIntValidator(1, numPages, pageNumEdit)); + // any old one will be deleted later along with its parent pageNumEdit +} + +void QPrintPreviewDialogPrivate::updateZoomFactor() +{ + zoomFactor->lineEdit()->setText(QString().sprintf("%.1f%%", preview->zoomFactor()*100)); +} + +void QPrintPreviewDialogPrivate::_q_fit(QAction* action) +{ + setFitting(true); + if (action == fitPageAction) + preview->fitInView(); + else + preview->fitToWidth(); +} + +void QPrintPreviewDialogPrivate::_q_zoomIn() +{ + setFitting(false); + preview->zoomIn(); + updateZoomFactor(); +} + +void QPrintPreviewDialogPrivate::_q_zoomOut() +{ + setFitting(false); + preview->zoomOut(); + updateZoomFactor(); +} + +void QPrintPreviewDialogPrivate::_q_pageNumEdited() +{ + bool ok = false; + int res = pageNumEdit->text().toInt(&ok); + if (ok) + preview->setCurrentPage(res); +} + +void QPrintPreviewDialogPrivate::_q_navigate(QAction* action) +{ + int curPage = preview->currentPage(); + if (action == prevPageAction) + preview->setCurrentPage(curPage - 1); + else if (action == nextPageAction) + preview->setCurrentPage(curPage + 1); + else if (action == firstPageAction) + preview->setCurrentPage(1); + else if (action == lastPageAction) + preview->setCurrentPage(preview->numPages()); + updateNavActions(); +} + +void QPrintPreviewDialogPrivate::_q_setMode(QAction* action) +{ + if (action == overviewModeAction) { + preview->setViewMode(QPrintPreviewWidget::AllPagesView); + setFitting(false); + fitGroup->setEnabled(false); + navGroup->setEnabled(false); + pageNumEdit->setEnabled(false); + pageNumLabel->setEnabled(false); + } else if (action == facingModeAction) { + preview->setViewMode(QPrintPreviewWidget::FacingPagesView); + } else { + preview->setViewMode(QPrintPreviewWidget::SinglePageView); + } + if (action == facingModeAction || action == singleModeAction) { + fitGroup->setEnabled(true); + navGroup->setEnabled(true); + pageNumEdit->setEnabled(true); + pageNumLabel->setEnabled(true); + setFitting(true); + } +} + +void QPrintPreviewDialogPrivate::_q_print() +{ + Q_Q(QPrintPreviewDialog); + +#if defined(Q_WS_WIN) || defined(Q_WS_MAC) + if (printer->outputFormat() != QPrinter::NativeFormat) { + QString title; + QString suffix; + if (printer->outputFormat() == QPrinter::PdfFormat) { + title = QCoreApplication::translate("QPrintPreviewDialog", "Export to PDF"); + suffix = QLatin1String(".pdf"); + } else { + title = QCoreApplication::translate("QPrintPreviewDialog", "Export to PostScript"); + suffix = QLatin1String(".ps"); + } + QString fileName = QFileDialog::getSaveFileName(q, title, printer->outputFileName(), + QLatin1String("*") + suffix); + if (!fileName.isEmpty()) { + if (QFileInfo(fileName).suffix().isEmpty()) + fileName.append(suffix); + printer->setOutputFileName(fileName); + } + if (!printer->outputFileName().isEmpty()) + preview->print(); + q->accept(); + return; + } +#endif + + if (!printDialog) + printDialog = new QPrintDialog(printer, q); + if (printDialog->exec() == QDialog::Accepted) { + preview->print(); + q->accept(); + } +} + +void QPrintPreviewDialogPrivate::_q_pageSetup() +{ + Q_Q(QPrintPreviewDialog); + + QPageSetupDialog pageSetup(printer, q); + if (pageSetup.exec() == QDialog::Accepted) { + // update possible orientation changes + if (preview->orientation() == QPrinter::Portrait) { + portraitAction->setChecked(true); + preview->setPortraitOrientation(); + }else { + landscapeAction->setChecked(true); + preview->setLandscapeOrientation(); + } + } +} + +void QPrintPreviewDialogPrivate::_q_previewChanged() +{ + updateNavActions(); + updatePageNumLabel(); + updateZoomFactor(); +} + +void QPrintPreviewDialogPrivate::_q_zoomFactorChanged() +{ + QString text = zoomFactor->lineEdit()->text(); + bool ok; + qreal factor = text.remove(QLatin1Char('%')).toFloat(&ok); + factor = qMax(qreal(1.0), qMin(qreal(1000.0), factor)); + if (ok) { + preview->setZoomFactor(factor/100.0); + zoomFactor->setEditText(QString(QLatin1String("%1%")).arg(factor)); + setFitting(false); + } +} + +/////////////////////////////////////////////////////////////////////////// + +/*! + \class QPrintPreviewDialog + \since 4.4 + + \brief The QPrintPreviewDialog class provides a dialog for + previewing and configuring page layouts for printer output. + + Using QPrintPreviewDialog in your existing application is + straightforward: + + \list 1 + \o Create the QPrintPreviewDialog. + + You can construct a QPrintPreviewDialog with an existing QPrinter + object, or you can have QPrintPreviewDialog create one for you, + which will be the system default printer. + + \o Connect the paintRequested() signal to a slot. + + When the dialog needs to generate a set of preview pages, the + paintRequested() signal will be emitted. You can use the exact + same code for the actual printing as for having the preview + generated, including calling QPrinter::newPage() to start a new + page in the preview. Connect a slot to the paintRequested() + signal, where you draw onto the QPrinter object that is passed + into the slot. + + \o Call exec(). + + Call QPrintPreviewDialog::exec() to show the preview dialog. + \endlist + + + \sa QPrinter, QPrintDialog, QPageSetupDialog, QPrintPreviewWidget +*/ + +/*! + Constructs a QPrintPreviewDialog based on \a printer and with \a + parent as the parent widget. The widget flags \a flags are passed on + to the QWidget constructor. + + \sa QWidget::setWindowFlags() +*/ +QPrintPreviewDialog::QPrintPreviewDialog(QPrinter* printer, QWidget *parent, Qt::WindowFlags flags) + : QDialog(parent, flags), d_ptr(new QPrintPreviewDialogPrivate(this)) +{ + Q_D(QPrintPreviewDialog); + d->init(printer); +} + +/*! + \overload + \fn QPrintPreviewDialog::QPrintPreviewDialog(QWidget *parent, Qt::WindowFlags flags) + + This will create an internal QPrinter object, which will use the + system default printer. +*/ +QPrintPreviewDialog::QPrintPreviewDialog(QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f), d_ptr(new QPrintPreviewDialogPrivate(this)) +{ + Q_D(QPrintPreviewDialog); + d->init(); +} + +/*! + Destroys the QPrintPreviewDialog. +*/ +QPrintPreviewDialog::~QPrintPreviewDialog() +{ + Q_D(QPrintPreviewDialog); + if (d->ownPrinter) + delete d->printer; + delete d->printDialog; + delete d_ptr; +} + +/*! + \reimp +*/ +void QPrintPreviewDialog::setVisible(bool visible) +{ + Q_D(QPrintPreviewDialog); + // this will make the dialog get a decent default size + if (visible && !d->initialized) { + d->preview->updatePreview(); + d->initialized = true; + } + QDialog::setVisible(visible); +} + +/*! + \reimp +*/ +void QPrintPreviewDialog::done(int result) +{ + Q_D(QPrintPreviewDialog); + QDialog::done(result); + if (d->receiverToDisconnectOnClose) { + disconnect(this, SIGNAL(finished(int)), + d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose); + d->receiverToDisconnectOnClose = 0; + } + d->memberToDisconnectOnClose.clear(); +} + +/*! + \overload + \since 4.5 + + Opens the dialog and connects its finished(int) signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QPrintPreviewDialog::open(QObject *receiver, const char *member) +{ + Q_D(QPrintPreviewDialog); + // the int parameter isn't very useful here; we could just as well connect + // to reject(), but this feels less robust somehow + connect(this, SIGNAL(finished(int)), receiver, member); + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +/*! + Returns a pointer to the QPrinter object this dialog is currently + operating on. +*/ +QPrinter *QPrintPreviewDialog::printer() +{ + Q_D(QPrintPreviewDialog); + return d->printer; +} + +/*! + \fn void QPrintPreviewDialog::paintRequested(QPrinter *printer) + + This signal is emitted when the QPrintPreviewDialog needs to generate + a set of preview pages. + + The \a printer instance supplied is the paint device onto which you should + paint the contents of each page, using the QPrinter instance in the same way + as you would when printing directly. +*/ + + +QT_END_NAMESPACE + +#include "moc_qprintpreviewdialog.cpp" +#include "qprintpreviewdialog.moc" + +#endif // QT_NO_PRINTPREVIEWDIALOG + + diff --git a/src/gui/dialogs/qprintpreviewdialog.h b/src/gui/dialogs/qprintpreviewdialog.h new file mode 100644 index 0000000000..c3a4d57867 --- /dev/null +++ b/src/gui/dialogs/qprintpreviewdialog.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPRINTPREVIEWDIALOG_H +#define QPRINTPREVIEWDIALOG_H + +#include <QtGui/qdialog.h> + +#ifndef QT_NO_PRINTPREVIEWDIALOG + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGraphicsView; +class QPrintPreviewDialogPrivate; + +class Q_GUI_EXPORT QPrintPreviewDialog : public QDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPrintPreviewDialog) + +public: + explicit QPrintPreviewDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0); + explicit QPrintPreviewDialog(QPrinter *printer, QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~QPrintPreviewDialog(); + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + + QPrinter *printer(); + + void setVisible(bool visible); + void done(int result); + +Q_SIGNALS: + void paintRequested(QPrinter *printer); + +private: + Q_PRIVATE_SLOT(d_func(), void _q_fit(QAction *action)) + Q_PRIVATE_SLOT(d_func(), void _q_zoomIn()) + Q_PRIVATE_SLOT(d_func(), void _q_zoomOut()) + Q_PRIVATE_SLOT(d_func(), void _q_navigate(QAction *action)) + Q_PRIVATE_SLOT(d_func(), void _q_setMode(QAction *action)) + Q_PRIVATE_SLOT(d_func(), void _q_pageNumEdited()) + Q_PRIVATE_SLOT(d_func(), void _q_print()) + Q_PRIVATE_SLOT(d_func(), void _q_pageSetup()) + Q_PRIVATE_SLOT(d_func(), void _q_previewChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_zoomFactorChanged()) + + QPrintPreviewDialogPrivate *d_ptr; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_PRINTPREVIEWDIALOG + +#endif // QPRINTPREVIEWDIALOG_H diff --git a/src/gui/dialogs/qprintpropertieswidget.ui b/src/gui/dialogs/qprintpropertieswidget.ui new file mode 100644 index 0000000000..26fa09599e --- /dev/null +++ b/src/gui/dialogs/qprintpropertieswidget.ui @@ -0,0 +1,70 @@ +<ui version="4.0" > + <class>QPrintPropertiesWidget</class> + <widget class="QWidget" name="QPrintPropertiesWidget" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>396</width> + <height>288</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4" > + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QTabWidget" name="tabs" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="tabPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>392</width> + <height>261</height> + </rect> + </property> + <attribute name="title" > + <string>Page</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <widget class="QPageSetupWidget" native="1" name="pageSetup" /> + </item> + </layout> + </widget> + <widget class="QWidget" name="cupsPropertiesPage" > + <attribute name="title" > + <string>Advanced</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout_2" > + <item> + <widget class="QTreeView" name="treeView" > + <property name="alternatingRowColors" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QPageSetupWidget</class> + <extends>QWidget</extends> + <header>qpagesetupdialog_unix_p.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/gui/dialogs/qprintsettingsoutput.ui b/src/gui/dialogs/qprintsettingsoutput.ui new file mode 100644 index 0000000000..fc57e863f9 --- /dev/null +++ b/src/gui/dialogs/qprintsettingsoutput.ui @@ -0,0 +1,371 @@ +<ui version="4.0" > + <class>QPrintSettingsOutput</class> + <widget class="QWidget" name="QPrintSettingsOutput" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>416</width> + <height>166</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2" > + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QTabWidget" name="tabs" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="copiesTab" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>412</width> + <height>139</height> + </rect> + </property> + <attribute name="title" > + <string>Copies</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <widget class="QGroupBox" name="gbPrintRange" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>Print range</string> + </property> + <layout class="QVBoxLayout" name="_3" > + <property name="spacing" > + <number>4</number> + </property> + <property name="margin" > + <number>6</number> + </property> + <item> + <widget class="QRadioButton" name="printAll" > + <property name="text" > + <string>Print all</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="_4" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QRadioButton" name="printRange" > + <property name="text" > + <string>Pages from</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="from" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="minimum" > + <number>1</number> + </property> + <property name="maximum" > + <number>999</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>to</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="to" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="minimum" > + <number>1</number> + </property> + <property name="maximum" > + <number>999</number> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QRadioButton" name="printSelection" > + <property name="text" > + <string>Selection</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>1</width> + <height>1</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Output Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Copies:</string> + </property> + <property name="buddy" > + <cstring>copies</cstring> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2" > + <widget class="QSpinBox" name="copies" > + <property name="minimum" > + <number>1</number> + </property> + <property name="maximum" > + <number>999</number> + </property> + </widget> + </item> + <item row="0" column="3" > + <spacer name="horizontalSpacer" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>91</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QCheckBox" name="collate" > + <property name="text" > + <string>Collate</string> + </property> + </widget> + </item> + <item rowspan="2" row="1" column="2" colspan="2" > + <widget class="QLabel" name="outputIcon" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Ignored" hsizetype="Ignored" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QCheckBox" name="reverse" > + <property name="text" > + <string>Reverse</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="4" > + <spacer name="verticalSpacer_2" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>1</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="optionsTab" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>412</width> + <height>139</height> + </rect> + </property> + <attribute name="title" > + <string>Options</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_2" > + <item row="0" column="1" > + <widget class="QGroupBox" name="colorMode" > + <property name="title" > + <string>Color Mode</string> + </property> + <layout class="QGridLayout" name="gridLayout_4" > + <item row="2" column="0" > + <spacer name="verticalSpacer_6" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>1</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0" > + <widget class="QRadioButton" name="color" > + <property name="text" > + <string>Color</string> + </property> + </widget> + </item> + <item rowspan="3" row="0" column="1" > + <widget class="QLabel" name="colorIcon" /> + </item> + <item row="1" column="0" > + <widget class="QRadioButton" name="grayscale" > + <property name="text" > + <string>Grayscale</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="0" > + <widget class="QGroupBox" name="duplex" > + <property name="title" > + <string>Duplex Printing</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QRadioButton" name="noDuplex" > + <property name="text" > + <string>None</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="duplexLong" > + <property name="text" > + <string>Long side</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="duplexShort" > + <property name="text" > + <string>Short side</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_42" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>1</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>printRange</sender> + <signal>toggled(bool)</signal> + <receiver>from</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>76</x> + <y>59</y> + </hint> + <hint type="destinationlabel" > + <x>122</x> + <y>57</y> + </hint> + </hints> + </connection> + <connection> + <sender>printRange</sender> + <signal>toggled(bool)</signal> + <receiver>to</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>69</x> + <y>67</y> + </hint> + <hint type="destinationlabel" > + <x>215</x> + <y>67</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/gui/dialogs/qprintwidget.ui b/src/gui/dialogs/qprintwidget.ui new file mode 100644 index 0000000000..8a4f3bde0a --- /dev/null +++ b/src/gui/dialogs/qprintwidget.ui @@ -0,0 +1,116 @@ +<ui version="4.0" > + <class>QPrintWidget</class> + <widget class="QWidget" name="QPrintWidget" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>443</width> + <height>175</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2" > + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="printerGroup" > + <property name="title" > + <string>Printer</string> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>&Name:</string> + </property> + <property name="buddy" > + <cstring>printers</cstring> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QComboBox" name="printers" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>3</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QPushButton" name="properties" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Minimum" > + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>P&roperties</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Location:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLabel" name="location" /> + </item> + <item row="1" column="2" > + <widget class="QCheckBox" name="preview" > + <property name="text" > + <string>Preview</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Type:</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="type" /> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="lOutput" > + <property name="text" > + <string>Output &file:</string> + </property> + <property name="buddy" > + <cstring>filename</cstring> + </property> + </widget> + </item> + <item row="3" column="1" colspan="2" > + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <widget class="QLineEdit" name="filename" /> + </item> + <item> + <widget class="QToolButton" name="fileBrowser" > + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/gui/dialogs/qprogressdialog.cpp b/src/gui/dialogs/qprogressdialog.cpp new file mode 100644 index 0000000000..66a1285003 --- /dev/null +++ b/src/gui/dialogs/qprogressdialog.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qprogressdialog.h" + +#ifndef QT_NO_PROGRESSDIALOG + +#include "qshortcut.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qdatetime.h" +#include "qlabel.h" +#include "qprogressbar.h" +#include "qapplication.h" +#include "qstyle.h" +#include "qpushbutton.h" +#include "qcursor.h" +#include "qtimer.h" +#include <private/qdialog_p.h> +#include <limits.h> + +QT_BEGIN_NAMESPACE + +// If the operation is expected to take this long (as predicted by +// progress time), show the progress dialog. +static const int defaultShowTime = 4000; +// Wait at least this long before attempting to make a prediction. +static const int minWaitTime = 50; + +class QProgressDialogPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QProgressDialog) + +public: + QProgressDialogPrivate() : label(0), cancel(0), bar(0), + shown_once(false), + cancellation_flag(false), + showTime(defaultShowTime), +#ifndef QT_NO_SHORTCUT + escapeShortcut(0), +#endif + useDefaultCancelText(false) + { + } + + void init(const QString &labelText, const QString &cancelText, int min, int max); + void layout(); + void retranslateStrings(); + void _q_disconnectOnClose(); + + QLabel *label; + QPushButton *cancel; + QProgressBar *bar; + QTimer *forceTimer; + bool shown_once; + bool cancellation_flag; + QTime starttime; +#ifndef QT_NO_CURSOR + QCursor parentCursor; +#endif + int showTime; + bool autoClose; + bool autoReset; + bool forceHide; +#ifndef QT_NO_SHORTCUT + QShortcut *escapeShortcut; +#endif + bool useDefaultCancelText; + QPointer<QObject> receiverToDisconnectOnClose; + QByteArray memberToDisconnectOnClose; +}; + +void QProgressDialogPrivate::init(const QString &labelText, const QString &cancelText, + int min, int max) +{ + Q_Q(QProgressDialog); + label = new QLabel(labelText, q); + int align = q->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, q); + label->setAlignment(Qt::Alignment(align)); + bar = new QProgressBar(q); + bar->setRange(min, max); + autoClose = true; + autoReset = true; + forceHide = false; + QObject::connect(q, SIGNAL(canceled()), q, SLOT(cancel())); + forceTimer = new QTimer(q); + QObject::connect(forceTimer, SIGNAL(timeout()), q, SLOT(forceShow())); + if (useDefaultCancelText) { + retranslateStrings(); + } else { + q->setCancelButtonText(cancelText); + } +} + +void QProgressDialogPrivate::layout() +{ + Q_Q(QProgressDialog); + int sp = q->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); + int mtb = q->style()->pixelMetric(QStyle::PM_DefaultTopLevelMargin); + int mlr = qMin(q->width() / 10, mtb); + const bool centered = + bool(q->style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, q)); + + QSize cs = cancel ? cancel->sizeHint() : QSize(0,0); + QSize bh = bar->sizeHint(); + int cspc; + int lh = 0; + + // Find spacing and sizes that fit. It is important that a progress + // dialog can be made very small if the user demands it so. + for (int attempt=5; attempt--;) { + cspc = cancel ? cs.height() + sp : 0; + lh = qMax(0, q->height() - mtb - bh.height() - sp - cspc); + + if (lh < q->height()/4) { + // Getting cramped + sp /= 2; + mtb /= 2; + if (cancel) { + cs.setHeight(qMax(4,cs.height()-sp-2)); + } + bh.setHeight(qMax(4,bh.height()-sp-1)); + } else { + break; + } + } + + if (cancel) { + cancel->setGeometry( + centered ? q->width()/2 - cs.width()/2 : q->width() - mlr - cs.width(), + q->height() - mtb - cs.height(), + cs.width(), cs.height()); + } + + if (label) + label->setGeometry(mlr, 0, q->width()-mlr*2, lh); + bar->setGeometry(mlr, lh+sp, q->width()-mlr*2, bh.height()); +} + +void QProgressDialogPrivate::retranslateStrings() +{ + Q_Q(QProgressDialog); + if (useDefaultCancelText) + q->setCancelButtonText(QProgressDialog::tr("Cancel")); +} + +void QProgressDialogPrivate::_q_disconnectOnClose() +{ + Q_Q(QProgressDialog); + if (receiverToDisconnectOnClose) { + QObject::disconnect(q, SIGNAL(canceled()), receiverToDisconnectOnClose, + memberToDisconnectOnClose); + receiverToDisconnectOnClose = 0; + } + memberToDisconnectOnClose.clear(); +} + +/*! + \class QProgressDialog + \brief The QProgressDialog class provides feedback on the progress of a slow operation. + \ingroup dialogs + \mainclass + + A progress dialog is used to give the user an indication of how long + an operation is going to take, and to demonstrate that the + application has not frozen. It can also give the user an opportunity + to abort the operation. + + A common problem with progress dialogs is that it is difficult to know + when to use them; operations take different amounts of time on different + hardware. QProgressDialog offers a solution to this problem: + it estimates the time the operation will take (based on time for + steps), and only shows itself if that estimate is beyond minimumDuration() + (4 seconds by default). + + Use setMinimum() and setMaximum() or the constructor to set the number of + "steps" in the operation and call setValue() as the operation + progresses. The number of steps can be chosen arbitrarily. It can be the + number of files copied, the number of bytes received, the number of + iterations through the main loop of your algorithm, or some other + suitable unit. Progress starts at the value set by setMinimum(), + and the progress dialog shows that the operation has finished when + you call setValue() with the value set by setMaximum() as its argument. + + The dialog automatically resets and hides itself at the end of the + operation. Use setAutoReset() and setAutoClose() to change this + behavior. Note that if you set a new maximum (using setMaximum() or + setRange()) that equals your current value(), the dialog will not + close regardless. + + There are two ways of using QProgressDialog: modal and modeless. + + Compared to a modeless QProgressDialog, a modal QProgressDialog is simpler + to use for the programmer. Do the operation in a loop, call \l setValue() at + intervals, and check for cancellation with wasCanceled(). For example: + + \snippet doc/src/snippets/dialogs/dialogs.cpp 3 + + A modeless progress dialog is suitable for operations that take + place in the background, where the user is able to interact with the + application. Such operations are typically based on QTimer (or + QObject::timerEvent()), QSocketNotifier, or QUrlOperator; or performed + in a separate thread. A QProgressBar in the status bar of your main window + is often an alternative to a modeless progress dialog. + + You need to have an event loop to be running, connect the + canceled() signal to a slot that stops the operation, and call \l + setValue() at intervals. For example: + + \snippet doc/src/snippets/dialogs/dialogs.cpp 4 + \codeline + \snippet doc/src/snippets/dialogs/dialogs.cpp 5 + \codeline + \snippet doc/src/snippets/dialogs/dialogs.cpp 6 + + In both modes the progress dialog may be customized by + replacing the child widgets with custom widgets by using setLabel(), + setBar(), and setCancelButton(). + The functions setLabelText() and setCancelButtonText() + set the texts shown. + + \image plastique-progressdialog.png A progress dialog shown in the Plastique widget style. + + \sa QDialog, QProgressBar, {fowler}{GUI Design Handbook: Progress Indicator}, + {Find Files Example}, {Pixelator Example} +*/ + + +/*! + Constructs a progress dialog. + + Default settings: + \list + \i The label text is empty. + \i The cancel button text is (translated) "Cancel". + \i minimum is 0; + \i maximum is 100 + \endlist + + The \a parent argument is dialog's parent widget. The widget flags, \a f, are + passed to the QDialog::QDialog() constructor. + + \sa setLabelText(), setCancelButtonText(), setCancelButton(), + setMinimum(), setMaximum() +*/ + +QProgressDialog::QProgressDialog(QWidget *parent, Qt::WindowFlags f) + : QDialog(*(new QProgressDialogPrivate), parent, f) +{ + Q_D(QProgressDialog); + d->useDefaultCancelText = true; + d->init(QString::fromLatin1(""), QString(), 0, 100); +} + +/*! + Constructs a progress dialog. + + The \a labelText is the text used to remind the user what is progressing. + + The \a cancelButtonText is the text to display on the cancel button. If + QString() is passed then no cancel button is shown. + + The \a minimum and \a maximum is the number of steps in the operation for + which this progress dialog shows progress. For example, if the + operation is to examine 50 files, this value minimum value would be 0, + and the maximum would be 50. Before examining the first file, call + setValue(0). As each file is processed call setValue(1), setValue(2), + etc., finally calling setValue(50) after examining the last file. + + The \a parent argument is the dialog's parent widget. The parent, \a parent, and + widget flags, \a f, are passed to the QDialog::QDialog() constructor. + + \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(), + setMinimum(), setMaximum() +*/ + +QProgressDialog::QProgressDialog(const QString &labelText, + const QString &cancelButtonText, + int minimum, int maximum, + QWidget *parent, Qt::WindowFlags f) + : QDialog(*(new QProgressDialogPrivate), parent, f) +{ + Q_D(QProgressDialog); + d->init(labelText, cancelButtonText, minimum, maximum); +} + + +/*! + Destroys the progress dialog. +*/ + +QProgressDialog::~QProgressDialog() +{ +} + +/*! + \fn void QProgressDialog::canceled() + + This signal is emitted when the cancel button is clicked. + It is connected to the cancel() slot by default. + + \sa wasCanceled() +*/ + + +/*! + Sets the label to \a label. The progress dialog resizes to fit. The + label becomes owned by the progress dialog and will be deleted when + necessary, so do not pass the address of an object on the stack. + + \sa setLabelText() +*/ + +void QProgressDialog::setLabel(QLabel *label) +{ + Q_D(QProgressDialog); + delete d->label; + d->label = label; + if (label) { + if (label->parentWidget() == this) { + label->hide(); // until we resize + } else { + label->setParent(this, 0); + } + } + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); + if (label) + label->show(); +} + + +/*! + \property QProgressDialog::labelText + \brief the label's text + + The default text is an empty string. +*/ + +QString QProgressDialog::labelText() const +{ + Q_D(const QProgressDialog); + if (d->label) + return d->label->text(); + return QString(); +} + +void QProgressDialog::setLabelText(const QString &text) +{ + Q_D(QProgressDialog); + if (d->label) { + d->label->setText(text); + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); + } +} + + +/*! + Sets the cancel button to the push button, \a cancelButton. The + progress dialog takes ownership of this button which will be deleted + when necessary, so do not pass the address of an object that is on + the stack, i.e. use new() to create the button. If 0 is passed then + no cancel button will be shown. + + \sa setCancelButtonText() +*/ + +void QProgressDialog::setCancelButton(QPushButton *cancelButton) +{ + Q_D(QProgressDialog); + delete d->cancel; + d->cancel = cancelButton; + if (cancelButton) { + if (cancelButton->parentWidget() == this) { + cancelButton->hide(); // until we resize + } else { + cancelButton->setParent(this, 0); + } + connect(d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled())); +#ifndef QT_NO_SHORTCUT + d->escapeShortcut = new QShortcut(Qt::Key_Escape, this, SIGNAL(canceled())); +#endif + } else { +#ifndef QT_NO_SHORTCUT + delete d->escapeShortcut; + d->escapeShortcut = 0; +#endif + } + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); + if (cancelButton) + cancelButton->show(); +} + +/*! + Sets the cancel button's text to \a cancelButtonText. If the text + is set to QString() then it will cause the cancel button to be + hidden and deleted. + + \sa setCancelButton() +*/ + +void QProgressDialog::setCancelButtonText(const QString &cancelButtonText) +{ + Q_D(QProgressDialog); + d->useDefaultCancelText = false; + + if (!cancelButtonText.isNull()) { + if (d->cancel) + d->cancel->setText(cancelButtonText); + else + setCancelButton(new QPushButton(cancelButtonText, this)); + } else { + setCancelButton(0); + } + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); +} + + +/*! + Sets the progress bar widget to \a bar. The progress dialog resizes to + fit. The progress dialog takes ownership of the progress \a bar which + will be deleted when necessary, so do not use a progress bar + allocated on the stack. +*/ + +void QProgressDialog::setBar(QProgressBar *bar) +{ + Q_D(QProgressDialog); + if (!bar) { + qWarning("QProgressDialog::setBar: Cannot set a null progress bar"); + return; + } +#ifndef QT_NO_DEBUG + if (value() > 0) + qWarning("QProgressDialog::setBar: Cannot set a new progress bar " + "while the old one is active"); +#endif + delete d->bar; + d->bar = bar; + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); +} + + +/*! + \property QProgressDialog::wasCanceled + \brief whether the dialog was canceled +*/ + +bool QProgressDialog::wasCanceled() const +{ + Q_D(const QProgressDialog); + return d->cancellation_flag; +} + + +/*! + \property QProgressDialog::maximum + \brief the highest value represented by the progress bar + + The default is 0. + + \sa minimum, setRange() +*/ + +int QProgressDialog::maximum() const +{ + Q_D(const QProgressDialog); + return d->bar->maximum(); +} + +void QProgressDialog::setMaximum(int maximum) +{ + Q_D(QProgressDialog); + d->bar->setMaximum(maximum); +} + +/*! + \property QProgressDialog::minimum + \brief the lowest value represented by the progress bar + + The default is 0. + + \sa maximum, setRange() +*/ + +int QProgressDialog::minimum() const +{ + Q_D(const QProgressDialog); + return d->bar->minimum(); +} + +void QProgressDialog::setMinimum(int minimum) +{ + Q_D(QProgressDialog); + d->bar->setMinimum(minimum); +} + +/*! + Sets the progress dialog's minimum and maximum values + to \a minimum and \a maximum, respectively. + + If \a maximum is smaller than \a minimum, \a minimum becomes the only + legal value. + + If the current value falls outside the new range, the progress + dialog is reset with reset(). + + \sa minimum, maximum +*/ +void QProgressDialog::setRange(int minimum, int maximum) +{ + Q_D(QProgressDialog); + d->bar->setRange(minimum, maximum); +} + + +/*! + Resets the progress dialog. + The progress dialog becomes hidden if autoClose() is true. + + \sa setAutoClose(), setAutoReset() +*/ + +void QProgressDialog::reset() +{ + Q_D(QProgressDialog); +#ifndef QT_NO_CURSOR + if (value() >= 0) { + if (parentWidget()) + parentWidget()->setCursor(d->parentCursor); + } +#endif + if (d->autoClose || d->forceHide) + hide(); + d->bar->reset(); + d->cancellation_flag = false; + d->shown_once = false; + d->forceTimer->stop(); + + /* + I wish we could disconnect the user slot provided to open() here but + unfortunately reset() is usually called before the slot has been invoked. + (reset() is itself invoked when canceled() is emitted.) + */ + if (d->receiverToDisconnectOnClose) + QMetaObject::invokeMethod(this, "_q_disconnectOnClose", Qt::QueuedConnection); +} + +/*! + Resets the progress dialog. wasCanceled() becomes true until + the progress dialog is reset. + The progress dialog becomes hidden. +*/ + +void QProgressDialog::cancel() +{ + Q_D(QProgressDialog); + d->forceHide = true; + reset(); + d->forceHide = false; + d->cancellation_flag = true; +} + + +int QProgressDialog::value() const +{ + Q_D(const QProgressDialog); + return d->bar->value(); +} + +/*! + \property QProgressDialog::value + \brief the current amount of progress made. + + For the progress dialog to work as expected, you should initially set + this property to 0 and finally set it to + QProgressDialog::maximum(); you can call setValue() any number of times + in-between. + + \warning If the progress dialog is modal + (see QProgressDialog::QProgressDialog()), + this function calls QApplication::processEvents(), so take care that + this does not cause undesirable re-entrancy in your code. For example, + don't use a QProgressDialog inside a paintEvent()! + + \sa minimum, maximum +*/ +void QProgressDialog::setValue(int progress) +{ + Q_D(QProgressDialog); + if (progress == d->bar->value() + || (d->bar->value() == -1 && progress == d->bar->maximum())) + return; + + d->bar->setValue(progress); + + if (d->shown_once) { + if (isModal()) + qApp->processEvents(); + } else { + if (progress == 0) { + d->starttime.start(); + d->forceTimer->start(d->showTime); + return; + } else { + bool need_show; + int elapsed = d->starttime.elapsed(); + if (elapsed >= d->showTime) { + need_show = true; + } else { + if (elapsed > minWaitTime) { + int estimate; + int totalSteps = maximum() - minimum(); + int myprogress = progress - minimum(); + if ((totalSteps - myprogress) >= INT_MAX / elapsed) + estimate = (totalSteps - myprogress) / myprogress * elapsed; + else + estimate = elapsed * (totalSteps - myprogress) / myprogress; + need_show = estimate >= d->showTime; + } else { + need_show = false; + } + } + if (need_show) { + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); + show(); + d->shown_once = true; + } + } +#ifdef Q_WS_MAC + QApplication::flush(); +#endif + } + + if (progress == d->bar->maximum() && d->autoReset) + reset(); +} + +/*! + Returns a size that fits the contents of the progress dialog. + The progress dialog resizes itself as required, so you should not + need to call this yourself. +*/ + +QSize QProgressDialog::sizeHint() const +{ + Q_D(const QProgressDialog); + QSize sh = d->label ? d->label->sizeHint() : QSize(0, 0); + QSize bh = d->bar->sizeHint(); + int margin = style()->pixelMetric(QStyle::PM_DefaultTopLevelMargin); + int spacing = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); + int h = margin * 2 + bh.height() + sh.height() + spacing; + if (d->cancel) + h += d->cancel->sizeHint().height() + spacing; + return QSize(qMax(200, sh.width() + 2 * margin), h); +} + +/*!\reimp +*/ +void QProgressDialog::resizeEvent(QResizeEvent *) +{ + Q_D(QProgressDialog); + d->layout(); +} + +/*! + \reimp +*/ +void QProgressDialog::changeEvent(QEvent *ev) +{ + Q_D(QProgressDialog); + if (ev->type() == QEvent::StyleChange) + d->layout(); + else if (ev->type() == QEvent::LanguageChange) + d->retranslateStrings(); + QDialog::changeEvent(ev); +} + +/*! + \property QProgressDialog::minimumDuration + \brief the time that must pass before the dialog appears + + If the expected duration of the task is less than the + minimumDuration, the dialog will not appear at all. This prevents + the dialog popping up for tasks that are quickly over. For tasks + that are expected to exceed the minimumDuration, the dialog will + pop up after the minimumDuration time or as soon as any progress + is set. + + If set to 0, the dialog is always shown as soon as any progress is + set. The default is 4000 milliseconds. +*/ +void QProgressDialog::setMinimumDuration(int ms) +{ + Q_D(QProgressDialog); + d->showTime = ms; + if (d->bar->value() == 0) { + d->forceTimer->stop(); + d->forceTimer->start(ms); + } +} + +int QProgressDialog::minimumDuration() const +{ + Q_D(const QProgressDialog); + return d->showTime; +} + + +/*! + \reimp +*/ + +void QProgressDialog::closeEvent(QCloseEvent *e) +{ + emit canceled(); + QDialog::closeEvent(e); +} + +/*! + \property QProgressDialog::autoReset + \brief whether the progress dialog calls reset() as soon as value() equals maximum() + + The default is true. + + \sa setAutoClose() +*/ + +void QProgressDialog::setAutoReset(bool b) +{ + Q_D(QProgressDialog); + d->autoReset = b; +} + +bool QProgressDialog::autoReset() const +{ + Q_D(const QProgressDialog); + return d->autoReset; +} + +/*! + \property QProgressDialog::autoClose + \brief whether the dialog gets hidden by reset() + + The default is true. + + \sa setAutoReset() +*/ + +void QProgressDialog::setAutoClose(bool close) +{ + Q_D(QProgressDialog); + d->autoClose = close; +} + +bool QProgressDialog::autoClose() const +{ + Q_D(const QProgressDialog); + return d->autoClose; +} + +/*! + \reimp +*/ + +void QProgressDialog::showEvent(QShowEvent *e) +{ + Q_D(QProgressDialog); + QDialog::showEvent(e); + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); + d->forceTimer->stop(); +} + +/*! + Shows the dialog if it is still hidden after the algorithm has been started + and minimumDuration milliseconds have passed. + + \sa setMinimumDuration() +*/ + +void QProgressDialog::forceShow() +{ + Q_D(QProgressDialog); + d->forceTimer->stop(); + if (d->shown_once || d->cancellation_flag) + return; + + show(); + d->shown_once = true; +} + +/*! + \since 4.5 + \overload + + Opens the dialog and connects its accepted() signal to the slot specified + by \a receiver and \a member. + + The signal will be disconnected from the slot when the dialog is closed. +*/ +void QProgressDialog::open(QObject *receiver, const char *member) +{ + Q_D(QProgressDialog); + connect(this, SIGNAL(canceled()), receiver, member); + d->receiverToDisconnectOnClose = receiver; + d->memberToDisconnectOnClose = member; + QDialog::open(); +} + +QT_END_NAMESPACE + +#include "moc_qprogressdialog.cpp" + +#endif // QT_NO_PROGRESSDIALOG diff --git a/src/gui/dialogs/qprogressdialog.h b/src/gui/dialogs/qprogressdialog.h new file mode 100644 index 0000000000..256981c5f1 --- /dev/null +++ b/src/gui/dialogs/qprogressdialog.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPROGRESSDIALOG_H +#define QPROGRESSDIALOG_H + +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_PROGRESSDIALOG + +class QPushButton; +class QLabel; +class QProgressBar; +class QTimer; +class QProgressDialogPrivate; + +class Q_GUI_EXPORT QProgressDialog : public QDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QProgressDialog) + Q_PROPERTY(bool wasCanceled READ wasCanceled) + Q_PROPERTY(int minimum READ minimum WRITE setMinimum) + Q_PROPERTY(int maximum READ maximum WRITE setMaximum) + Q_PROPERTY(int value READ value WRITE setValue) + Q_PROPERTY(bool autoReset READ autoReset WRITE setAutoReset) + Q_PROPERTY(bool autoClose READ autoClose WRITE setAutoClose) + Q_PROPERTY(int minimumDuration READ minimumDuration WRITE setMinimumDuration) + Q_PROPERTY(QString labelText READ labelText WRITE setLabelText) + +public: + explicit QProgressDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0); + QProgressDialog(const QString &labelText, const QString &cancelButtonText, + int minimum, int maximum, QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~QProgressDialog(); + + void setLabel(QLabel *label); + void setCancelButton(QPushButton *button); + void setBar(QProgressBar *bar); + + bool wasCanceled() const; + + int minimum() const; + int maximum() const; + + int value() const; + + QSize sizeHint() const; + + QString labelText() const; + int minimumDuration() const; + + void setAutoReset(bool reset); + bool autoReset() const; + void setAutoClose(bool close); + bool autoClose() const; + +#ifdef Q_NO_USING_KEYWORD +#ifndef Q_QDOC + void open() { QDialog::open(); } +#endif +#else + using QDialog::open; +#endif + void open(QObject *receiver, const char *member); + +public Q_SLOTS: + void cancel(); + void reset(); + void setMaximum(int maximum); + void setMinimum(int minimum); + void setRange(int minimum, int maximum); + void setValue(int progress); + void setLabelText(const QString &text); + void setCancelButtonText(const QString &text); + void setMinimumDuration(int ms); + +Q_SIGNALS: + void canceled(); + +protected: + void resizeEvent(QResizeEvent *event); + void closeEvent(QCloseEvent *event); + void changeEvent(QEvent *event); + void showEvent(QShowEvent *event); + +protected Q_SLOTS: + void forceShow(); + +private: + Q_DISABLE_COPY(QProgressDialog) + + Q_PRIVATE_SLOT(d_func(), void _q_disconnectOnClose()) +}; + +#endif // QT_NO_PROGRESSDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPROGRESSDIALOG_H diff --git a/src/gui/dialogs/qsidebar.cpp b/src/gui/dialogs/qsidebar.cpp new file mode 100644 index 0000000000..1bd2b7d1e4 --- /dev/null +++ b/src/gui/dialogs/qsidebar.cpp @@ -0,0 +1,485 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsidebar_p.h" +#include "qfilesystemmodel.h" + +#ifndef QT_NO_FILEDIALOG + +#include <qaction.h> +#include <qurl.h> +#include <qmenu.h> +#include <qmimedata.h> +#include <qevent.h> +#include <qdebug.h> +#include <qfileiconprovider.h> +#include <qfiledialog.h> + +QT_BEGIN_NAMESPACE + +/*! + QUrlModel lets you have indexes from a QFileSystemModel to a list. When QFileSystemModel + changes them QUrlModel will automatically update. + + Example usage: File dialog sidebar and combo box + */ +QUrlModel::QUrlModel(QObject *parent) : QStandardItemModel(parent), showFullPath(false), fileSystemModel(0) +{ +} + +/*! + \reimp +*/ +QStringList QUrlModel::mimeTypes() const +{ + return QStringList(QLatin1String("text/uri-list")); +} + +/*! + \reimp +*/ +Qt::ItemFlags QUrlModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QStandardItemModel::flags(index); + if (index.isValid()) { + flags &= ~Qt::ItemIsEditable; + // ### some future version could support "moving" urls onto a folder + flags &= ~Qt::ItemIsDropEnabled; + } + + if (index.data(Qt::DecorationRole).isNull()) + flags &= ~Qt::ItemIsEnabled; + + if (invalidUrls.contains(index.data(UrlRole).toUrl())) + flags &= ~Qt::ItemIsEnabled; + + return flags; +} + +/*! + \reimp +*/ +QMimeData *QUrlModel::mimeData(const QModelIndexList &indexes) const +{ + QList<QUrl> list; + for (int i = 0; i < indexes.count(); ++i) { + if (indexes.at(i).column() == 0) + list.append(indexes.at(i).data(UrlRole).toUrl()); + } + QMimeData *data = new QMimeData(); + data->setUrls(list); + return data; +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + Decide based upon the data if it should be accepted or not + + We only accept dirs and not files +*/ +bool QUrlModel::canDrop(QDragEnterEvent *event) +{ + if (!event->mimeData()->formats().contains(mimeTypes().first())) + return false; + + const QList<QUrl> list = event->mimeData()->urls(); + for (int i = 0; i < list.count(); ++i) { + QModelIndex idx = fileSystemModel->index(list.at(0).toLocalFile()); + if (!fileSystemModel->isDir(idx)) + return false; + } + return true; +} + +/*! + \reimp +*/ +bool QUrlModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + if (!data->formats().contains(mimeTypes().first())) + return false; + Q_UNUSED(action); + Q_UNUSED(column); + Q_UNUSED(parent); + addUrls(data->urls(), row); + return true; +} + +#endif // QT_NO_DRAGANDDROP + +/*! + \reimp + + If the role is the UrlRole then handle otherwise just pass to QStandardItemModel +*/ +bool QUrlModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (value.type() == QVariant::Url) { + QUrl url = value.toUrl(); + QModelIndex dirIndex = fileSystemModel->index(url.toLocalFile()); + //On windows the popup display the "C:\", convert to nativeSeparators + if (showFullPath) + QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString())); + else { + QStandardItemModel::setData(index, QDir::toNativeSeparators(fileSystemModel->data(dirIndex, QFileSystemModel::FilePathRole).toString()), Qt::ToolTipRole); + QStandardItemModel::setData(index, fileSystemModel->data(dirIndex).toString()); + } + QStandardItemModel::setData(index, fileSystemModel->data(dirIndex, Qt::DecorationRole), + Qt::DecorationRole); + QStandardItemModel::setData(index, url, UrlRole); + return true; + } + return QStandardItemModel::setData(index, value, role); +} + +void QUrlModel::setUrl(const QModelIndex &index, const QUrl &url, const QModelIndex &dirIndex) +{ + setData(index, url, UrlRole); + if (url.path().isEmpty()) { + setData(index, fileSystemModel->myComputer()); + setData(index, fileSystemModel->myComputer(Qt::DecorationRole), Qt::DecorationRole); + } else { + QString newName; + if (showFullPath) { + //On windows the popup display the "C:\", convert to nativeSeparators + newName = QDir::toNativeSeparators(dirIndex.data(QFileSystemModel::FilePathRole).toString()); + } else { + newName = dirIndex.data().toString(); + } + + QIcon newIcon = qvariant_cast<QIcon>(dirIndex.data(Qt::DecorationRole)); + if (!dirIndex.isValid()) { + newIcon = fileSystemModel->iconProvider()->icon(QFileIconProvider::Folder); + newName = QFileInfo(url.toLocalFile()).fileName(); + if (!invalidUrls.contains(url)) + invalidUrls.append(url); + } + + // Make sure that we have at least 32x32 images + const QSize size = newIcon.actualSize(QSize(32,32)); + if (size.width() < 32) { + QPixmap smallPixmap = newIcon.pixmap(QSize(32, 32)); + newIcon.addPixmap(smallPixmap.scaledToWidth(32, Qt::SmoothTransformation)); + } + + if (index.data().toString() != newName) + setData(index, newName); + QIcon oldIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole)); + if (oldIcon.cacheKey() != newIcon.cacheKey()) + setData(index, newIcon, Qt::DecorationRole); + } +} + +void QUrlModel::setUrls(const QList<QUrl> &list) +{ + removeRows(0, rowCount()); + invalidUrls.clear(); + watching.clear(); + addUrls(list, 0); +} + +/*! + Add urls \a list into the list at \a row. If move then movie + existing ones to row. + + \sa dropMimeData() +*/ +void QUrlModel::addUrls(const QList<QUrl> &list, int row, bool move) +{ + if (row == -1) + row = rowCount(); + row = qMin(row, rowCount()); + for (int i = list.count() - 1; i >= 0; --i) { + QUrl url = list.at(i); + if (!url.isValid() || url.scheme() != QLatin1String("file")) + continue; + for (int j = 0; move && j < rowCount(); ++j) { + if (index(j, 0).data(UrlRole) == url) { + removeRow(j); + if (j <= row) + row--; + break; + } + } + row = qMax(row, 0); + QModelIndex idx = fileSystemModel->index(url.toLocalFile()); + if (!fileSystemModel->isDir(idx)) + continue; + insertRows(row, 1); + setUrl(index(row, 0), url, idx); + watching.append(QPair<QModelIndex, QString>(idx, url.toLocalFile())); + } +} + +/*! + Return the complete list of urls in a QList. +*/ +QList<QUrl> QUrlModel::urls() const +{ + QList<QUrl> list; + for (int i = 0; i < rowCount(); ++i) + list.append(data(index(i, 0), UrlRole).toUrl()); + return list; +} + +/*! + QFileSystemModel to get index's from, clears existing rows +*/ +void QUrlModel::setFileSystemModel(QFileSystemModel *model) +{ + if (model == fileSystemModel) + return; + if (fileSystemModel != 0) { + disconnect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); + disconnect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + disconnect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(layoutChanged())); + } + fileSystemModel = model; + if (fileSystemModel != 0) { + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); + connect(model, SIGNAL(layoutChanged()), + this, SLOT(layoutChanged())); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(layoutChanged())); + } + clear(); + insertColumns(0, 1); +} + +/* + If one of the index's we are watching has changed update our internal data +*/ +void QUrlModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + QModelIndex parent = topLeft.parent(); + for (int i = 0; i < watching.count(); ++i) { + QModelIndex index = watching.at(i).first; + if (index.model() && topLeft.model()) { + Q_ASSERT(index.model() == topLeft.model()); + } + if ( index.row() >= topLeft.row() + && index.row() <= bottomRight.row() + && index.column() >= topLeft.column() + && index.column() <= bottomRight.column() + && index.parent() == parent) { + changed(watching.at(i).second); + } + } +} + +/*! + Re-get all of our data, anything could have changed! + */ +void QUrlModel::layoutChanged() +{ + QStringList paths; + for (int i = 0; i < watching.count(); ++i) + paths.append(watching.at(i).second); + watching.clear(); + for (int i = 0; i < paths.count(); ++i) { + QString path = paths.at(i); + QModelIndex newIndex = fileSystemModel->index(path); + watching.append(QPair<QModelIndex, QString>(newIndex, path)); + if (newIndex.isValid()) + changed(path); + } +} + +/*! + The following path changed data update our copy of that data + + \sa layoutChanged() dataChanged() +*/ +void QUrlModel::changed(const QString &path) +{ + for (int i = 0; i < rowCount(); ++i) { + QModelIndex idx = index(i, 0); + if (idx.data(UrlRole).toUrl().toLocalFile() == path) { + setData(idx, idx.data(UrlRole).toUrl()); + } + } +} + +QSidebar::QSidebar(QWidget *parent) : QListView(parent) +{ +} + +void QSidebar::init(QFileSystemModel *model, const QList<QUrl> &newUrls) +{ + // ### TODO make icon size dynamic + setIconSize(QSize(24,24)); + setUniformItemSizes(true); + urlModel = new QUrlModel(this); + urlModel->setFileSystemModel(model); + setModel(urlModel); + + connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(clicked(const QModelIndex &))); +#ifndef QT_NO_DRAGANDDROP + setDragDropMode(QAbstractItemView::DragDrop); +#endif + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(showContextMenu(const QPoint &))); + urlModel->setUrls(newUrls); + setCurrentIndex(this->model()->index(0,0)); +} + +QSidebar::~QSidebar() +{ +} + +#ifndef QT_NO_DRAGANDDROP +void QSidebar::dragEnterEvent(QDragEnterEvent *event) +{ + if (urlModel->canDrop(event)) + QListView::dragEnterEvent(event); +} +#endif // QT_NO_DRAGANDDROP + +QSize QSidebar::sizeHint() const +{ + if (model()) + return QListView::sizeHintForIndex(model()->index(0, 0)) + QSize(2 * frameWidth(), 2 * frameWidth()); + return QListView::sizeHint(); +} + +void QSidebar::selectUrl(const QUrl &url) +{ + disconnect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(clicked(const QModelIndex &))); + + selectionModel()->clear(); + for (int i = 0; i < model()->rowCount(); ++i) { + if (model()->index(i, 0).data(QUrlModel::UrlRole).toUrl() == url) { + selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select); + break; + } + } + + connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(clicked(const QModelIndex &))); +} + +#ifndef QT_NO_MENU +/*! + \internal + + \sa removeEntry() +*/ +void QSidebar::showContextMenu(const QPoint &position) +{ + QList<QAction *> actions; + if (indexAt(position).isValid()) { + QAction *action = new QAction(QFileDialog::tr("Remove"), this); + if (indexAt(position).data(QUrlModel::UrlRole).toUrl().path().isEmpty()) + action->setEnabled(false); + connect(action, SIGNAL(triggered()), this, SLOT(removeEntry())); + actions.append(action); + } + if (actions.count() > 0) + QMenu::exec(actions, mapToGlobal(position)); +} +#endif // QT_NO_MENU + +/*! + \internal + + \sa showContextMenu() +*/ +void QSidebar::removeEntry() +{ + QList<QModelIndex> idxs = selectionModel()->selectedIndexes(); + QList<QPersistentModelIndex> indexes; + for (int i = 0; i < idxs.count(); i++) + indexes.append(idxs.at(i)); + + for (int i = 0; i < indexes.count(); ++i) + if (!indexes.at(i).data(QUrlModel::UrlRole).toUrl().path().isEmpty()) + model()->removeRow(indexes.at(i).row()); +} + +/*! + \internal + + \sa goToUrl() +*/ +void QSidebar::clicked(const QModelIndex &index) +{ + QUrl url = model()->index(index.row(), 0).data(QUrlModel::UrlRole).toUrl(); + emit goToUrl(url); + selectUrl(url); +} + +/*! + \reimp + Don't automatically select something + */ +void QSidebar::focusInEvent(QFocusEvent *event) +{ + QAbstractScrollArea::focusInEvent(event); + viewport()->update(); +} + +/*! + \reimp + */ +bool QSidebar::event(QEvent * event) +{ + if (event->type() == QEvent::KeyRelease) { + QKeyEvent* ke = (QKeyEvent*) event; + if (ke->key() == Qt::Key_Delete) { + removeEntry(); + return true; + } + } + return QListView::event(event); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/dialogs/qsidebar_p.h b/src/gui/dialogs/qsidebar_p.h new file mode 100644 index 0000000000..ecbbb3783a --- /dev/null +++ b/src/gui/dialogs/qsidebar_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIDEBAR_H +#define QSIDEBAR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qlistwidget.h> +#include <qstandarditemmodel.h> +#include <qurl.h> + +#ifndef QT_NO_FILEDIALOG + +QT_BEGIN_NAMESPACE + +class QFileSystemModel; +class Q_AUTOTEST_EXPORT QUrlModel : public QStandardItemModel +{ + Q_OBJECT + +public: + enum Roles { + UrlRole = Qt::UserRole + 1 + }; + + QUrlModel(QObject *parent = 0); + + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; +#ifndef QT_NO_DRAGANDDROP + bool canDrop(QDragEnterEvent *event); + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); +#endif + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole); + + void setUrls(const QList<QUrl> &list); + void addUrls(const QList<QUrl> &urls, int row = -1, bool move = true); + QList<QUrl> urls() const; + void setFileSystemModel(QFileSystemModel *model); + bool showFullPath; + +private Q_SLOTS: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void layoutChanged(); + +private: + void setUrl(const QModelIndex &index, const QUrl &url, const QModelIndex &dirIndex); + void changed(const QString &path); + void addIndexToWatch(const QString &path, const QModelIndex &index); + QFileSystemModel *fileSystemModel; + QList<QPair<QModelIndex, QString> > watching; + QList<QUrl> invalidUrls; +}; + +class Q_AUTOTEST_EXPORT QSidebar : public QListView +{ + Q_OBJECT + +Q_SIGNALS: + void goToUrl(const QUrl &url); + +public: + QSidebar(QWidget *parent = 0); + void init(QFileSystemModel *model, const QList<QUrl> &newUrls); + ~QSidebar(); + + QSize sizeHint() const; + + void setUrls(const QList<QUrl> &list) { urlModel->setUrls(list); } + void addUrls(const QList<QUrl> &list, int row) { urlModel->addUrls(list, row); } + QList<QUrl> urls() const { return urlModel->urls(); } + + void selectUrl(const QUrl &url); + +protected: + bool event(QEvent * e); + void focusInEvent(QFocusEvent *event); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *event); +#endif + +private Q_SLOTS: + void clicked(const QModelIndex &index); +#ifndef QT_NO_MENU + void showContextMenu(const QPoint &position); +#endif + void removeEntry(); + +private: + QUrlModel *urlModel; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_FILEDIALOG + +#endif // QSIDEBAR_H + diff --git a/src/gui/dialogs/qwizard.cpp b/src/gui/dialogs/qwizard.cpp new file mode 100644 index 0000000000..32395c412b --- /dev/null +++ b/src/gui/dialogs/qwizard.cpp @@ -0,0 +1,3765 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwizard.h" + +#ifndef QT_NO_WIZARD + +#include "qabstractspinbox.h" +#include "qalgorithms.h" +#include "qapplication.h" +#include "qboxlayout.h" +#include "qlayoutitem.h" +#include "qdesktopwidget.h" +#include "qevent.h" +#include "qframe.h" +#include "qlabel.h" +#include "qlineedit.h" +#include "qpainter.h" +#include "qpushbutton.h" +#include "qset.h" +#include "qstyle.h" +#include "qvarlengtharray.h" +#if defined(Q_WS_MAC) +#include "private/qt_mac_p.h" +#include "qlibrary.h" +#elif !defined(QT_NO_STYLE_WINDOWSVISTA) +#include "qwizard_win_p.h" +#include "qtimer.h" +#endif + +#include "private/qdialog_p.h" +#include <qdebug.h> + +#ifdef Q_OS_WINCE +extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp +#endif + +#include <string.h> // for memset() + +QT_BEGIN_NAMESPACE + +// These fudge terms were needed a few places to obtain pixel-perfect results +const int GapBetweenLogoAndRightEdge = 5; +const int ModernHeaderTopMargin = 2; +const int ClassicHMargin = 4; +const int MacButtonTopMargin = 13; +const int MacLayoutLeftMargin = 20; +const int MacLayoutTopMargin = 14; +const int MacLayoutRightMargin = 20; +const int MacLayoutBottomMargin = 17; + +static void changeSpacerSize(QLayout *layout, int index, int width, int height) +{ + QSpacerItem *spacer = layout->itemAt(index)->spacerItem(); + if (!spacer) + return; + spacer->changeSize(width, height); +} + +static QWidget *iWantTheFocus(QWidget *ancestor) +{ + const int MaxIterations = 100; + + QWidget *candidate = ancestor; + for (int i = 0; i < MaxIterations; ++i) { + candidate = candidate->nextInFocusChain(); + if (!candidate) + break; + + if (candidate->focusPolicy() & Qt::TabFocus) { + if (candidate != ancestor && ancestor->isAncestorOf(candidate)) + return candidate; + } + } + return 0; +} + +static bool objectInheritsXAndXIsCloserThanY(const QObject *object, const QByteArray &classX, + const QByteArray &classY) +{ + const QMetaObject *metaObject = object->metaObject(); + while (metaObject) { + if (metaObject->className() == classX) + return true; + if (metaObject->className() == classY) + return false; + metaObject = metaObject->superClass(); + } + return false; +} + +const int NFallbackDefaultProperties = 7; + +const struct { + const char *className; + const char *property; + const char *changedSignal; +} fallbackProperties[NFallbackDefaultProperties] = { + // If you modify this list, make sure to update the documentation (and the auto test) + { "QAbstractButton", "checked", SIGNAL(toggled(bool)) }, + { "QAbstractSlider", "value", SIGNAL(valueChanged(int)) }, + { "QComboBox", "currentIndex", SIGNAL(currentIndexChanged(int)) }, + { "QDateTimeEdit", "dateTime", SIGNAL(dateTimeChanged(QDateTime)) }, + { "QLineEdit", "text", SIGNAL(textChanged(QString)) }, + { "QListWidget", "currentRow", SIGNAL(currentRowChanged(int)) }, + { "QSpinBox", "value", SIGNAL(valueChanged(int)) } +}; + +class QWizardDefaultProperty +{ +public: + QByteArray className; + QByteArray property; + QByteArray changedSignal; + + inline QWizardDefaultProperty() {} + inline QWizardDefaultProperty(const char *className, const char *property, + const char *changedSignal) + : className(className), property(property), changedSignal(changedSignal) {} +}; + +class QWizardField +{ +public: + inline QWizardField() {} + QWizardField(QWizardPage *page, const QString &spec, QObject *object, const char *property, + const char *changedSignal); + + void resolve(const QVector<QWizardDefaultProperty> &defaultPropertyTable); + void findProperty(const QWizardDefaultProperty *properties, int propertyCount); + + QWizardPage *page; + QString name; + bool mandatory; + QObject *object; + QByteArray property; + QByteArray changedSignal; + QVariant initialValue; +}; + +QWizardField::QWizardField(QWizardPage *page, const QString &spec, QObject *object, + const char *property, const char *changedSignal) + : page(page), name(spec), mandatory(false), object(object), property(property), + changedSignal(changedSignal) +{ + if (name.endsWith(QLatin1Char('*'))) { + name.chop(1); + mandatory = true; + } +} + +void QWizardField::resolve(const QVector<QWizardDefaultProperty> &defaultPropertyTable) +{ + if (property.isEmpty()) + findProperty(defaultPropertyTable.constData(), defaultPropertyTable.count()); + initialValue = object->property(property); +} + +void QWizardField::findProperty(const QWizardDefaultProperty *properties, int propertyCount) +{ + QByteArray className; + + for (int i = 0; i < propertyCount; ++i) { + if (objectInheritsXAndXIsCloserThanY(object, properties[i].className, className)) { + className = properties[i].className; + property = properties[i].property; + changedSignal = properties[i].changedSignal; + } + } +} + +class QWizardLayoutInfo +{ +public: + inline QWizardLayoutInfo() + : topLevelMarginLeft(-1), topLevelMarginRight(-1), topLevelMarginTop(-1), + topLevelMarginBottom(-1), childMarginLeft(-1), childMarginRight(-1), + childMarginTop(-1), childMarginBottom(-1), hspacing(-1), vspacing(-1), + wizStyle(QWizard::ClassicStyle), header(false), watermark(false), title(false), + subTitle(false), extension(false) {} + + int topLevelMarginLeft; + int topLevelMarginRight; + int topLevelMarginTop; + int topLevelMarginBottom; + int childMarginLeft; + int childMarginRight; + int childMarginTop; + int childMarginBottom; + int hspacing; + int vspacing; + int buttonSpacing; + QWizard::WizardStyle wizStyle; + bool header; + bool watermark; + bool title; + bool subTitle; + bool extension; + + bool operator==(const QWizardLayoutInfo &other); + inline bool operator!=(const QWizardLayoutInfo &other) { return !operator==(other); } +}; + +bool QWizardLayoutInfo::operator==(const QWizardLayoutInfo &other) +{ + return topLevelMarginLeft == other.topLevelMarginLeft + && topLevelMarginRight == other.topLevelMarginRight + && topLevelMarginTop == other.topLevelMarginTop + && topLevelMarginBottom == other.topLevelMarginBottom + && childMarginLeft == other.childMarginLeft + && childMarginRight == other.childMarginRight + && childMarginTop == other.childMarginTop + && childMarginBottom == other.childMarginBottom + && hspacing == other.hspacing + && vspacing == other.vspacing + && buttonSpacing == other.buttonSpacing + && wizStyle == other.wizStyle + && header == other.header + && watermark == other.watermark + && title == other.title + && subTitle == other.subTitle + && extension == other.extension; +} + +class QWizardHeader : public QWidget +{ +public: + enum RulerType { Ruler }; + + inline QWizardHeader(RulerType /* ruler */, QWidget *parent = 0) + : QWidget(parent) { setFixedHeight(2); } + QWizardHeader(QWidget *parent = 0); + + void setup(const QWizardLayoutInfo &info, const QString &title, + const QString &subTitle, const QPixmap &logo, const QPixmap &banner, + Qt::TextFormat titleFormat, Qt::TextFormat subTitleFormat); + +protected: + void paintEvent(QPaintEvent *event); +#if !defined(QT_NO_STYLE_WINDOWSVISTA) +private: + bool vistaDisabled() const; +#endif +private: + QLabel *titleLabel; + QLabel *subTitleLabel; + QLabel *logoLabel; + QGridLayout *layout; + QPixmap bannerPixmap; +}; + +QWizardHeader::QWizardHeader(QWidget *parent) + : QWidget(parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + setBackgroundRole(QPalette::Base); + + titleLabel = new QLabel(this); + titleLabel->setBackgroundRole(QPalette::Base); + + subTitleLabel = new QLabel(this); + subTitleLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); + subTitleLabel->setWordWrap(true); + + logoLabel = new QLabel(this); + + QFont font = titleLabel->font(); + font.setBold(true); + titleLabel->setFont(font); + + layout = new QGridLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + + layout->setRowMinimumHeight(3, 1); + layout->setRowStretch(4, 1); + + layout->setColumnStretch(2, 1); + layout->setColumnMinimumWidth(4, 2 * GapBetweenLogoAndRightEdge); + layout->setColumnMinimumWidth(6, GapBetweenLogoAndRightEdge); + + layout->addWidget(titleLabel, 2, 1, 1, 2); + layout->addWidget(subTitleLabel, 4, 2); + layout->addWidget(logoLabel, 1, 5, 5, 1); +} + +#if !defined(QT_NO_STYLE_WINDOWSVISTA) +bool QWizardHeader::vistaDisabled() const +{ + bool styleDisabled = false; + QWizard *wiz = parentWidget() ? qobject_cast <QWizard *>(parentWidget()->parentWidget()) : 0; + if (wiz) { + // Designer dosen't support the Vista style for Wizards. This property is used to turn + // off the Vista style. + const QVariant v = wiz->property("_q_wizard_vista_off"); + styleDisabled = v.isValid() && v.toBool(); + } + return styleDisabled; +} +#endif + +void QWizardHeader::setup(const QWizardLayoutInfo &info, const QString &title, + const QString &subTitle, const QPixmap &logo, const QPixmap &banner, + Qt::TextFormat titleFormat, Qt::TextFormat subTitleFormat) +{ + bool modern = ((info.wizStyle == QWizard::ModernStyle) +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + || ((info.wizStyle == QWizard::AeroStyle) + && (QVistaHelper::vistaState() == QVistaHelper::Classic) || vistaDisabled()) +#endif + ); + + layout->setRowMinimumHeight(0, modern ? ModernHeaderTopMargin : 0); + layout->setRowMinimumHeight(1, modern ? info.topLevelMarginTop - ModernHeaderTopMargin - 1 : 0); + layout->setRowMinimumHeight(6, (modern ? 3 : GapBetweenLogoAndRightEdge) + 2); + + int minColumnWidth0 = modern ? info.topLevelMarginLeft + info.topLevelMarginRight : 0; + int minColumnWidth1 = modern ? info.topLevelMarginLeft + info.topLevelMarginRight + 1 + : info.topLevelMarginLeft + ClassicHMargin; + layout->setColumnMinimumWidth(0, minColumnWidth0); + layout->setColumnMinimumWidth(1, minColumnWidth1); + + titleLabel->setTextFormat(titleFormat); + titleLabel->setText(title); + logoLabel->setPixmap(logo); + + subTitleLabel->setTextFormat(subTitleFormat); + subTitleLabel->setText(QLatin1String("Pq\nPq")); + int desiredSubTitleHeight = subTitleLabel->sizeHint().height(); + subTitleLabel->setText(subTitle); + + if (modern) { + bannerPixmap = banner; + } else { + bannerPixmap = QPixmap(); + } + + if (bannerPixmap.isNull()) { + /* + There is no widthForHeight() function, so we simulate it with a loop. + */ + int candidateSubTitleWidth = qMin(512, 2 * qApp->desktop()->width() / 3); + int delta = candidateSubTitleWidth >> 1; + while (delta > 0) { + if (subTitleLabel->heightForWidth(candidateSubTitleWidth - delta) + <= desiredSubTitleHeight) + candidateSubTitleWidth -= delta; + delta >>= 1; + } + + subTitleLabel->setMinimumSize(candidateSubTitleWidth, desiredSubTitleHeight); + + QSize size = layout->totalMinimumSize(); + setMinimumSize(size); + setMaximumSize(QWIDGETSIZE_MAX, size.height()); + } else { + subTitleLabel->setMinimumSize(0, 0); + setFixedSize(banner.size() + QSize(0, 2)); + } + updateGeometry(); +} + +void QWizardHeader::paintEvent(QPaintEvent * /* event */) +{ + QPainter painter(this); + painter.drawPixmap(0, 0, bannerPixmap); + + int x = width() - 2; + int y = height() - 2; + const QPalette &pal = palette(); + painter.setPen(pal.mid().color()); + painter.drawLine(0, y, x, y); + painter.setPen(pal.base().color()); + painter.drawPoint(x + 1, y); + painter.drawLine(0, y + 1, x + 1, y + 1); +} + +// We save one vtable by basing QWizardRuler on QWizardHeader +class QWizardRuler : public QWizardHeader +{ +public: + inline QWizardRuler(QWidget *parent = 0) + : QWizardHeader(Ruler, parent) {} +}; + +class QWizardPagePrivate : public QWidgetPrivate +{ + Q_DECLARE_PUBLIC(QWizardPage) + +public: + enum TriState { Tri_Unknown = -1, Tri_False, Tri_True }; + + inline QWizardPagePrivate() + : wizard(0), completeState(Tri_Unknown), explicitlyFinal(false), commit(false) {} + + bool cachedIsComplete() const; + void _q_maybeEmitCompleteChanged(); + void _q_updateCachedCompleteState(); + + QWizard *wizard; + QString title; + QString subTitle; + QPixmap pixmaps[QWizard::NPixmaps]; + QVector<QWizardField> pendingFields; + mutable TriState completeState; + bool explicitlyFinal; + bool commit; + QMap<int, QString> buttonCustomTexts; +}; + +bool QWizardPagePrivate::cachedIsComplete() const +{ + Q_Q(const QWizardPage); + if (completeState == Tri_Unknown) + completeState = q->isComplete() ? Tri_True : Tri_False; + return completeState == Tri_True; +} + +void QWizardPagePrivate::_q_maybeEmitCompleteChanged() +{ + Q_Q(QWizardPage); + TriState newState = q->isComplete() ? Tri_True : Tri_False; + if (newState != completeState) + emit q->completeChanged(); +} + +void QWizardPagePrivate::_q_updateCachedCompleteState() +{ + Q_Q(QWizardPage); + completeState = q->isComplete() ? Tri_True : Tri_False; +} + +class QWizardAntiFlickerWidget : public QWidget +{ + QWizard *wizard; + QWizardPrivate *wizardPrivate; +public: + QWizardAntiFlickerWidget(QWizard *wizard, QWizardPrivate *wizardPrivate) + : QWidget(wizard) + , wizard(wizard) + , wizardPrivate(wizardPrivate) {} +#if !defined(QT_NO_STYLE_WINDOWSVISTA) +protected: + void paintEvent(QPaintEvent *); +#endif +}; + +class QWizardPrivate : public QDialogPrivate +{ + Q_DECLARE_PUBLIC(QWizard) + +public: + typedef QMap<int, QWizardPage *> PageMap; + + enum Direction { + Backward, + Forward + }; + + inline QWizardPrivate() + : start(-1) + , current(-1) + , canContinue(false) + , canFinish(false) + , disableUpdatesCount(0) + , opts(0) + , buttonsHaveCustomLayout(false) + , titleFmt(Qt::AutoText) + , subTitleFmt(Qt::AutoText) + , placeholderWidget1(0) + , placeholderWidget2(0) + , headerWidget(0) + , watermarkLabel(0) + , titleLabel(0) + , subTitleLabel(0) + , bottomRuler(0) +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + , vistaInitPending(false) + , vistaState(QVistaHelper::Dirty) + , vistaStateChanged(false) + , inHandleAeroStyleChange(false) +#endif + , minimumWidth(0) + , minimumHeight(0) + , maximumWidth(QWIDGETSIZE_MAX) + , maximumHeight(QWIDGETSIZE_MAX) + { + for (int i = 0; i < QWizard::NButtons; ++i) + btns[i] = 0; +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA + && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based) + vistaInitPending = true; +#endif + } + + void init(); + void reset(); + void cleanupPagesNotInHistory(); + void addField(const QWizardField &field); + void removeFieldAt(int index); + void switchToPage(int newId, Direction direction); + QWizardLayoutInfo layoutInfoForCurrentPage(); + void recreateLayout(const QWizardLayoutInfo &info); + void updateLayout(); + void updateMinMaxSizes(const QWizardLayoutInfo &info); + void updateCurrentPage(); + bool ensureButton(QWizard::WizardButton which) const; + void connectButton(QWizard::WizardButton which) const; + void updateButtonTexts(); + void updateButtonLayout(); + void setButtonLayout(const QWizard::WizardButton *array, int size); + bool buttonLayoutContains(QWizard::WizardButton which); + void updatePixmap(QWizard::WizardPixmap which); +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + bool vistaDisabled() const; + bool isVistaThemeEnabled(QVistaHelper::VistaState state) const; + void handleAeroStyleChange(); +#endif + bool isVistaThemeEnabled() const; + void disableUpdates(); + void enableUpdates(); + void _q_emitCustomButtonClicked(); + void _q_updateButtonStates(); + void setStyle(QStyle *style); +#ifdef Q_WS_MAC + static QPixmap findDefaultBackgroundPixmap(); +#endif + + PageMap pageMap; + QVector<QWizardField> fields; + QMap<QString, int> fieldIndexMap; + QVector<QWizardDefaultProperty> defaultPropertyTable; + QList<int> history; + QSet<int> initialized; // ### remove and move bit to QWizardPage? + int start; + int current; + bool canContinue; + bool canFinish; + QWizardLayoutInfo layoutInfo; + int disableUpdatesCount; + + QWizard::WizardStyle wizStyle; + QWizard::WizardOptions opts; + QMap<int, QString> buttonCustomTexts; + bool buttonsHaveCustomLayout; + QList<QWizard::WizardButton> buttonsCustomLayout; + Qt::TextFormat titleFmt; + Qt::TextFormat subTitleFmt; + mutable QPixmap defaultPixmaps[QWizard::NPixmaps]; + + union { + // keep in sync with QWizard::WizardButton + mutable struct { + QAbstractButton *back; + QAbstractButton *next; + QAbstractButton *commit; + QAbstractButton *finish; + QAbstractButton *cancel; + QAbstractButton *help; + } btn; + mutable QAbstractButton *btns[QWizard::NButtons]; + }; + QWizardAntiFlickerWidget *antiFlickerWidget; + QWidget *placeholderWidget1; + QWidget *placeholderWidget2; + QWizardHeader *headerWidget; + QLabel *watermarkLabel; + QFrame *pageFrame; + QLabel *titleLabel; + QLabel *subTitleLabel; + QWizardRuler *bottomRuler; + + QVBoxLayout *pageVBoxLayout; + QHBoxLayout *buttonLayout; + QGridLayout *mainLayout; + +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + QVistaHelper *vistaHelper; + bool vistaInitPending; + QVistaHelper::VistaState vistaState; + bool vistaStateChanged; + bool inHandleAeroStyleChange; +#endif + int minimumWidth; + int minimumHeight; + int maximumWidth; + int maximumHeight; +}; + +static QString buttonDefaultText(int wstyle, int which, const QWizardPrivate *wizardPrivate) +{ +#if defined(QT_NO_STYLE_WINDOWSVISTA) + Q_UNUSED(wizardPrivate); +#endif + const bool macStyle = (wstyle == QWizard::MacStyle); + switch (which) { + case QWizard::BackButton: + return macStyle ? QWizard::tr("Go Back") : QWizard::tr("< &Back"); + case QWizard::NextButton: + if (macStyle) + return QWizard::tr("Continue"); + else + return wizardPrivate->isVistaThemeEnabled() + ? QWizard::tr("&Next") : QWizard::tr("&Next >"); + case QWizard::CommitButton: + return QWizard::tr("Commit"); + case QWizard::FinishButton: + return macStyle ? QWizard::tr("Done") : QWizard::tr("&Finish"); + case QWizard::CancelButton: + return QWizard::tr("Cancel"); + case QWizard::HelpButton: + return macStyle ? QWizard::tr("Help") : QWizard::tr("&Help"); + default: + return QString(); + } +} + +void QWizardPrivate::init() +{ + Q_Q(QWizard); + + antiFlickerWidget = new QWizardAntiFlickerWidget(q, this); + wizStyle = QWizard::WizardStyle(q->style()->styleHint(QStyle::SH_WizardStyle, 0, q)); + if (wizStyle == QWizard::MacStyle) { + opts = (QWizard::NoDefaultButton | QWizard::NoCancelButton); + } else if (wizStyle == QWizard::ModernStyle) { + opts = QWizard::HelpButtonOnRight; + } + +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + vistaHelper = new QVistaHelper(q); +#endif + + // create these buttons right away; create the other buttons as necessary + ensureButton(QWizard::BackButton); + ensureButton(QWizard::NextButton); + ensureButton(QWizard::CommitButton); + ensureButton(QWizard::FinishButton); + + pageFrame = new QFrame(antiFlickerWidget); + pageFrame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + pageVBoxLayout = new QVBoxLayout(pageFrame); + pageVBoxLayout->setSpacing(0); + pageVBoxLayout->addSpacing(0); + QSpacerItem *spacerItem = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding); + pageVBoxLayout->addItem(spacerItem); + + buttonLayout = new QHBoxLayout; + mainLayout = new QGridLayout(antiFlickerWidget); + mainLayout->setSizeConstraint(QLayout::SetNoConstraint); + + updateButtonLayout(); + + for (int i = 0; i < NFallbackDefaultProperties; ++i) + defaultPropertyTable.append(QWizardDefaultProperty(fallbackProperties[i].className, + fallbackProperties[i].property, + fallbackProperties[i].changedSignal)); +} + +void QWizardPrivate::reset() +{ + Q_Q(QWizard); + if (current != -1) { + q->currentPage()->hide(); + cleanupPagesNotInHistory(); + for (int i = history.count() - 1; i >= 0; --i) + q->cleanupPage(history.at(i)); + history.clear(); + initialized.clear(); + + current = -1; + emit q->currentIdChanged(-1); + } +} + +void QWizardPrivate::cleanupPagesNotInHistory() +{ + Q_Q(QWizard); + + const QSet<int> original = initialized; + QSet<int>::const_iterator i = original.constBegin(); + QSet<int>::const_iterator end = original.constEnd(); + + for (; i != end; ++i) { + if (!history.contains(*i)) { + q->cleanupPage(*i); + initialized.remove(*i); + } + } +} + +void QWizardPrivate::addField(const QWizardField &field) +{ + QWizardField myField = field; + myField.resolve(defaultPropertyTable); + + if (fieldIndexMap.contains(myField.name)) { + qWarning("QWizardPage::addField: Duplicate field '%s'", qPrintable(myField.name)); + return; + } + + fieldIndexMap.insert(myField.name, fields.count()); + fields += myField; + if (myField.mandatory && !myField.changedSignal.isEmpty()) + QObject::connect(myField.object, myField.changedSignal, + myField.page, SLOT(_q_maybeEmitCompleteChanged())); +} + +void QWizardPrivate::removeFieldAt(int index) +{ + const QWizardField &field = fields.at(index); + fieldIndexMap.remove(field.name); + if (field.mandatory && !field.changedSignal.isEmpty()) + QObject::disconnect(field.object, field.changedSignal, + field.page, SLOT(_q_maybeEmitCompleteChanged())); + fields.remove(index); +} + +void QWizardPrivate::switchToPage(int newId, Direction direction) +{ + Q_Q(QWizard); + + disableUpdates(); + + int oldId = current; + if (QWizardPage *oldPage = q->currentPage()) { + oldPage->hide(); + + if (direction == Backward) { + if (!(opts & QWizard::IndependentPages)) { + q->cleanupPage(oldId); + initialized.remove(oldId); + } + Q_ASSERT(history.last() == oldId); + history.removeLast(); + Q_ASSERT(history.last() == newId); + } + } + + current = newId; + + QWizardPage *newPage = q->currentPage(); + if (newPage) { + if (direction == Forward) { + if (!initialized.contains(current)) { + initialized.insert(current); + q->initializePage(current); + } + history.append(current); + } + newPage->show(); + } + + canContinue = (q->nextId() != -1); + canFinish = (newPage && newPage->isFinalPage()); + + _q_updateButtonStates(); + updateButtonTexts(); + + const QWizard::WizardButton nextOrCommit = + newPage && newPage->isCommitPage() ? QWizard::CommitButton : QWizard::NextButton; + QAbstractButton *nextOrFinishButton = + btns[canContinue ? nextOrCommit : QWizard::FinishButton]; + QWidget *candidate = 0; + + /* + If there is no default button and the Next or Finish button + is enabled, give focus directly to it as a convenience to the + user. This is the normal case on Mac OS X. + + Otherwise, give the focus to the new page's first child that + can handle it. If there is no such child, give the focus to + Next or Finish. + */ + if ((opts & QWizard::NoDefaultButton) && nextOrFinishButton->isEnabled()) { + candidate = nextOrFinishButton; + } else if (newPage) { + candidate = iWantTheFocus(newPage); + } + if (!candidate) + candidate = nextOrFinishButton; + candidate->setFocus(); + + if (wizStyle == QWizard::MacStyle) + q->updateGeometry(); + + enableUpdates(); + updateLayout(); + + emit q->currentIdChanged(current); +} + +// keep in sync with QWizard::WizardButton +static const char * const buttonSlots[QWizard::NStandardButtons] = { + SLOT(back()), SLOT(next()), SLOT(next()), SLOT(accept()), SLOT(reject()), + SIGNAL(helpRequested()) +}; + +QWizardLayoutInfo QWizardPrivate::layoutInfoForCurrentPage() +{ + Q_Q(QWizard); + QStyle *style = q->style(); + + QWizardLayoutInfo info; + + const int layoutHorizontalSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); + info.topLevelMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, q); + info.topLevelMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, q); + info.topLevelMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, q); + info.topLevelMarginBottom = style->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, q); + info.childMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, titleLabel); + info.childMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, titleLabel); + info.childMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, titleLabel); + info.childMarginBottom = style->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, titleLabel); + info.hspacing = (layoutHorizontalSpacing == -1) + ? style->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, Qt::Horizontal) + : layoutHorizontalSpacing; + info.vspacing = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing); + info.buttonSpacing = (layoutHorizontalSpacing == -1) + ? style->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal) + : layoutHorizontalSpacing; + + if (wizStyle == QWizard::MacStyle) + info.buttonSpacing = 12; + + info.wizStyle = wizStyle; + if ((info.wizStyle == QWizard::AeroStyle) +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + && (QVistaHelper::vistaState() == QVistaHelper::Classic || vistaDisabled()) +#endif + ) + info.wizStyle = QWizard::ModernStyle; + + QString titleText; + QString subTitleText; + QPixmap backgroundPixmap; + QPixmap watermarkPixmap; + + if (QWizardPage *page = q->currentPage()) { + titleText = page->title(); + subTitleText = page->subTitle(); + backgroundPixmap = page->pixmap(QWizard::BackgroundPixmap); + watermarkPixmap = page->pixmap(QWizard::WatermarkPixmap); + } + + info.header = (info.wizStyle == QWizard::ClassicStyle || info.wizStyle == QWizard::ModernStyle) + && !(opts & QWizard::IgnoreSubTitles) && !subTitleText.isEmpty(); + info.watermark = (info.wizStyle != QWizard::MacStyle) && (info.wizStyle != QWizard::AeroStyle) + && !watermarkPixmap.isNull(); + info.title = !info.header && !titleText.isEmpty(); + info.subTitle = !(opts & QWizard::IgnoreSubTitles) && !info.header && !subTitleText.isEmpty(); + info.extension = info.watermark && (opts & QWizard::ExtendedWatermarkPixmap); + + return info; +} + +void QWizardPrivate::recreateLayout(const QWizardLayoutInfo &info) +{ + Q_Q(QWizard); + + /* + Start by undoing the main layout. + */ + for (int i = mainLayout->count() - 1; i >= 0; --i) { + QLayoutItem *item = mainLayout->takeAt(i); + if (item->layout()) { + item->layout()->setParent(0); + } else { + delete item; + } + } + for (int i = mainLayout->columnCount() - 1; i >= 0; --i) + mainLayout->setColumnMinimumWidth(i, 0); + for (int i = mainLayout->rowCount() - 1; i >= 0; --i) + mainLayout->setRowMinimumHeight(i, 0); + + /* + Now, recreate it. + */ + + bool mac = (info.wizStyle == QWizard::MacStyle); + bool classic = (info.wizStyle == QWizard::ClassicStyle); + bool modern = (info.wizStyle == QWizard::ModernStyle); + bool aero = (info.wizStyle == QWizard::AeroStyle); + int deltaMarginLeft = info.topLevelMarginLeft - info.childMarginLeft; + int deltaMarginRight = info.topLevelMarginRight - info.childMarginRight; + int deltaMarginTop = info.topLevelMarginTop - info.childMarginTop; + int deltaMarginBottom = info.topLevelMarginBottom - info.childMarginBottom; + int deltaVSpacing = info.topLevelMarginBottom - info.vspacing; + + int row = 0; + int numColumns; + if (mac) { + numColumns = 3; + } else if (info.watermark) { + numColumns = 2; + } else { + numColumns = 1; + } + int pageColumn = qMin(1, numColumns - 1); + + if (mac) { + mainLayout->setMargin(0); + mainLayout->setSpacing(0); + buttonLayout->setContentsMargins(MacLayoutLeftMargin, MacButtonTopMargin, MacLayoutRightMargin, MacLayoutBottomMargin); + pageVBoxLayout->setMargin(7); + } else { + if (modern) { + mainLayout->setMargin(0); + mainLayout->setSpacing(0); + pageVBoxLayout->setContentsMargins(deltaMarginLeft, deltaMarginTop, + deltaMarginRight, deltaMarginBottom); + buttonLayout->setContentsMargins(info.topLevelMarginLeft, info.topLevelMarginTop, + info.topLevelMarginRight, info.topLevelMarginBottom); + } else { + mainLayout->setContentsMargins(info.topLevelMarginLeft, info.topLevelMarginTop, + info.topLevelMarginRight, info.topLevelMarginBottom); + mainLayout->setHorizontalSpacing(info.hspacing); + mainLayout->setVerticalSpacing(info.vspacing); + pageVBoxLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->setContentsMargins(0, 0, 0, 0); + } + } + buttonLayout->setSpacing(info.buttonSpacing); + + if (info.header) { + if (!headerWidget) + headerWidget = new QWizardHeader(antiFlickerWidget); + headerWidget->setAutoFillBackground(modern); + mainLayout->addWidget(headerWidget, row++, 0, 1, numColumns); + } + if (headerWidget) + headerWidget->setVisible(info.header); + + int watermarkStartRow = row; + + if (mac) + mainLayout->setRowMinimumHeight(row++, 10); + + if (info.title) { + if (!titleLabel) { + titleLabel = new QLabel(antiFlickerWidget); + titleLabel->setBackgroundRole(QPalette::Base); + titleLabel->setWordWrap(true); + } + + QFont titleFont = q->font(); + titleFont.setPointSize(titleFont.pointSize() + (mac ? 3 : 4)); + titleFont.setBold(true); + titleLabel->setPalette(QPalette()); + + if (aero) { + // ### hardcoded for now: + titleFont = QFont(QLatin1String("Segoe UI"), 12); + QPalette pal(titleLabel->palette()); + pal.setColor(QPalette::Text, "#003399"); + titleLabel->setPalette(pal); + } + + titleLabel->setFont(titleFont); + const int aeroTitleIndent = 25; // ### hardcoded for now - should be calculated somehow + if (aero) + titleLabel->setIndent(aeroTitleIndent); + else if (mac) + titleLabel->setIndent(2); + else if (classic) + titleLabel->setIndent(info.childMarginLeft); + else + titleLabel->setIndent(info.topLevelMarginLeft); + if (modern) { + if (!placeholderWidget1) { + placeholderWidget1 = new QWidget(antiFlickerWidget); + placeholderWidget1->setBackgroundRole(QPalette::Base); + } + placeholderWidget1->setFixedHeight(info.topLevelMarginLeft + 2); + mainLayout->addWidget(placeholderWidget1, row++, pageColumn); + } + mainLayout->addWidget(titleLabel, row++, pageColumn); + if (modern) { + if (!placeholderWidget2) { + placeholderWidget2 = new QWidget(antiFlickerWidget); + placeholderWidget2->setBackgroundRole(QPalette::Base); + } + placeholderWidget2->setFixedHeight(5); + mainLayout->addWidget(placeholderWidget2, row++, pageColumn); + } + if (mac) + mainLayout->setRowMinimumHeight(row++, 7); + } + if (placeholderWidget1) + placeholderWidget1->setVisible(info.title && modern); + if (placeholderWidget2) + placeholderWidget2->setVisible(info.title && modern); + + if (info.subTitle) { + if (!subTitleLabel) { + subTitleLabel = new QLabel(pageFrame); + subTitleLabel->setWordWrap(true); + + subTitleLabel->setContentsMargins(info.childMarginLeft , 0, + info.childMarginRight , 0); + + pageVBoxLayout->insertWidget(1, subTitleLabel); + } + } + + // ### try to replace with margin. + changeSpacerSize(pageVBoxLayout, 0, 0, info.subTitle ? info.childMarginLeft : 0); + + int hMargin = mac ? 1 : 0; + int vMargin = hMargin; + + pageFrame->setFrameStyle(mac ? (QFrame::Box | QFrame::Raised) : QFrame::NoFrame); + pageFrame->setLineWidth(0); + pageFrame->setMidLineWidth(hMargin); + + if (info.header) { + if (modern) { + hMargin = info.topLevelMarginLeft; + vMargin = deltaMarginBottom; + } else if (classic) { + hMargin = deltaMarginLeft + ClassicHMargin; + vMargin = 0; + } + } + + if (aero) { + int leftMargin = 18; // ### hardcoded for now - should be calculated somehow + int topMargin = vMargin; + int rightMargin = hMargin; // ### for now + int bottomMargin = vMargin; + pageFrame->setContentsMargins(leftMargin, topMargin, rightMargin, bottomMargin); + } else { + pageFrame->setContentsMargins(hMargin, vMargin, hMargin, vMargin); + } + + if (info.watermark && !watermarkLabel) { + watermarkLabel = new QLabel(antiFlickerWidget); + watermarkLabel->setBackgroundRole(QPalette::Base); + watermarkLabel->setMinimumHeight(1); + watermarkLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + watermarkLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); + } + + //bool wasSemiTransparent = pageFrame->testAttribute(Qt::WA_SetPalette); + const bool wasSemiTransparent = + pageFrame->palette().brush(QPalette::Window).color().alpha() < 255 + || pageFrame->palette().brush(QPalette::Base).color().alpha() < 255; + if (mac) { + if (!wasSemiTransparent) { + QPalette pal = pageFrame->palette(); + pal.setBrush(QPalette::Window, QColor(255, 255, 255, 153)); + // ### The next line is required to ensure visual semitransparency when + // ### switching from ModernStyle to MacStyle. See TAG1 below. + pal.setBrush(QPalette::Base, QColor(255, 255, 255, 153)); + pageFrame->setPalette(pal); + pageFrame->setAutoFillBackground(true); + antiFlickerWidget->setAutoFillBackground(false); + } + } else { + if (wasSemiTransparent) + pageFrame->setPalette(QPalette()); + + bool baseBackground = (modern && !info.header); // ### TAG1 + pageFrame->setBackgroundRole(baseBackground ? QPalette::Base : QPalette::Window); + + if (titleLabel) + titleLabel->setAutoFillBackground(baseBackground); + pageFrame->setAutoFillBackground(baseBackground); + if (watermarkLabel) + watermarkLabel->setAutoFillBackground(baseBackground); + if (placeholderWidget1) + placeholderWidget1->setAutoFillBackground(baseBackground); + if (placeholderWidget2) + placeholderWidget2->setAutoFillBackground(baseBackground); + + if (aero) { + QPalette pal = pageFrame->palette(); + pal.setBrush(QPalette::Window, QColor(255, 255, 255)); + pageFrame->setPalette(pal); + pageFrame->setAutoFillBackground(true); + pal = antiFlickerWidget->palette(); + pal.setBrush(QPalette::Window, QColor(255, 255, 255)); + antiFlickerWidget->setPalette(pal); + antiFlickerWidget->setAutoFillBackground(true); + } + } + + mainLayout->addWidget(pageFrame, row++, pageColumn); + + int watermarkEndRow = row; + if (classic) + mainLayout->setRowMinimumHeight(row++, deltaVSpacing); + + if (aero) { + buttonLayout->setContentsMargins(9, 9, 9, 9); + mainLayout->setContentsMargins(0, 11, 0, 0); + } + + int buttonStartColumn = info.extension ? 1 : 0; + int buttonNumColumns = info.extension ? 1 : numColumns; + + if (classic || modern) { + if (!bottomRuler) + bottomRuler = new QWizardRuler(antiFlickerWidget); + mainLayout->addWidget(bottomRuler, row++, buttonStartColumn, 1, buttonNumColumns); + } + + if (classic) + mainLayout->setRowMinimumHeight(row++, deltaVSpacing); + + mainLayout->addLayout(buttonLayout, row++, buttonStartColumn, 1, buttonNumColumns); + + if (info.watermark) { + if (info.extension) + watermarkEndRow = row; + mainLayout->addWidget(watermarkLabel, watermarkStartRow, 0, + watermarkEndRow - watermarkStartRow, 1); + } + + mainLayout->setColumnMinimumWidth(0, mac && !info.watermark ? 181 : 0); + if (mac) + mainLayout->setColumnMinimumWidth(2, 21); + + if (headerWidget) + headerWidget->setVisible(info.header); + if (titleLabel) + titleLabel->setVisible(info.title); + if (subTitleLabel) + subTitleLabel->setVisible(info.subTitle); + if (bottomRuler) + bottomRuler->setVisible(classic || modern); + if (watermarkLabel) + watermarkLabel->setVisible(info.watermark); + + layoutInfo = info; +} + +void QWizardPrivate::updateLayout() +{ + Q_Q(QWizard); + + disableUpdates(); + + QWizardLayoutInfo info = layoutInfoForCurrentPage(); + if (layoutInfo != info) + recreateLayout(info); + QWizardPage *page = q->currentPage(); + + // If the page can expand vertically, let it stretch "infinitely" more + // than the QSpacerItem at the bottom. Otherwise, let the QSpacerItem + // stretch "infinitely" more than the page. Change the bottom item's + // policy accordingly. The case that the page has no layout is basically + // for Designer, only. + if (page) { + bool expandPage = !page->layout(); + if (!expandPage) { + const QLayoutItem *pageItem = pageVBoxLayout->itemAt(pageVBoxLayout->indexOf(page)); + expandPage = pageItem->expandingDirections() & Qt::Vertical; + } + QSpacerItem *bottomSpacer = pageVBoxLayout->itemAt(pageVBoxLayout->count() - 1)->spacerItem(); + Q_ASSERT(bottomSpacer); + bottomSpacer->changeSize(0, 0, QSizePolicy::Ignored, expandPage ? QSizePolicy::Ignored : QSizePolicy::MinimumExpanding); + pageVBoxLayout->invalidate(); + } + + if (info.header) { + Q_ASSERT(page); + headerWidget->setup(info, page->title(), page->subTitle(), + page->pixmap(QWizard::LogoPixmap), page->pixmap(QWizard::BannerPixmap), + titleFmt, subTitleFmt); + } + + if (info.watermark) { + Q_ASSERT(page); + watermarkLabel->setPixmap(page->pixmap(QWizard::WatermarkPixmap)); + } + if (info.title) { + Q_ASSERT(page); + titleLabel->setTextFormat(titleFmt); + titleLabel->setText(page->title()); + } + if (info.subTitle) { + Q_ASSERT(page); + subTitleLabel->setTextFormat(subTitleFmt); + subTitleLabel->setText(page->subTitle()); + } + + enableUpdates(); + updateMinMaxSizes(info); +} + +void QWizardPrivate::updateMinMaxSizes(const QWizardLayoutInfo &info) +{ + Q_Q(QWizard); + + int extraHeight = 0; +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (isVistaThemeEnabled()) + extraHeight = vistaHelper->titleBarSize() + vistaHelper->topOffset(); +#endif + QSize minimumSize = mainLayout->totalMinimumSize() + QSize(0, extraHeight); + QSize maximumSize; +#if defined(Q_WS_WIN) + if (QSysInfo::WindowsVersion > QSysInfo::WV_Me) // ### See Tasks 164078 and 161660 +#endif + maximumSize = mainLayout->totalMaximumSize(); + if (info.header && headerWidget->maximumWidth() != QWIDGETSIZE_MAX) { + minimumSize.setWidth(headerWidget->maximumWidth()); + maximumSize.setWidth(headerWidget->maximumWidth()); + } + if (info.watermark) { + minimumSize.setHeight(mainLayout->totalSizeHint().height()); + maximumSize.setHeight(mainLayout->totalSizeHint().height()); + } + if (q->minimumWidth() == minimumWidth) { + minimumWidth = minimumSize.width(); + q->setMinimumWidth(minimumWidth); + } + if (q->minimumHeight() == minimumHeight) { + minimumHeight = minimumSize.height(); + q->setMinimumHeight(minimumHeight); + } + if (q->maximumWidth() == maximumWidth) { + maximumWidth = maximumSize.width(); + q->setMaximumWidth(maximumWidth); + } + if (q->maximumHeight() == maximumHeight) { + maximumHeight = maximumSize.height(); + q->setMaximumHeight(maximumHeight); + } +} + +void QWizardPrivate::updateCurrentPage() +{ + Q_Q(QWizard); + if (q->currentPage()) { + canContinue = (q->nextId() != -1); + canFinish = q->currentPage()->isFinalPage(); + } else { + canContinue = false; + canFinish = false; + } + _q_updateButtonStates(); + updateButtonTexts(); +} + +bool QWizardPrivate::ensureButton(QWizard::WizardButton which) const +{ + Q_Q(const QWizard); + if (uint(which) >= QWizard::NButtons) + return false; + + if (!btns[which]) { + QPushButton *pushButton = new QPushButton(antiFlickerWidget); + QStyle *style = q->style(); + if (style != QApplication::style()) // Propagate style + pushButton->setStyle(style); + // Make navigation buttons detectable as passive interactor in designer + switch (which) { + case QWizard::CommitButton: + case QWizard::FinishButton: + case QWizard::CancelButton: + break; + default: { + QString objectName = QLatin1String("__qt__passive_wizardbutton"); + objectName += QString::number(which); + pushButton->setObjectName(objectName); + } + break; + } +#ifdef Q_WS_MAC + pushButton->setAutoDefault(false); +#endif + pushButton->hide(); +#ifdef Q_CC_HPACC + const_cast<QWizardPrivate *>(this)->btns[which] = pushButton; +#else + btns[which] = pushButton; +#endif + if (which < QWizard::NStandardButtons) + pushButton->setText(buttonDefaultText(wizStyle, which, this)); + connectButton(which); + } + return true; +} + +void QWizardPrivate::connectButton(QWizard::WizardButton which) const +{ + Q_Q(const QWizard); + if (which < QWizard::NStandardButtons) { + QObject::connect(btns[which], SIGNAL(clicked()), q, buttonSlots[which]); + } else { + QObject::connect(btns[which], SIGNAL(clicked()), q, SLOT(_q_emitCustomButtonClicked())); + } +} + +void QWizardPrivate::updateButtonTexts() +{ + Q_Q(QWizard); + for (int i = 0; i < QWizard::NButtons; ++i) { + if (btns[i]) { + if (q->currentPage() && (q->currentPage()->d_func()->buttonCustomTexts.contains(i))) + btns[i]->setText(q->currentPage()->d_func()->buttonCustomTexts.value(i)); + else if (buttonCustomTexts.contains(i)) + btns[i]->setText(buttonCustomTexts.value(i)); + else if (i < QWizard::NStandardButtons) + btns[i]->setText(buttonDefaultText(wizStyle, i, this)); + } + } +} + +void QWizardPrivate::updateButtonLayout() +{ + if (buttonsHaveCustomLayout) { + QVarLengthArray<QWizard::WizardButton> array(buttonsCustomLayout.count()); + for (int i = 0; i < buttonsCustomLayout.count(); ++i) + array[i] = buttonsCustomLayout.at(i); + setButtonLayout(array.constData(), array.count()); + } else { + // Positions: + // Help Stretch Custom1 Custom2 Custom3 Cancel Back Next Commit Finish Cancel Help + + const int ArraySize = 12; + QWizard::WizardButton array[ArraySize]; + memset(array, -1, sizeof(array)); + Q_ASSERT(array[0] == QWizard::NoButton); + + if (opts & QWizard::HaveHelpButton) { + int i = (opts & QWizard::HelpButtonOnRight) ? 11 : 0; + array[i] = QWizard::HelpButton; + } + array[1] = QWizard::Stretch; + if (opts & QWizard::HaveCustomButton1) + array[2] = QWizard::CustomButton1; + if (opts & QWizard::HaveCustomButton2) + array[3] = QWizard::CustomButton2; + if (opts & QWizard::HaveCustomButton3) + array[4] = QWizard::CustomButton3; + + if (!(opts & QWizard::NoCancelButton)) { + int i = (opts & QWizard::CancelButtonOnLeft) ? 5 : 10; + array[i] = QWizard::CancelButton; + } + array[6] = QWizard::BackButton; + array[7] = QWizard::NextButton; + array[8] = QWizard::CommitButton; + array[9] = QWizard::FinishButton; + + setButtonLayout(array, ArraySize); + } +} + +void QWizardPrivate::setButtonLayout(const QWizard::WizardButton *array, int size) +{ + QWidget *prev = pageFrame; + + for (int i = buttonLayout->count() - 1; i >= 0; --i) { + QLayoutItem *item = buttonLayout->takeAt(i); + if (QWidget *widget = item->widget()) + widget->hide(); + delete item; + } + + for (int i = 0; i < size; ++i) { + QWizard::WizardButton which = array[i]; + if (which == QWizard::Stretch) { + buttonLayout->addStretch(1); + } else if (which != QWizard::NoButton) { + ensureButton(which); + buttonLayout->addWidget(btns[which]); + + // Back, Next, Commit, and Finish are handled in _q_updateButtonStates() + if (which != QWizard::BackButton && which != QWizard::NextButton + && which != QWizard::CommitButton && which != QWizard::FinishButton) + btns[which]->show(); + + if (prev) + QWidget::setTabOrder(prev, btns[which]); + prev = btns[which]; + } + } + + _q_updateButtonStates(); +} + +bool QWizardPrivate::buttonLayoutContains(QWizard::WizardButton which) +{ + return !buttonsHaveCustomLayout || buttonsCustomLayout.contains(which); +} + +void QWizardPrivate::updatePixmap(QWizard::WizardPixmap which) +{ + Q_Q(QWizard); + if (which == QWizard::BackgroundPixmap) { + if (wizStyle == QWizard::MacStyle) { + q->update(); + q->updateGeometry(); + } + } else { + updateLayout(); + } +} + +#if !defined(QT_NO_STYLE_WINDOWSVISTA) +bool QWizardPrivate::vistaDisabled() const +{ + Q_Q(const QWizard); + const QVariant v = q->property("_q_wizard_vista_off"); + return v.isValid() && v.toBool(); +} + +bool QWizardPrivate::isVistaThemeEnabled(QVistaHelper::VistaState state) const +{ + return wizStyle == QWizard::AeroStyle + && QVistaHelper::vistaState() == state + && !vistaDisabled(); +} + +void QWizardPrivate::handleAeroStyleChange() +{ + Q_Q(QWizard); + + if (inHandleAeroStyleChange) + return; // prevent recursion + inHandleAeroStyleChange = true; + + vistaHelper->backButton()->disconnect(); + q->removeEventFilter(vistaHelper); + + if (isVistaThemeEnabled()) { + if (isVistaThemeEnabled(QVistaHelper::VistaAero)) { + vistaHelper->setDWMTitleBar(QVistaHelper::ExtendedTitleBar); + q->installEventFilter(vistaHelper); + q->setMouseTracking(true); + antiFlickerWidget->move(0, vistaHelper->titleBarSize() + vistaHelper->topOffset()); + vistaHelper->backButton()->move( + 0, vistaHelper->topOffset() // ### should ideally work without the '+ 1' + - qMin(vistaHelper->topOffset(), vistaHelper->topPadding() + 1)); + } else { + vistaHelper->setDWMTitleBar(QVistaHelper::NormalTitleBar); + q->setMouseTracking(true); + antiFlickerWidget->move(0, vistaHelper->topOffset()); + vistaHelper->backButton()->move(0, -1); // ### should ideally work with (0, 0) + } + vistaHelper->setTitleBarIconAndCaptionVisible(false); + QObject::connect( + vistaHelper->backButton(), SIGNAL(clicked()), q, buttonSlots[QWizard::BackButton]); + vistaHelper->backButton()->show(); + } else { + q->setMouseTracking(true); // ### original value possibly different + q->unsetCursor(); // ### ditto + antiFlickerWidget->move(0, 0); + vistaHelper->backButton()->hide(); + vistaHelper->setTitleBarIconAndCaptionVisible(true); + } + + _q_updateButtonStates(); + + if (q->isVisible()) + vistaHelper->setWindowPosHack(); + + inHandleAeroStyleChange = false; +} +#endif + +bool QWizardPrivate::isVistaThemeEnabled() const +{ +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + return isVistaThemeEnabled(QVistaHelper::VistaAero) + || isVistaThemeEnabled(QVistaHelper::VistaBasic); +#else + return false; +#endif +} + +void QWizardPrivate::disableUpdates() +{ + Q_Q(QWizard); + if (disableUpdatesCount++ == 0) { + q->setUpdatesEnabled(false); + antiFlickerWidget->hide(); + } +} + +void QWizardPrivate::enableUpdates() +{ + Q_Q(QWizard); + if (--disableUpdatesCount == 0) { + antiFlickerWidget->show(); + q->setUpdatesEnabled(true); + } +} + +void QWizardPrivate::_q_emitCustomButtonClicked() +{ + Q_Q(QWizard); + QObject *button = q->sender(); + for (int i = QWizard::NStandardButtons; i < QWizard::NButtons; ++i) { + if (btns[i] == button) { + emit q->customButtonClicked(QWizard::WizardButton(i)); + break; + } + } +} + +void QWizardPrivate::_q_updateButtonStates() +{ + Q_Q(QWizard); + + disableUpdates(); + + const QWizardPage *page = q->currentPage(); + bool complete = page && page->isComplete(); + + btn.back->setEnabled(history.count() > 1 + && !q->page(history.at(history.count() - 2))->isCommitPage() + && (!canFinish || !(opts & QWizard::DisabledBackButtonOnLastPage))); + btn.next->setEnabled(canContinue && complete); + btn.commit->setEnabled(canContinue && complete); + btn.finish->setEnabled(canFinish && complete); + + const bool backButtonVisible = buttonLayoutContains(QWizard::BackButton) + && (history.count() > 1 || !(opts & QWizard::NoBackButtonOnStartPage)) + && (canContinue || !(opts & QWizard::NoBackButtonOnLastPage)); + bool commitPage = page && page->isCommitPage(); + btn.back->setVisible(backButtonVisible); + btn.next->setVisible(buttonLayoutContains(QWizard::NextButton) && !commitPage + && (canContinue || (opts & QWizard::HaveNextButtonOnLastPage))); + btn.commit->setVisible(buttonLayoutContains(QWizard::CommitButton) && commitPage + && canContinue); + btn.finish->setVisible(buttonLayoutContains(QWizard::FinishButton) + && (canFinish || (opts & QWizard::HaveFinishButtonOnEarlyPages))); + + bool useDefault = !(opts & QWizard::NoDefaultButton); + if (QPushButton *nextPush = qobject_cast<QPushButton *>(btn.next)) + nextPush->setDefault(canContinue && useDefault && !commitPage); + if (QPushButton *commitPush = qobject_cast<QPushButton *>(btn.commit)) + commitPush->setDefault(canContinue && useDefault && commitPage); + if (QPushButton *finishPush = qobject_cast<QPushButton *>(btn.finish)) + finishPush->setDefault(!canContinue && useDefault); + +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (isVistaThemeEnabled()) { + vistaHelper->backButton()->setEnabled(btn.back->isEnabled()); + vistaHelper->backButton()->setVisible(backButtonVisible); + btn.back->setVisible(false); + } +#endif + + enableUpdates(); +} + +void QWizardPrivate::setStyle(QStyle *style) +{ + for (int i = 0; i < QWizard::NButtons; i++) + if (btns[i]) + btns[i]->setStyle(style); + const PageMap::const_iterator pcend = pageMap.constEnd(); + for (PageMap::const_iterator it = pageMap.constBegin(); it != pcend; ++it) + it.value()->setStyle(style); +} + +#ifdef Q_WS_MAC + +#ifdef Q_WS_MAC32 +QT_BEGIN_INCLUDE_NAMESPACE +#include <QuickTime/QuickTime.h> +QT_END_INCLUDE_NAMESPACE +typedef OSErr (*PtrQTNewDataReferenceFromCFURL)(CFURLRef, UInt32, Handle*, OSType*); +typedef OSErr (*PtrGetGraphicsImporterForDataRefWithFlags)(Handle, OSType, ComponentInstance*, long); +typedef ComponentResult (*PtrGraphicsImportSetFlags)(GraphicsImportComponent, long); +typedef ComponentResult (*PtrGraphicsImportCreateCGImage)(GraphicsImportComponent, CGImageRef*, UInt32); + +static PtrQTNewDataReferenceFromCFURL ptrQTNewDataReferenceFromCFURL = 0; +static PtrGetGraphicsImporterForDataRefWithFlags ptrGetGraphicsImporterForDataRefWithFlags = 0; +static PtrGraphicsImportSetFlags ptrGraphicsImportSetFlags = 0; +static PtrGraphicsImportCreateCGImage ptrGraphicsImportCreateCGImage = 0; + +static bool resolveQuickTimeSymbols() +{ + if (ptrQTNewDataReferenceFromCFURL == 0) { + QLibrary library(QLatin1String("/System/Library/Frameworks/QuickTime.framework/QuickTime")); + ptrQTNewDataReferenceFromCFURL = reinterpret_cast<PtrQTNewDataReferenceFromCFURL>(library.resolve("QTNewDataReferenceFromCFURL")); + ptrGetGraphicsImporterForDataRefWithFlags = reinterpret_cast<PtrGetGraphicsImporterForDataRefWithFlags>(library.resolve("GetGraphicsImporterForDataRefWithFlags")); + ptrGraphicsImportSetFlags = reinterpret_cast<PtrGraphicsImportSetFlags>(library.resolve("GraphicsImportSetFlags")); + ptrGraphicsImportCreateCGImage = reinterpret_cast<PtrGraphicsImportCreateCGImage>(library.resolve("GraphicsImportCreateCGImage")); + } + + return ptrQTNewDataReferenceFromCFURL != 0 && ptrGetGraphicsImporterForDataRefWithFlags != 0 + && ptrGraphicsImportSetFlags != 0 && ptrGraphicsImportCreateCGImage != 0; +} + + +static QPixmap quicktimeTiff(const CFURLRef url) +{ + if (!resolveQuickTimeSymbols()) + return QPixmap(); + + QCFType <CGImageRef> imageRef = 0; + Handle dataRef; + OSType dataRefType; + GraphicsImportComponent gi; + ComponentResult result; + result = ptrQTNewDataReferenceFromCFURL(url, 0, &dataRef, &dataRefType); + if (dataRef != 0) { + OSStatus err = ptrGetGraphicsImporterForDataRefWithFlags(dataRef, dataRefType, &gi, 0); + if (err == noErr && gi) { + result = ptrGraphicsImportSetFlags(gi, (kGraphicsImporterDontDoGammaCorrection + + kGraphicsImporterDontUseColorMatching)); + if (!result) + result = ptrGraphicsImportCreateCGImage(gi, &imageRef, 0); + if (result) + qWarning("Qt: Problem reading TIFF image %ld(%s:%d)", result, __FILE__, __LINE__); + DisposeHandle(dataRef); + CloseComponent(gi); + } + } + + if (imageRef) + return QPixmap::fromMacCGImageRef(imageRef); + return QPixmap(); +} +#endif // Q_WS_MAC32 + +QPixmap QWizardPrivate::findDefaultBackgroundPixmap() +{ + QCFType<CFURLRef> url; + const int ExpectedImageWidth = 242; + const int ExpectedImageHeight = 414; + if (LSFindApplicationForInfo(kLSUnknownCreator, CFSTR("com.apple.KeyboardSetupAssistant"), + 0, 0, &url) == noErr) { + QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, url); + if (bundle) { + url = CFBundleCopyResourceURL(bundle, CFSTR("Background"), CFSTR("tif"), 0); + if (url) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { + QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithURL(url, 0); + QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0); + if (image) { + int width = CGImageGetWidth(image); + int height = CGImageGetHeight(image); + if (width == ExpectedImageWidth && height == ExpectedImageHeight) + return QPixmap::fromMacCGImageRef(image); + } + } else +#endif + { +#ifdef Q_WS_MAC32 + return quicktimeTiff(url); +#endif + } + } + } + } + return QPixmap(); + +} + +#endif + +#if !defined(QT_NO_STYLE_WINDOWSVISTA) +void QWizardAntiFlickerWidget::paintEvent(QPaintEvent *) +{ + if (wizardPrivate->isVistaThemeEnabled()) { + int leftMargin, topMargin, rightMargin, bottomMargin; + wizardPrivate->buttonLayout->getContentsMargins( + &leftMargin, &topMargin, &rightMargin, &bottomMargin); + const int buttonLayoutTop = wizardPrivate->buttonLayout->contentsRect().top() - topMargin; + QPainter painter(this); + const QBrush brush(QColor(240, 240, 240)); // ### hardcoded for now + painter.fillRect(0, buttonLayoutTop, width(), height() - buttonLayoutTop, brush); + painter.setPen(QPen(QBrush(QColor(223, 223, 223)), 0)); // ### hardcoded for now + painter.drawLine(0, buttonLayoutTop, width(), buttonLayoutTop); + if (wizardPrivate->isVistaThemeEnabled(QVistaHelper::VistaBasic)) { + if (window()->isActiveWindow()) + painter.setPen(QPen(QBrush(QColor(169, 191, 214)), 0)); // ### hardcoded for now + else + painter.setPen(QPen(QBrush(QColor(182, 193, 204)), 0)); // ### hardcoded for now + painter.drawLine(0, 0, width(), 0); + } + } +} +#endif + +/*! + \class QWizard + \since 4.3 + \brief The QWizard class provides a framework for wizards. + + A wizard (also called an assistant on Mac OS X) is a special type + of input dialog that consists of a sequence of pages. A wizard's + purpose is to guide the user through a process step by step. + Wizards are useful for complex or infrequent tasks that users may + find difficult to learn. + + QWizard inherits QDialog and represents a wizard. Each page is a + QWizardPage (a QWidget subclass). To create your own wizards, you + can use these classes directly, or you can subclass them for more + control. + + Topics: + + \tableofcontents + + \section1 A Trivial Example + + The following example illustrates how to create wizard pages and + add them to a wizard. For more advanced examples, see + \l{dialogs/classwizard}{Class Wizard} and \l{dialogs/licensewizard}{License + Wizard}. + + \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 1 + \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 3 + \dots + \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 4 + \codeline + \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 5 + \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 7 + \dots + \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 8 + \codeline + \snippet examples/dialogs/trivialwizard/trivialwizard.cpp 10 + + \section1 Wizard Look and Feel + + QWizard supports four wizard looks: + + \list + \o ClassicStyle + \o ModernStyle + \o MacStyle + \o AeroStyle + \endlist + + You can explicitly set the look to use using setWizardStyle() + (e.g., if you want the same look on all platforms). + + \table + \header \o ClassicStyle + \o ModernStyle + \o MacStyle + \o AeroStyle + \row \o \inlineimage qtwizard-classic1.png + \o \inlineimage qtwizard-modern1.png + \o \inlineimage qtwizard-mac1.png + \o \inlineimage qtwizard-aero1.png + \row \o \inlineimage qtwizard-classic2.png + \o \inlineimage qtwizard-modern2.png + \o \inlineimage qtwizard-mac2.png + \o \inlineimage qtwizard-aero2.png + \endtable + + Note: AeroStyle has effect only on a Windows Vista system with alpha compositing enabled. + ModernStyle is used as a fallback when this condition is not met. + + In addition to the wizard style, there are several options that + control the look and feel of the wizard. These can be set using + setOption() or setOptions(). For example, HaveHelpButton makes + QWizard show a \gui Help button along with the other wizard + buttons. + + You can even change the order of the wizard buttons to any + arbitrary order using setButtonLayout(), and you can add up to + three custom buttons (e.g., a \gui Print button) to the button + row. This is achieved by calling setButton() or setButtonText() + with CustomButton1, CustomButton2, or CustomButton3 to set up the + button, and by enabling the HaveCustomButton1, HaveCustomButton2, + or HaveCustomButton3 options. Whenever the user clicks a custom + button, customButtonClicked() is emitted. For example: + + \snippet examples/dialogs/licensewizard/licensewizard.cpp 29 + + \section1 Elements of a Wizard Page + + Wizards consist of a sequence of \l{QWizardPage}s. At any time, + only one page is shown. A page has the following attributes: + + \list + \o A \l{QWizardPage::}{title}. + \o A \l{QWizardPage::}{subTitle}. + \o A set of pixmaps, which may or may not be honored, depending + on the wizard's style: + \list + \o WatermarkPixmap (used by ClassicStyle and ModernStyle) + \o BannerPixmap (used by ModernStyle) + \o LogoPixmap (used by ClassicStyle and ModernStyle) + \o BackgroundPixmap (used by MacStyle) + \endlist + \endlist + + The diagram belows shows how QWizard renders these attributes, + assuming they are all present and ModernStyle is used: + + \image qtwizard-nonmacpage.png + + When a \l{QWizardPage::}{subTitle} is set, QWizard displays it + in a header, in which case it also uses the BannerPixmap and the + LogoPixmap to decorate the header. The WatermarkPixmap is + displayed on the left side, below the header. At the bottom, + there is a row of buttons allowing the user to navigate through + the pages. + + The page itself (the \l{QWizardPage} widget) occupies the area + between the header, the watermark, and the button row. Typically, + the page is a QWizardPage on which a QGridLayout is installed, + with standard child widgets (\l{QLabel}s, \l{QLineEdit}s, etc.). + + If the wizard's style is MacStyle, the page looks radically + different: + + \image qtwizard-macpage.png + + The watermark, banner, and logo pixmaps are ignored by the + MacStyle. If the BackgroundPixmap is set, it is used as the + background for the wizard; otherwise, a default "assistant" image + is used. + + The title and subtitle are set by calling + QWizardPage::setTitle() and QWizardPage::setSubTitle() on the + individual pages. They may be plain text or HTML (see titleFormat + and subTitleFormat). The pixmaps can be set globally for the + entire wizard using setPixmap(), or on a per-page basis using + QWizardPage::setPixmap(). + + \target field mechanism + \section1 Registering and Using Fields + + In many wizards, the contents of a page may affect the default + values of the fields of a later page. To make it easy to + communicate between pages, QWizard supports a "field" mechanism + that allows you to register a field (e.g., a QLineEdit) on a page + and to access its value from any page. It is also possible to + specify mandatory fields (i.e., fields that must be filled before + the user can advance to the next page). + + To register a field, call QWizardPage::registerField() field. + For example: + + \snippet examples/dialogs/classwizard/classwizard.cpp 8 + \dots + \snippet examples/dialogs/classwizard/classwizard.cpp 10 + \snippet examples/dialogs/classwizard/classwizard.cpp 11 + \dots + \snippet examples/dialogs/classwizard/classwizard.cpp 13 + + The above code registers three fields, \c className, \c + baseClass, and \c qobjectMacro, which are associated with three + child widgets. The asterisk (\c *) next to \c className denotes a + mandatory field. + + \target initialize page + The fields of any page are accessible from any other page. For + example: + + \snippet examples/dialogs/classwizard/classwizard.cpp 17 + + Here, we call QWizardPage::field() to access the contents of the + \c className field (which was defined in the \c ClassInfoPage) + and use it to initialize the \c OuputFilePage. The field's + contents is returned as a QVariant. + + When we create a field using QWizardPage::registerField(), we + pass a unique field name and a widget. We can also provide a Qt + property name and a "changed" signal (a signal that is emitted + when the property changes) as third and fourth arguments; + however, this is not necessary for the most common Qt widgets, + such as QLineEdit, QCheckBox, and QComboBox, because QWizard + knows which properties to look for. + + \target mandatory fields + + If an asterisk (\c *) is appended to the name when the property + is registered, the field is a \e{mandatory field}. When a page has + mandatory fields, the \gui Next and/or \gui Finish buttons are + enabled only when all mandatory fields are filled. + + To consider a field "filled", QWizard simply checks that the + field's current value doesn't equal the original value (the value + it had when initializePage() was called). For QLineEdit and + QAbstractSpinBox subclasses, QWizard also checks that + \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns + true, to honor any validator or mask. + + QWizard's mandatory field mechanism is provided for convenience. + A more powerful (but also more cumbersome) alternative is to + reimplement QWizardPage::isComplete() and to emit the + QWizardPage::completeChanged() signal whenever the page becomes + complete or incomplete. + + The enabled/disabled state of the \gui Next and/or \gui Finish + buttons is one way to perform validation on the user input. + Another way is to reimplement validateCurrentPage() (or + QWizardPage::validatePage()) to perform some last-minute + validation (and show an error message if the user has entered + incomplete or invalid information). If the function returns true, + the next page is shown (or the wizard finishes); otherwise, the + current page stays up. + + \section1 Creating Linear Wizards + + Most wizards have a linear structure, with page 1 followed by + page 2 and so on until the last page. The \l{dialogs/classwizard}{Class + Wizard} example is such a wizard. With QWizard, linear wizards + are created by instantiating the \l{QWizardPage}s and inserting + them using addPage(). By default, the pages are shown in the + order in which they were added. For example: + + \snippet examples/dialogs/classwizard/classwizard.cpp 0 + \dots + \snippet examples/dialogs/classwizard/classwizard.cpp 2 + + When a page is about to be shown, QWizard calls initializePage() + (which in turn calls QWizardPage::initializePage()) to fill the + page with default values. By default, this function does nothing, + but it can be reimplemented to initialize the page's contents + based on other pages' fields (see the \l{initialize page}{example + above}). + + If the user presses \gui Back, cleanupPage() is called (which in + turn calls QWizardPage::cleanupPage()). The default + implementation resets the page's fields to their original values + (the values they had before initializePage() was called). If you + want the \gui Back button to be non-destructive and keep the + values entered by the user, simply enable the IndependentPages + option. + + \section1 Creating Non-Linear Wizards + + Some wizards are more complex in that they allow different + traversal paths based on the information provided by the user. + The \l{dialogs/licensewizard}{License Wizard} example illustrates this. + It provides five wizard pages; depending on which options are + selected, the user can reach different pages. + + \image licensewizard-flow.png + + In complex wizards, pages are identified by IDs. These IDs are + typically defined using an enum. For example: + + \snippet examples/dialogs/licensewizard/licensewizard.h 0 + \dots + \snippet examples/dialogs/licensewizard/licensewizard.h 2 + \dots + \snippet examples/dialogs/licensewizard/licensewizard.h 3 + + The pages are inserted using setPage(), which takes an ID and an + instance of QWizardPage (or of a subclass): + + \snippet examples/dialogs/licensewizard/licensewizard.cpp 1 + \dots + \snippet examples/dialogs/licensewizard/licensewizard.cpp 8 + + By default, the pages are shown in increasing ID order. To + provide a dynamic order that depends on the options chosen by the + user, we must reimplement QWizardPage::nextId(). For example: + + \snippet examples/dialogs/licensewizard/licensewizard.cpp 18 + \codeline + \snippet examples/dialogs/licensewizard/licensewizard.cpp 23 + \codeline + \snippet examples/dialogs/licensewizard/licensewizard.cpp 24 + \codeline + \snippet examples/dialogs/licensewizard/licensewizard.cpp 25 + \codeline + \snippet examples/dialogs/licensewizard/licensewizard.cpp 26 + + It would also be possible to put all the logic in one place, in a + QWizard::nextId() reimplementation. For example: + + \snippet doc/src/snippets/code/src_gui_dialogs_qwizard.cpp 0 + + To start at another page than the page with the lowest ID, call + setStartId(). + + To test whether a page has been visited or not, call + hasVisitedPage(). For example: + + \snippet examples/dialogs/licensewizard/licensewizard.cpp 27 + + \sa QWizardPage, {Class Wizard Example}, {License Wizard Example} +*/ + +/*! + \enum QWizard::WizardButton + + This enum specifies the buttons in a wizard. + + \value BackButton The \gui Back button (\gui {Go Back} on Mac OS X) + \value NextButton The \gui Next button (\gui Continue on Mac OS X) + \value CommitButton The \gui Commit button + \value FinishButton The \gui Finish button (\gui Done on Mac OS X) + \value CancelButton The \gui Cancel button (see also NoCancelButton) + \value HelpButton The \gui Help button (see also HaveHelpButton) + \value CustomButton1 The first user-defined button (see also HaveCustomButton1) + \value CustomButton2 The second user-defined button (see also HaveCustomButton2) + \value CustomButton3 The third user-defined button (see also HaveCustomButton3) + + The following value is only useful when calling setButtonLayout(): + + \value Stretch A horizontal stretch in the button layout + + \omitvalue NoButton + \omitvalue NStandardButtons + \omitvalue NButtons + + \sa setButton(), setButtonText(), setButtonLayout(), customButtonClicked() +*/ + +/*! + \enum QWizard::WizardPixmap + + This enum specifies the pixmaps that can be associated with a page. + + \value WatermarkPixmap The tall pixmap on the left side of a ClassicStyle or ModernStyle page + \value LogoPixmap The small pixmap on the right side of a ClassicStyle or ModernStyle page header + \value BannerPixmap The pixmap that occupies the background of a ModernStyle page header + \value BackgroundPixmap The pixmap that occupies the background of a MacStyle wizard + + \omitvalue NPixmaps + + \sa setPixmap(), QWizardPage::setPixmap(), {Elements of a Wizard Page} +*/ + +/*! + \enum QWizard::WizardStyle + + This enum specifies the different looks supported by QWizard. + + \value ClassicStyle Classic Windows look + \value ModernStyle Modern Windows look + \value MacStyle Mac OS X look + \value AeroStyle Windows Aero look + + \omitvalue NStyles + + \sa setWizardStyle(), WizardOption, {Wizard Look and Feel} +*/ + +/*! + \enum QWizard::WizardOption + + This enum specifies various options that affect the look and feel + of a wizard. + + \value IndependentPages The pages are independent of each other + (i.e., they don't derive values from each + other). + \value IgnoreSubTitles Don't show any subtitles, even if they are set. + \value ExtendedWatermarkPixmap Extend any WatermarkPixmap all the + way down to the window's edge. + \value NoDefaultButton Don't make the \gui Next or \gui Finish button the + dialog's \l{QPushButton::setDefault()}{default button}. + \value NoBackButtonOnStartPage Don't show the \gui Back button on the start page. + \value NoBackButtonOnLastPage Don't show the \gui Back button on the last page. + \value DisabledBackButtonOnLastPage Disable the \gui Back button on the last page. + \value HaveNextButtonOnLastPage Show the (disabled) \gui Next button on the last page. + \value HaveFinishButtonOnEarlyPages Show the (disabled) \gui Finish button on non-final pages. + \value NoCancelButton Don't show the \gui Cancel button. + \value CancelButtonOnLeft Put the \gui Cancel button on the left of \gui Back (rather than on + the right of \gui Finish or \gui Next). + \value HaveHelpButton Show the \gui Help button. + \value HelpButtonOnRight Put the \gui Help button on the far right of the button layout + (rather than on the far left). + \value HaveCustomButton1 Show the first user-defined button (CustomButton1). + \value HaveCustomButton2 Show the second user-defined button (CustomButton2). + \value HaveCustomButton3 Show the third user-defined button (CustomButton3). + + \sa setOptions(), setOption(), testOption() +*/ + +/*! + Constructs a wizard with the given \a parent and window \a flags. + + \sa parent(), windowFlags() +*/ +QWizard::QWizard(QWidget *parent, Qt::WindowFlags flags) + : QDialog(*new QWizardPrivate, parent, flags) +{ + Q_D(QWizard); + d->init(); +#ifdef Q_OS_WINCE + if (!qt_wince_is_mobile()) + setWindowFlags(windowFlags() & ~Qt::WindowOkButtonHint); +#endif +} + +/*! + Destroys the wizard and its pages, releasing any allocated resources. +*/ +QWizard::~QWizard() +{ + Q_D(QWizard); + delete d->buttonLayout; +} + +/*! + Adds the given \a page to the wizard, and returns the page's ID. + + The ID is guaranteed to be larger than any other ID in the + QWizard so far. + + \sa setPage(), page() +*/ +int QWizard::addPage(QWizardPage *page) +{ + Q_D(QWizard); + int theid = 0; + if (!d->pageMap.isEmpty()) + theid = (d->pageMap.constEnd() - 1).key() + 1; + setPage(theid, page); + return theid; +} + +/*! + \fn void QWizard::setPage(int id, QWizardPage *page) + + Adds the given \a page to the wizard with the given \a id. + + \sa addPage(), page() +*/ +void QWizard::setPage(int theid, QWizardPage *page) +{ + Q_D(QWizard); + + if (!page) { + qWarning("QWizard::setPage: Cannot insert null page"); + return; + } + + if (theid == -1) { + qWarning("QWizard::setPage: Cannot insert page with ID -1"); + return; + } + + if (d->pageMap.contains(theid)) { + qWarning("QWizard::setPage: Page with duplicate ID %d ignored", theid); + return; + } + + page->setParent(d->pageFrame); + + QVector<QWizardField> &pendingFields = page->d_func()->pendingFields; + for (int i = 0; i < pendingFields.count(); ++i) + d->addField(pendingFields.at(i)); + pendingFields.clear(); + + connect(page, SIGNAL(completeChanged()), this, SLOT(_q_updateButtonStates())); + + d->pageMap.insert(theid, page); + page->d_func()->wizard = this; + + int n = d->pageVBoxLayout->count(); + + // disable layout to prevent layout updates while adding + bool pageVBoxLayoutEnabled = d->pageVBoxLayout->isEnabled(); + d->pageVBoxLayout->setEnabled(false); + + d->pageVBoxLayout->insertWidget(n - 1, page); + + // hide new page and reset layout to old status + page->hide(); + d->pageVBoxLayout->setEnabled(pageVBoxLayoutEnabled); +} + +/*! + Removes the page with the given \a id. cleanupPage() will be called if necessary. + \since 4.5 + \sa addPage(), setPage() +*/ +void QWizard::removePage(int id) +{ + Q_D(QWizard); + + QWizardPage *removedPage = 0; + + if (d->start == id) + d->start = -1; + + if (!d->history.contains(id)) { + // Case 1: removing a page not in the history + removedPage = d->pageMap.take(id); + d->updateCurrentPage(); + } else if (id != d->current) { + // Case 2: removing a page in the history before the current page + removedPage = d->pageMap.take(id); + d->history.removeOne(id); + d->_q_updateButtonStates(); + } else if (d->history.count() == 1) { + // Case 3: removing the current page which is the first (and only) one in the history + d->reset(); + removedPage = d->pageMap.take(id); + if (d->pageMap.isEmpty()) + d->updateCurrentPage(); + else + restart(); + } else { + // Case 4: removing the current page which is not the first one in the history + back(); + removedPage = d->pageMap.take(id); + d->updateCurrentPage(); + } + + if (removedPage) { + if (d->initialized.contains(id)) { + cleanupPage(id); + d->initialized.remove(id); + } + + d->pageVBoxLayout->removeWidget(removedPage); + + for (int i = d->fields.count() - 1; i >= 0; --i) { + if (d->fields.at(i).page == removedPage) { + removedPage->d_func()->pendingFields += d->fields.at(i); + d->removeFieldAt(i); + } + } + } +} + +/*! + \fn QWizardPage *QWizard::page(int id) const + + Returns the page with the given \a id, or 0 if there is no such + page. + + \sa addPage(), setPage() +*/ +QWizardPage *QWizard::page(int theid) const +{ + Q_D(const QWizard); + return d->pageMap.value(theid); +} + +/*! + \fn bool QWizard::hasVisitedPage(int id) const + + Returns true if the page history contains page \a id; otherwise, + returns false. + + Pressing \gui Back marks the current page as "unvisited" again. + + \sa visitedPages() +*/ +bool QWizard::hasVisitedPage(int theid) const +{ + Q_D(const QWizard); + return d->history.contains(theid); +} + +/*! + Returns the list of IDs of visited pages, in the order in which the pages + were visited. + + Pressing \gui Back marks the current page as "unvisited" again. + + \sa hasVisitedPage() +*/ +QList<int> QWizard::visitedPages() const +{ + Q_D(const QWizard); + return d->history; +} + +/*! + Returns the list of page IDs. + \since 4.5 +*/ +QList<int> QWizard::pageIds() const +{ + Q_D(const QWizard); + return d->pageMap.keys(); +} + +/*! + \property QWizard::startId + \brief the ID of the first page + + If this property isn't explicitly set, this property defaults to + the lowest page ID in this wizard, or -1 if no page has been + inserted yet. + + \sa restart(), nextId() +*/ +void QWizard::setStartId(int theid) +{ + Q_D(QWizard); + if (!d->pageMap.contains(theid)) { + qWarning("QWizard::setStartId: Invalid page ID %d", theid); + return; + } + d->start = theid; +} + +int QWizard::startId() const +{ + Q_D(const QWizard); + if (d->start != -1) + return d->start; + if (!d->pageMap.isEmpty()) + return d->pageMap.constBegin().key(); + return -1; +} + +/*! + Returns a pointer to the current page, or 0 if there is no current + page (e.g., before the wizard is shown). + + This is equivalent to calling page(currentId()). + + \sa page(), currentId(), restart() +*/ +QWizardPage *QWizard::currentPage() const +{ + Q_D(const QWizard); + return page(d->current); +} + +/*! + \property QWizard::currentId + \brief the ID of the current page + + This property cannot be set directly. To change the current page, + call next(), back(), or restart(). + + By default, this property has a value of -1, indicating that no page is + currently shown. + + \sa currentIdChanged(), currentPage() +*/ +int QWizard::currentId() const +{ + Q_D(const QWizard); + return d->current; +} + +/*! + Sets the value of the field called \a name to \a value. + + This function can be used to set fields on any page of the wizard. + + \sa QWizardPage::registerField(), QWizardPage::setField(), field() +*/ +void QWizard::setField(const QString &name, const QVariant &value) +{ + Q_D(QWizard); + + int index = d->fieldIndexMap.value(name, -1); + if (index != -1) { + const QWizardField &field = d->fields.at(index); + if (!field.object->setProperty(field.property, value)) + qWarning("QWizard::setField: Couldn't write to property '%s'", + field.property.constData()); + return; + } + + qWarning("QWizard::setField: No such field '%s'", qPrintable(name)); +} + +/*! + Returns the value of the field called \a name. + + This function can be used to access fields on any page of the wizard. + + \sa QWizardPage::registerField(), QWizardPage::field(), setField() +*/ +QVariant QWizard::field(const QString &name) const +{ + Q_D(const QWizard); + + int index = d->fieldIndexMap.value(name, -1); + if (index != -1) { + const QWizardField &field = d->fields.at(index); + return field.object->property(field.property); + } + + qWarning("QWizard::field: No such field '%s'", qPrintable(name)); + return QVariant(); +} + +/*! + \property QWizard::wizardStyle + \brief the look and feel of the wizard + + By default, QWizard uses the AeroStyle on a Windows Vista system with alpha compositing + enabled, regardless of the current widget style. If this is not the case, the default + wizard style depends on the current widget style as follows: MacStyle is the default if + the current widget style is QMacStyle, ModernStyle is the default if the current widget + style is QWindowsStyle, and ClassicStyle is the default in all other cases. + + \sa {Wizard Look and Feel}, options +*/ +void QWizard::setWizardStyle(WizardStyle style) +{ + Q_D(QWizard); + + const bool styleChange = style != d->wizStyle; + +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + const bool aeroStyleChange = + d->vistaInitPending || d->vistaStateChanged || (styleChange && (style == AeroStyle || d->wizStyle == AeroStyle)); + d->vistaStateChanged = false; + d->vistaInitPending = false; +#endif + + if (styleChange +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + || aeroStyleChange +#endif + ) { + d->disableUpdates(); + d->wizStyle = style; + d->updateButtonTexts(); + d->updateLayout(); + updateGeometry(); + d->enableUpdates(); +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (aeroStyleChange) + d->handleAeroStyleChange(); +#endif + } +} + +QWizard::WizardStyle QWizard::wizardStyle() const +{ + Q_D(const QWizard); + return d->wizStyle; +} + +/*! + Sets the given \a option to be enabled if \a on is true; + otherwise, clears the given \a option. + + \sa options, testOption(), setWizardStyle() +*/ +void QWizard::setOption(WizardOption option, bool on) +{ + Q_D(QWizard); + if (!(d->opts & option) != !on) + setOptions(d->opts ^ option); +} + +/*! + Returns true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption(), setWizardStyle() +*/ +bool QWizard::testOption(WizardOption option) const +{ + Q_D(const QWizard); + return (d->opts & option) != 0; +} + +/*! + \property QWizard::options + \brief the various options that affect the look and feel of the wizard + + By default, the following options are set (depending on the platform): + + \list + \o Windows: HelpButtonOnRight. + \o Mac OS X: NoDefaultButton and NoCancelButton. + \o X11 and QWS (Qt for Embedded Linux): none. + \endlist + + \sa wizardStyle +*/ +void QWizard::setOptions(WizardOptions options) +{ + Q_D(QWizard); + + WizardOptions changed = (options ^ d->opts); + if (!changed) + return; + + d->disableUpdates(); + + d->opts = options; + if ((changed & IndependentPages) && !(d->opts & IndependentPages)) + d->cleanupPagesNotInHistory(); + + if (changed & (NoDefaultButton | HaveHelpButton | HelpButtonOnRight | NoCancelButton + | CancelButtonOnLeft | HaveCustomButton1 | HaveCustomButton2 + | HaveCustomButton3)) { + d->updateButtonLayout(); + } else if (changed & (NoBackButtonOnStartPage | NoBackButtonOnLastPage + | HaveNextButtonOnLastPage | HaveFinishButtonOnEarlyPages + | DisabledBackButtonOnLastPage)) { + d->_q_updateButtonStates(); + } + + d->enableUpdates(); + d->updateLayout(); +} + +QWizard::WizardOptions QWizard::options() const +{ + Q_D(const QWizard); + return d->opts; +} + +/*! + Sets the text on button \a which to be \a text. + + By default, the text on buttons depends on the wizardStyle. For + example, on Mac OS X, the \gui Next button is called \gui + Continue. + + To add extra buttons to the wizard (e.g., a \gui Print button), + one way is to call setButtonText() with CustomButton1, + CustomButton2, or CustomButton3 to set their text, and make the + buttons visible using the HaveCustomButton1, HaveCustomButton2, + and/or HaveCustomButton3 options. + + Button texts may also be set on a per-page basis using QWizardPage::setButtonText(). + + \sa setButton(), button(), setButtonLayout(), setOptions(), QWizardPage::setButtonText() +*/ +void QWizard::setButtonText(WizardButton which, const QString &text) +{ + Q_D(QWizard); + + if (!d->ensureButton(which)) + return; + + d->buttonCustomTexts.insert(which, text); + + if (!currentPage() || !currentPage()->d_func()->buttonCustomTexts.contains(which)) + d->btns[which]->setText(text); +} + +/*! + Returns the text on button \a which. + + If a text has ben set using setButtonText(), this text is returned. + + By default, the text on buttons depends on the wizardStyle. For + example, on Mac OS X, the \gui Next button is called \gui + Continue. + + \sa button(), setButton(), setButtonText(), QWizardPage::buttonText(), + QWizardPage::setButtonText() +*/ +QString QWizard::buttonText(WizardButton which) const +{ + Q_D(const QWizard); + + if (!d->ensureButton(which)) + return QString(); + + if (d->buttonCustomTexts.contains(which)) + return d->buttonCustomTexts.value(which); + + const QString defText = buttonDefaultText(d->wizStyle, which, d); + if(!defText.isNull()) + return defText; + + return d->btns[which]->text(); +} + +/*! + Sets the order in which buttons are displayed to \a layout, where + \a layout is a list of \l{WizardButton}s. + + The default layout depends on the options (e.g., whether + HelpButtonOnRight) that are set. You can call this function if + you need more control over the buttons' layout than what \l + options already provides. + + You can specify horizontal stretches in the layout using \l + Stretch. + + Example: + + \snippet doc/src/snippets/code/src_gui_dialogs_qwizard.cpp 1 + + \sa setButton(), setButtonText(), setOptions() +*/ +void QWizard::setButtonLayout(const QList<WizardButton> &layout) +{ + Q_D(QWizard); + + for (int i = 0; i < layout.count(); ++i) { + WizardButton button1 = layout.at(i); + + if (button1 == NoButton || button1 == Stretch) + continue; + if (!d->ensureButton(button1)) + return; + + // O(n^2), but n is very small + for (int j = 0; j < i; ++j) { + WizardButton button2 = layout.at(j); + if (button2 == button1) { + qWarning("QWizard::setButtonLayout: Duplicate button in layout"); + return; + } + } + } + + d->buttonsHaveCustomLayout = true; + d->buttonsCustomLayout = layout; + d->updateButtonLayout(); +} + +/*! + Sets the button corresponding to role \a which to \a button. + + To add extra buttons to the wizard (e.g., a \gui Print button), + one way is to call setButton() with CustomButton1 to + CustomButton3, and make the buttons visible using the + HaveCustomButton1 to HaveCustomButton3 options. + + \sa setButtonText(), setButtonLayout(), options +*/ +void QWizard::setButton(WizardButton which, QAbstractButton *button) +{ + Q_D(QWizard); + + if (uint(which) >= NButtons || d->btns[which] == button) + return; + + if (QAbstractButton *oldButton = d->btns[which]) { + d->buttonLayout->removeWidget(oldButton); + delete oldButton; + } + + d->btns[which] = button; + if (button) { + button->setParent(d->antiFlickerWidget); + d->buttonCustomTexts.insert(which, button->text()); + d->connectButton(which); + } else { + d->buttonCustomTexts.remove(which); // ### what about page-specific texts set for 'which' + d->ensureButton(which); // (QWizardPage::setButtonText())? Clear them as well? + } + + d->updateButtonLayout(); +} + +/*! + Returns the button corresponding to role \a which. + + \sa setButton(), setButtonText() +*/ +QAbstractButton *QWizard::button(WizardButton which) const +{ + Q_D(const QWizard); +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (d->wizStyle == AeroStyle && which == BackButton) + return d->vistaHelper->backButton(); +#endif + if (!d->ensureButton(which)) + return 0; + return d->btns[which]; +} + +/*! + \property QWizard::titleFormat + \brief the text format used by page titles + + The default format is Qt::AutoText. + + \sa QWizardPage::title, subTitleFormat +*/ +void QWizard::setTitleFormat(Qt::TextFormat format) +{ + Q_D(QWizard); + d->titleFmt = format; + d->updateLayout(); +} + +Qt::TextFormat QWizard::titleFormat() const +{ + Q_D(const QWizard); + return d->titleFmt; +} + +/*! + \property QWizard::subTitleFormat + \brief the text format used by page subtitles + + The default format is Qt::AutoText. + + \sa QWizardPage::title, titleFormat +*/ +void QWizard::setSubTitleFormat(Qt::TextFormat format) +{ + Q_D(QWizard); + d->subTitleFmt = format; + d->updateLayout(); +} + +Qt::TextFormat QWizard::subTitleFormat() const +{ + Q_D(const QWizard); + return d->subTitleFmt; +} + +/*! + Sets the pixmap for role \a which to \a pixmap. + + The pixmaps are used by QWizard when displaying a page. Which + pixmaps are actually used depend on the \l{Wizard Look and + Feel}{wizard style}. + + Pixmaps can also be set for a specific page using + QWizardPage::setPixmap(). + + \sa QWizardPage::setPixmap(), {Elements of a Wizard Page} +*/ +void QWizard::setPixmap(WizardPixmap which, const QPixmap &pixmap) +{ + Q_D(QWizard); + Q_ASSERT(uint(which) < NPixmaps); + d->defaultPixmaps[which] = pixmap; + d->updatePixmap(which); +} + +/*! + Returns the pixmap set for role \a which. + + By default, the only pixmap that is set is the BackgroundPixmap on + Mac OS X. + + \sa QWizardPage::pixmap(), {Elements of a Wizard Page} +*/ +QPixmap QWizard::pixmap(WizardPixmap which) const +{ + Q_D(const QWizard); + Q_ASSERT(uint(which) < NPixmaps); +#ifdef Q_WS_MAC + if (which == BackgroundPixmap && d->defaultPixmaps[BackgroundPixmap].isNull()) + d->defaultPixmaps[BackgroundPixmap] = d->findDefaultBackgroundPixmap(); +#endif + return d->defaultPixmaps[which]; +} + +/*! + Sets the default property for \a className to be \a property, + and the associated change signal to be \a changedSignal. + + The default property is used when an instance of \a className (or + of one of its subclasses) is passed to + QWizardPage::registerField() and no property is specified. + + QWizard knows the most common Qt widgets. For these (or their + subclasses), you don't need to specify a \a property or a \a + changedSignal. The table below lists these widgets: + + \table + \header \o Widget \o Property \o Change Notification Signal + \row \o QAbstractButton \o bool \l{QAbstractButton::}{checked} \o \l{QAbstractButton::}{toggled()} + \row \o QAbstractSlider \o int \l{QAbstractSlider::}{value} \o \l{QAbstractSlider::}{valueChanged()} + \row \o QComboBox \o int \l{QComboBox::}{currentIndex} \o \l{QComboBox::}{currentIndexChanged()} + \row \o QDateTimeEdit \o QDateTime \l{QDateTimeEdit::}{dateTime} \o \l{QDateTimeEdit::}{dateTimeChanged()} + \row \o QLineEdit \o QString \l{QLineEdit::}{text} \o \l{QLineEdit::}{textChanged()} + \row \o QListWidget \o int \l{QListWidget::}{currentRow} \o \l{QListWidget::}{currentRowChanged()} + \row \o QSpinBox \o int \l{QSpinBox::}{value} \o \l{QSpinBox::}{valueChanged()} + \endtable + + \sa QWizardPage::registerField() +*/ +void QWizard::setDefaultProperty(const char *className, const char *property, + const char *changedSignal) +{ + Q_D(QWizard); + for (int i = d->defaultPropertyTable.count() - 1; i >= 0; --i) { + if (qstrcmp(d->defaultPropertyTable.at(i).className, className) == 0) { + d->defaultPropertyTable.remove(i); + break; + } + } + d->defaultPropertyTable.append(QWizardDefaultProperty(className, property, changedSignal)); +} + +/*! + \reimp +*/ +void QWizard::setVisible(bool visible) +{ + Q_D(QWizard); + if (visible) { + if (d->current == -1) + restart(); + } + QDialog::setVisible(visible); +} + +/*! + \reimp +*/ +QSize QWizard::sizeHint() const +{ + Q_D(const QWizard); + QSize result = d->mainLayout->totalSizeHint(); + QSize extra(500, 360); + if (d->wizStyle == MacStyle && d->current != -1) { + QSize pixmap(currentPage()->pixmap(BackgroundPixmap).size()); + extra.setWidth(616); + if (!pixmap.isNull()) { + extra.setHeight(pixmap.height()); + + /* + The width isn't always reliable as a size hint, as + some wizard backgrounds just cover the leftmost area. + Use a rule of thumb to determine if the width is + reliable or not. + */ + if (pixmap.width() >= pixmap.height()) + extra.setWidth(pixmap.width()); + } + } + return result.expandedTo(extra); +} + +/*! + \fn void QWizard::currentIdChanged(int id) + + This signal is emitted when the current page changes, with the new + current \a id. + + \sa currentId(), currentPage() +*/ + +/*! + \fn void QWizard::helpRequested() + + This signal is emitted when the user clicks the \gui Help button. + + By default, no \gui Help button is shown. Call + setOption(HaveHelpButton, true) to have one. + + Example: + + \snippet examples/dialogs/licensewizard/licensewizard.cpp 0 + \dots + \snippet examples/dialogs/licensewizard/licensewizard.cpp 5 + \snippet examples/dialogs/licensewizard/licensewizard.cpp 7 + \dots + \snippet examples/dialogs/licensewizard/licensewizard.cpp 8 + \codeline + \snippet examples/dialogs/licensewizard/licensewizard.cpp 10 + \dots + \snippet examples/dialogs/licensewizard/licensewizard.cpp 12 + \codeline + \snippet examples/dialogs/licensewizard/licensewizard.cpp 14 + \codeline + \snippet examples/dialogs/licensewizard/licensewizard.cpp 15 + + \sa customButtonClicked() +*/ + +/*! + \fn void QWizard::customButtonClicked(int which) + + This signal is emitted when the user clicks a custom button. \a + which can be CustomButton1, CustomButton2, or CustomButton3. + + By default, no custom button is shown. Call setOption() with + HaveCustomButton1, HaveCustomButton2, or HaveCustomButton3 to have + one, and use setButtonText() or setButton() to configure it. + + \sa helpRequested() +*/ + +/*! + Goes back to the previous page. + + This is equivalent to pressing the \gui Back button. + + \sa next(), accept(), reject(), restart() +*/ +void QWizard::back() +{ + Q_D(QWizard); + int n = d->history.count() - 2; + if (n < 0) + return; + d->switchToPage(d->history.at(n), QWizardPrivate::Backward); +} + +/*! + Advances to the next page. + + This is equivalent to pressing the \gui Next or \gui Commit button. + + \sa nextId(), back(), accept(), reject(), restart() +*/ +void QWizard::next() +{ + Q_D(QWizard); + + if (d->current == -1) + return; + + if (validateCurrentPage()) { + int next = nextId(); + if (next != -1) { + if (d->history.contains(next)) { + qWarning("QWizard::next: Page %d already met", next); + return; + } + if (!d->pageMap.contains(next)) { + qWarning("QWizard::next: No such page %d", next); + return; + } + d->switchToPage(next, QWizardPrivate::Forward); + } + } +} + +/*! + Restarts the wizard at the start page. This function is called automatically when the + wizard is shown. + + \sa startId() +*/ +void QWizard::restart() +{ + Q_D(QWizard); + d->disableUpdates(); + d->reset(); + d->switchToPage(startId(), QWizardPrivate::Forward); + d->enableUpdates(); +} + +/*! + \reimp +*/ +bool QWizard::event(QEvent *event) +{ + Q_D(QWizard); + if (event->type() == QEvent::StyleChange) { // Propagate style + d->setStyle(style()); + d->updateLayout(); + } +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + else if (event->type() == QEvent::Show && d->vistaInitPending) { + d->vistaInitPending = false; + d->wizStyle = AeroStyle; + d->handleAeroStyleChange(); + } + else if (d->isVistaThemeEnabled()) { + d->vistaHelper->mouseEvent(event); + } +#endif + return QDialog::event(event); +} + +/*! + \reimp +*/ +void QWizard::resizeEvent(QResizeEvent *event) +{ + Q_D(QWizard); + int heightOffset = 0; +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (d->isVistaThemeEnabled()) { + heightOffset = d->vistaHelper->topOffset(); + if (d->isVistaThemeEnabled(QVistaHelper::VistaAero)) + heightOffset += d->vistaHelper->titleBarSize(); + } +#endif + d->antiFlickerWidget->resize(event->size().width(), event->size().height() - heightOffset); +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (d->isVistaThemeEnabled()) + d->vistaHelper->resizeEvent(event); +#endif + QDialog::resizeEvent(event); +} + +/*! + \reimp +*/ +void QWizard::paintEvent(QPaintEvent * event) +{ + Q_D(QWizard); + if (d->wizStyle == MacStyle && currentPage()) { + QPixmap backgroundPixmap = currentPage()->pixmap(BackgroundPixmap); + if (backgroundPixmap.isNull()) + return; + + QPainter painter(this); + painter.drawPixmap(0, (height() - backgroundPixmap.height()) / 2, backgroundPixmap); + } +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + else if (d->isVistaThemeEnabled()) { + if (d->isVistaThemeEnabled(QVistaHelper::VistaBasic)) { + QPainter painter(this); + QColor color = d->vistaHelper->basicWindowFrameColor(); + painter.fillRect(0, 0, width(), QVistaHelper::topOffset(), color); + } + d->vistaHelper->paintEvent(event); + } +#else + Q_UNUSED(event); +#endif +} + +#if defined(Q_WS_WIN) +/*! + \reimp +*/ +bool QWizard::winEvent(MSG *message, long *result) +{ +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + Q_D(QWizard); + if (d->isVistaThemeEnabled()) { + const bool winEventResult = d->vistaHelper->handleWinEvent(message, result); + if (QVistaHelper::vistaState() != d->vistaState) { + d->vistaState = QVistaHelper::vistaState(); + d->vistaStateChanged = true; + setWizardStyle(AeroStyle); + } + return winEventResult; + } else { + return QDialog::winEvent(message, result); + } +#else + return QDialog::winEvent(message, result); +#endif +} +#endif + +/*! + \reimp +*/ +void QWizard::done(int result) +{ + Q_D(QWizard); + // canceling leaves the wizard in a known state + if (result == Rejected) { + d->reset(); + } else { + if (!validateCurrentPage()) + return; + } + QDialog::done(result); +} + +/*! + \fn void QWizard::initializePage(int id) + + This virtual function is called by QWizard to prepare page \a id + just before it is shown either as a result of QWizard::restart() + being called, or as a result of the user clicking \gui Next. (However, if the \l + QWizard::IndependentPages option is set, this function is only + called the first time the page is shown.) + + By reimplementing this function, you can ensure that the page's + fields are properly initialized based on fields from previous + pages. + + The default implementation calls QWizardPage::initializePage() on + page(\a id). + + \sa QWizardPage::initializePage(), cleanupPage() +*/ +void QWizard::initializePage(int theid) +{ + QWizardPage *page = this->page(theid); + if (page) + page->initializePage(); +} + +/*! + \fn void QWizard::cleanupPage(int id) + + This virtual function is called by QWizard to clean up page \a id just before the + user leaves it by clicking \gui Back (unless the \l QWizard::IndependentPages option is set). + + The default implementation calls QWizardPage::cleanupPage() on + page(\a id). + + \sa QWizardPage::cleanupPage(), initializePage() +*/ +void QWizard::cleanupPage(int theid) +{ + QWizardPage *page = this->page(theid); + if (page) + page->cleanupPage(); +} + +/*! + This virtual function is called by QWizard when the user clicks + \gui Next or \gui Finish to perform some last-minute validation. + If it returns true, the next page is shown (or the wizard + finishes); otherwise, the current page stays up. + + The default implementation calls QWizardPage::validatePage() on + the currentPage(). + + When possible, it is usually better style to disable the \gui + Next or \gui Finish button (by specifying \l{mandatory fields} or + by reimplementing QWizardPage::isComplete()) than to reimplement + validateCurrentPage(). + + \sa QWizardPage::validatePage(), currentPage() +*/ +bool QWizard::validateCurrentPage() +{ + QWizardPage *page = currentPage(); + if (!page) + return true; + + return page->validatePage(); +} + +/*! + This virtual function is called by QWizard to find out which page + to show when the user clicks the \gui Next button. + + The return value is the ID of the next page, or -1 if no page follows. + + The default implementation calls QWizardPage::nextId() on the + currentPage(). + + By reimplementing this function, you can specify a dynamic page + order. + + \sa QWizardPage::nextId(), currentPage() +*/ +int QWizard::nextId() const +{ + const QWizardPage *page = currentPage(); + if (!page) + return -1; + + return page->nextId(); +} + +/*! + \class QWizardPage + \since 4.3 + \brief The QWizardPage class is the base class for wizard pages. + + QWizard represents a wizard. Each page is a QWizardPage. When + you create your own wizards, you can use QWizardPage directly, + or you can subclass it for more control. + + A page has the following attributes, which are rendered by + QWizard: a \l title, a \l subTitle, and a \l{setPixmap()}{set of + pixmaps}. See \l{Elements of a Wizard Page} for details. Once a + page is added to the wizard (using QWizard::addPage() or + QWizard::setPage()), wizard() returns a pointer to the + associated QWizard object. + + Page provides five virtual functions that can be reimplemented to + provide custom behavior: + + \list + \o initializePage() is called to initialize the page's contents + when the user clicks the wizard's \gui Next button. If you + want to derive the page's default from what the user entered + on previous pages, this is the function to reimplement. + \o cleanupPage() is called to reset the page's contents when the + user clicks the wizard's \gui Back button. + \o validatePage() validates the page when the user clicks \gui + Next or \gui Finish. It is often used to show an error message + if the user has entered incomplete or invalid information. + \o nextId() returns the ID of the next page. It is useful when + \l{creating non-linear wizards}, which allow different + traversal paths based on the information provided by the user. + \o isComplete() is called to determine whether the \gui Next + and/or \gui Finish button should be enabled or disabled. If + you reimplement isComplete(), also make sure that + completeChanged() is emitted whenever the complete state + changes. + \endlist + + Normally, the \gui Next button and the \gui Finish button of a + wizard are mutually exclusive. If isFinalPage() returns true, \gui + Finish is available; otherwise, \gui Next is available. By + default, isFinalPage() is true only when nextId() returns -1. If + you want to show \gui Next and \gui Final simultaneously for a + page (letting the user perform an "early finish"), call + setFinalPage(true) on that page. For wizards that support early + finishes, you might also want to set the + \l{QWizard::}{HaveNextButtonOnLastPage} and + \l{QWizard::}{HaveFinishButtonOnEarlyPages} options on the + wizard. + + In many wizards, the contents of a page may affect the default + values of the fields of a later page. To make it easy to + communicate between pages, QWizard supports a \l{Registering and + Using Fields}{"field" mechanism} that allows you to register a + field (e.g., a QLineEdit) on a page and to access its value from + any page. Fields are global to the entire wizard and make it easy + for any single page to access information stored by another page, + without having to put all the logic in QWizard or having the + pages know explicitly about each other. Fields are registered + using registerField() and can be accessed at any time using + field() and setField(). + + \sa QWizard, {Class Wizard Example}, {License Wizard Example} +*/ + +/*! + Constructs a wizard page with the given \a parent. + + When the page is inserted into a wizard using QWizard::addPage() + or QWizard::setPage(), the parent is automatically set to be the + wizard. + + \sa wizard() +*/ +QWizardPage::QWizardPage(QWidget *parent) + : QWidget(*new QWizardPagePrivate, parent, 0) +{ + connect(this, SIGNAL(completeChanged()), this, SLOT(_q_updateCachedCompleteState())); +} + +/*! + \property QWizardPage::title + \brief the title of the page + + The title is shown by the QWizard, above the actual page. All + pages should have a title. + + The title may be plain text or HTML, depending on the value of the + \l{QWizard::titleFormat} property. + + By default, this property contains an empty string. + + \sa subTitle, {Elements of a Wizard Page} +*/ +void QWizardPage::setTitle(const QString &title) +{ + Q_D(QWizardPage); + d->title = title; + if (d->wizard && d->wizard->currentPage() == this) + d->wizard->d_func()->updateLayout(); +} + +QString QWizardPage::title() const +{ + Q_D(const QWizardPage); + return d->title; +} + +/*! + \property QWizardPage::subTitle + \brief the subtitle of the page + + The subtitle is shown by the QWizard, between the title and the + actual page. Subtitles are optional. In + \l{QWizard::ClassicStyle}{ClassicStyle} and + \l{QWizard::ModernStyle}{ModernStyle}, using subtitles is + necessary to make the header appear. In + \l{QWizard::MacStyle}{MacStyle}, the subtitle is shown as a text + label just above the actual page. + + The subtitle may be plain text or HTML, depending on the value of + the \l{QWizard::subTitleFormat} property. + + By default, this property contains an empty string. + + \sa title, QWizard::IgnoreSubTitles, {Elements of a Wizard Page} +*/ +void QWizardPage::setSubTitle(const QString &subTitle) +{ + Q_D(QWizardPage); + d->subTitle = subTitle; + if (d->wizard && d->wizard->currentPage() == this) + d->wizard->d_func()->updateLayout(); +} + +QString QWizardPage::subTitle() const +{ + Q_D(const QWizardPage); + return d->subTitle; +} + +/*! + Sets the pixmap for role \a which to \a pixmap. + + The pixmaps are used by QWizard when displaying a page. Which + pixmaps are actually used depend on the \l{Wizard Look and + Feel}{wizard style}. + + Pixmaps can also be set for the entire wizard using + QWizard::setPixmap(), in which case they apply for all pages that + don't specify a pixmap. + + \sa QWizard::setPixmap(), {Elements of a Wizard Page} +*/ +void QWizardPage::setPixmap(QWizard::WizardPixmap which, const QPixmap &pixmap) +{ + Q_D(QWizardPage); + Q_ASSERT(uint(which) < QWizard::NPixmaps); + d->pixmaps[which] = pixmap; + if (d->wizard && d->wizard->currentPage() == this) + d->wizard->d_func()->updatePixmap(which); +} + +/*! + Returns the pixmap set for role \a which. + + Pixmaps can also be set for the entire wizard using + QWizard::setPixmap(), in which case they apply for all pages that + don't specify a pixmap. + + \sa QWizard::pixmap(), {Elements of a Wizard Page} +*/ +QPixmap QWizardPage::pixmap(QWizard::WizardPixmap which) const +{ + Q_D(const QWizardPage); + Q_ASSERT(uint(which) < QWizard::NPixmaps); + + const QPixmap &pixmap = d->pixmaps[which]; + if (!pixmap.isNull()) + return pixmap; + + if (wizard()) + return wizard()->pixmap(which); + + return pixmap; +} + +/*! + This virtual function is called by QWizard::initializePage() to + prepare the page just before it is shown either as a result of QWizard::restart() + being called, or as a result of the user clicking \gui Next. + (However, if the \l QWizard::IndependentPages option is set, this function is only + called the first time the page is shown.) + + By reimplementing this function, you can ensure that the page's + fields are properly initialized based on fields from previous + pages. For example: + + \snippet examples/dialogs/classwizard/classwizard.cpp 17 + + The default implementation does nothing. + + \sa QWizard::initializePage(), cleanupPage(), QWizard::IndependentPages +*/ +void QWizardPage::initializePage() +{ +} + +/*! + This virtual function is called by QWizard::cleanupPage() when + the user leaves the page by clicking \gui Back (unless the \l QWizard::IndependentPages + option is set). + + The default implementation resets the page's fields to their + original values (the values they had before initializePage() was + called). + + \sa QWizard::cleanupPage(), initializePage(), QWizard::IndependentPages +*/ +void QWizardPage::cleanupPage() +{ + Q_D(QWizardPage); + if (d->wizard) { + QVector<QWizardField> &fields = d->wizard->d_func()->fields; + for (int i = 0; i < fields.count(); ++i) { + const QWizardField &field = fields.at(i); + if (field.page == this) + field.object->setProperty(field.property, field.initialValue); + } + } +} + +/*! + This virtual function is called by QWizard::validateCurrentPage() + when the user clicks \gui Next or \gui Finish to perform some + last-minute validation. If it returns true, the next page is shown + (or the wizard finishes); otherwise, the current page stays up. + + The default implementation returns true. + + When possible, it is usually better style to disable the \gui + Next or \gui Finish button (by specifying \l{mandatory fields} or + reimplementing isComplete()) than to reimplement validatePage(). + + \sa QWizard::validateCurrentPage(), isComplete() +*/ +bool QWizardPage::validatePage() +{ + return true; +} + +/*! + This virtual function is called by QWizard to determine whether + the \gui Next or \gui Finish button should be enabled or + disabled. + + The default implementation returns true if all \l{mandatory + fields} are filled; otherwise, it returns false. + + If you reimplement this function, make sure to emit completeChanged(), + from the rest of your implementation, whenever the value of isComplete() + changes. This ensures that QWizard updates the enabled or disabled state of + its buttons. An example of the reimplementation is + available \l{http://doc.trolltech.com/qq/qq22-qwizard.html#validatebeforeitstoolate} + {here}. + + \sa completeChanged(), isFinalPage() +*/ +bool QWizardPage::isComplete() const +{ + Q_D(const QWizardPage); + + if (!d->wizard) + return true; + + const QVector<QWizardField> &wizardFields = d->wizard->d_func()->fields; + for (int i = wizardFields.count() - 1; i >= 0; --i) { + const QWizardField &field = wizardFields.at(i); + if (field.page == this && field.mandatory) { + QVariant value = field.object->property(field.property); + if (value == field.initialValue) + return false; + +#ifndef QT_NO_LINEEDIT + if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(field.object)) { + if (!lineEdit->hasAcceptableInput()) + return false; + } +#endif +#ifndef QT_NO_SPINBOX + if (QAbstractSpinBox *spinBox = qobject_cast<QAbstractSpinBox *>(field.object)) { + if (!spinBox->hasAcceptableInput()) + return false; + } +#endif + } + } + return true; +} + +/*! + Explicitly sets this page to be final if \a finalPage is true. + + After calling setFinalPage(true), isFinalPage() returns true and the \gui + Finish button is visible (and enabled if isComplete() returns + true). + + After calling setFinalPage(false), isFinalPage() returns true if + nextId() returns -1; otherwise, it returns false. + + \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages +*/ +void QWizardPage::setFinalPage(bool finalPage) +{ + Q_D(QWizardPage); + d->explicitlyFinal = finalPage; + QWizard *wizard = this->wizard(); + if (wizard && wizard->currentPage() == this) + wizard->d_func()->updateCurrentPage(); +} + +/*! + This function is called by QWizard to determine whether the \gui + Finish button should be shown for this page or not. + + By default, it returns true if there is no next page + (i.e., nextId() returns -1); otherwise, it returns false. + + By explicitly calling setFinalPage(true), you can let the user perform an + "early finish". + + \sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages +*/ +bool QWizardPage::isFinalPage() const +{ + Q_D(const QWizardPage); + if (d->explicitlyFinal) + return true; + + QWizard *wizard = this->wizard(); + if (wizard && wizard->currentPage() == this) { + // try to use the QWizard implementation if possible + return wizard->nextId() == -1; + } else { + return nextId() == -1; + } +} + +/*! + Sets this page to be a commit page if \a commitPage is true; otherwise, + sets it to be a normal page. + + A commit page is a page that represents an action which cannot be undone + by clicking \gui Back or \gui Cancel. + + A \gui Commit button replaces the \gui Next button on a commit page. Clicking this + button simply calls QWizard::next() just like clicking \gui Next does. + + A page entered directly from a commit page has its \gui Back button disabled. + + \sa isCommitPage() +*/ +void QWizardPage::setCommitPage(bool commitPage) +{ + Q_D(QWizardPage); + d->commit = commitPage; + QWizard *wizard = this->wizard(); + if (wizard && wizard->currentPage() == this) + wizard->d_func()->updateCurrentPage(); +} + +/*! + Returns true if this page is a commit page; otherwise returns false. + + \sa setCommitPage() +*/ +bool QWizardPage::isCommitPage() const +{ + Q_D(const QWizardPage); + return d->commit; +} + +/*! + Sets the text on button \a which to be \a text on this page. + + By default, the text on buttons depends on the QWizard::wizardStyle, + but may be redefined for the wizard as a whole using QWizard::setButtonText(). + + \sa buttonText(), QWizard::setButtonText(), QWizard::buttonText() +*/ +void QWizardPage::setButtonText(QWizard::WizardButton which, const QString &text) +{ + Q_D(QWizardPage); + d->buttonCustomTexts.insert(which, text); + if (wizard() && wizard()->currentPage() == this && wizard()->d_func()->btns[which]) + wizard()->d_func()->btns[which]->setText(text); +} + +/*! + Returns the text on button \a which on this page. + + If a text has ben set using setButtonText(), this text is returned. + Otherwise, if a text has been set using QWizard::setButtonText(), + this text is returned. + + By default, the text on buttons depends on the QWizard::wizardStyle. + For example, on Mac OS X, the \gui Next button is called \gui + Continue. + + \sa setButtonText(), QWizard::buttonText(), QWizard::setButtonText() +*/ +QString QWizardPage::buttonText(QWizard::WizardButton which) const +{ + Q_D(const QWizardPage); + + if (d->buttonCustomTexts.contains(which)) + return d->buttonCustomTexts.value(which); + + if (wizard()) + return wizard()->buttonText(which); + + return QString(); +} + +/*! + This virtual function is called by QWizard::nextId() to find + out which page to show when the user clicks the \gui Next button. + + The return value is the ID of the next page, or -1 if no page follows. + + By default, this function returns the lowest ID greater than the ID + of the current page, or -1 if there is no such ID. + + By reimplementing this function, you can specify a dynamic page + order. For example: + + \snippet examples/dialogs/licensewizard/licensewizard.cpp 18 + + \sa QWizard::nextId() +*/ +int QWizardPage::nextId() const +{ + Q_D(const QWizardPage); + + if (!d->wizard) + return -1; + + bool foundCurrentPage = false; + + const QWizardPrivate::PageMap &pageMap = d->wizard->d_func()->pageMap; + QWizardPrivate::PageMap::const_iterator i = pageMap.constBegin(); + QWizardPrivate::PageMap::const_iterator end = pageMap.constEnd(); + + for (; i != end; ++i) { + if (i.value() == this) { + foundCurrentPage = true; + } else if (foundCurrentPage) { + return i.key(); + } + } + return -1; +} + +/*! + \fn void QWizardPage::completeChanged() + + This signal is emitted whenever the complete state of the page + (i.e., the value of isComplete()) changes. + + If you reimplement isComplete(), make sure to emit + completeChanged() whenever the value of isComplete() changes, to + ensure that QWizard updates the enabled or disabled state of its + buttons. + + \sa isComplete() +*/ + +/*! + Sets the value of the field called \a name to \a value. + + This function can be used to set fields on any page of the wizard. + It is equivalent to calling + wizard()->\l{QWizard::setField()}{setField(\a name, \a value)}. + + \sa QWizard::setField(), field(), registerField() +*/ +void QWizardPage::setField(const QString &name, const QVariant &value) +{ + Q_D(QWizardPage); + if (!d->wizard) + return; + d->wizard->setField(name, value); +} + +/*! + Returns the value of the field called \a name. + + This function can be used to access fields on any page of the + wizard. It is equivalent to calling + wizard()->\l{QWizard::field()}{field(\a name)}. + + Example: + + \snippet examples/dialogs/classwizard/classwizard.cpp 17 + + \sa QWizard::field(), setField(), registerField() +*/ +QVariant QWizardPage::field(const QString &name) const +{ + Q_D(const QWizardPage); + if (!d->wizard) + return QVariant(); + return d->wizard->field(name); +} + +/*! + Creates a field called \a name associated with the given \a + property of the given \a widget. From then on, that property + becomes accessible using field() and setField(). + + Fields are global to the entire wizard and make it easy for any + single page to access information stored by another page, without + having to put all the logic in QWizard or having the pages know + explicitly about each other. + + If \a name ends with an asterisk (\c *), the field is a mandatory + field. When a page has mandatory fields, the \gui Next and/or + \gui Finish buttons are enabled only when all mandatory fields + are filled. This requires a \a changedSignal to be specified, to + tell QWizard to recheck the value stored by the mandatory field. + + QWizard knows the most common Qt widgets. For these (or their + subclasses), you don't need to specify a \a property or a \a + changedSignal. The table below lists these widgets: + + \table + \header \o Widget \o Property \o Change Notification Signal + \row \o QAbstractButton \o bool \l{QAbstractButton::}{checked} \o \l{QAbstractButton::}{toggled()} + \row \o QAbstractSlider \o int \l{QAbstractSlider::}{value} \o \l{QAbstractSlider::}{valueChanged()} + \row \o QComboBox \o int \l{QComboBox::}{currentIndex} \o \l{QComboBox::}{currentIndexChanged()} + \row \o QDateTimeEdit \o QDateTime \l{QDateTimeEdit::}{dateTime} \o \l{QDateTimeEdit::}{dateTimeChanged()} + \row \o QLineEdit \o QString \l{QLineEdit::}{text} \o \l{QLineEdit::}{textChanged()} + \row \o QListWidget \o int \l{QListWidget::}{currentRow} \o \l{QListWidget::}{currentRowChanged()} + \row \o QSpinBox \o int \l{QSpinBox::}{value} \o \l{QSpinBox::}{valueChanged()} + \endtable + + You can use QWizard::setDefaultProperty() to add entries to this + table or to override existing entries. + + To consider a field "filled", QWizard simply checks that their + current value doesn't equal their original value (the value they + had before initializePage() was called). For QLineEdit, it also + checks that + \l{QLineEdit::hasAcceptableInput()}{hasAcceptableInput()} returns + true, to honor any validator or mask. + + QWizard's mandatory field mechanism is provided for convenience. + It can be bypassed by reimplementing QWizardPage::isComplete(). + + \sa field(), setField(), QWizard::setDefaultProperty() +*/ +void QWizardPage::registerField(const QString &name, QWidget *widget, const char *property, + const char *changedSignal) +{ + Q_D(QWizardPage); + QWizardField field(this, name, widget, property, changedSignal); + if (d->wizard) { + d->wizard->d_func()->addField(field); + } else { + d->pendingFields += field; + } +} + +/*! + Returns the wizard associated with this page, or 0 if this page + hasn't been inserted into a QWizard yet. + + \sa QWizard::addPage(), QWizard::setPage() +*/ +QWizard *QWizardPage::wizard() const +{ + Q_D(const QWizardPage); + return d->wizard; +} + +QT_END_NAMESPACE + +#include "moc_qwizard.cpp" + +#endif // QT_NO_WIZARD diff --git a/src/gui/dialogs/qwizard.h b/src/gui/dialogs/qwizard.h new file mode 100644 index 0000000000..ef3ed39aff --- /dev/null +++ b/src/gui/dialogs/qwizard.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWIZARD_H +#define QWIZARD_H + +#include <QtGui/qdialog.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_WIZARD + +class QAbstractButton; +class QWizardPage; +class QWizardPrivate; + +class Q_GUI_EXPORT QWizard : public QDialog +{ + Q_OBJECT + Q_ENUMS(WizardStyle WizardOption) + Q_FLAGS(WizardOptions) + Q_PROPERTY(WizardStyle wizardStyle READ wizardStyle WRITE setWizardStyle) + Q_PROPERTY(WizardOptions options READ options WRITE setOptions) + Q_PROPERTY(Qt::TextFormat titleFormat READ titleFormat WRITE setTitleFormat) + Q_PROPERTY(Qt::TextFormat subTitleFormat READ subTitleFormat WRITE setSubTitleFormat) + Q_PROPERTY(int startId READ startId WRITE setStartId) + Q_PROPERTY(int currentId READ currentId NOTIFY currentIdChanged) + +public: + enum WizardButton { + BackButton, + NextButton, + CommitButton, + FinishButton, + CancelButton, + HelpButton, + CustomButton1, + CustomButton2, + CustomButton3, + Stretch, + + NoButton = -1, + NStandardButtons = 6, + NButtons = 9 + }; + + enum WizardPixmap { + WatermarkPixmap, + LogoPixmap, + BannerPixmap, + BackgroundPixmap, + NPixmaps + }; + + enum WizardStyle { + ClassicStyle, + ModernStyle, + MacStyle, + AeroStyle, + NStyles + }; + + enum WizardOption { + IndependentPages = 0x00000001, + IgnoreSubTitles = 0x00000002, + ExtendedWatermarkPixmap = 0x00000004, + NoDefaultButton = 0x00000008, + NoBackButtonOnStartPage = 0x00000010, + NoBackButtonOnLastPage = 0x00000020, + DisabledBackButtonOnLastPage = 0x00000040, + HaveNextButtonOnLastPage = 0x00000080, + HaveFinishButtonOnEarlyPages = 0x00000100, + NoCancelButton = 0x00000200, + CancelButtonOnLeft = 0x00000400, + HaveHelpButton = 0x00000800, + HelpButtonOnRight = 0x00001000, + HaveCustomButton1 = 0x00002000, + HaveCustomButton2 = 0x00004000, + HaveCustomButton3 = 0x00008000 + }; + + Q_DECLARE_FLAGS(WizardOptions, WizardOption) + + explicit QWizard(QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~QWizard(); + + int addPage(QWizardPage *page); + void setPage(int id, QWizardPage *page); + void removePage(int id); + QWizardPage *page(int id) const; + bool hasVisitedPage(int id) const; + QList<int> visitedPages() const; // ### visitedIds()? + QList<int> pageIds() const; + void setStartId(int id); + int startId() const; + QWizardPage *currentPage() const; + int currentId() const; + + virtual bool validateCurrentPage(); + virtual int nextId() const; + + void setField(const QString &name, const QVariant &value); + QVariant field(const QString &name) const; + + void setWizardStyle(WizardStyle style); + WizardStyle wizardStyle() const; + + void setOption(WizardOption option, bool on = true); + bool testOption(WizardOption option) const; + void setOptions(WizardOptions options); + WizardOptions options() const; + + void setButtonText(WizardButton which, const QString &text); + QString buttonText(WizardButton which) const; + void setButtonLayout(const QList<WizardButton> &layout); + void setButton(WizardButton which, QAbstractButton *button); + QAbstractButton *button(WizardButton which) const; + + void setTitleFormat(Qt::TextFormat format); + Qt::TextFormat titleFormat() const; + void setSubTitleFormat(Qt::TextFormat format); + Qt::TextFormat subTitleFormat() const; + void setPixmap(WizardPixmap which, const QPixmap &pixmap); + QPixmap pixmap(WizardPixmap which) const; + + void setDefaultProperty(const char *className, const char *property, + const char *changedSignal); + + void setVisible(bool visible); + QSize sizeHint() const; + +Q_SIGNALS: + void currentIdChanged(int id); + void helpRequested(); + void customButtonClicked(int which); + +public Q_SLOTS: + void back(); + void next(); + void restart(); + +protected: + bool event(QEvent *event); + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); +#if defined(Q_WS_WIN) + bool winEvent(MSG * message, long * result); +#endif + void done(int result); + virtual void initializePage(int id); + virtual void cleanupPage(int id); + +private: + Q_DISABLE_COPY(QWizard) + Q_DECLARE_PRIVATE(QWizard) + Q_PRIVATE_SLOT(d_func(), void _q_emitCustomButtonClicked()) + Q_PRIVATE_SLOT(d_func(), void _q_updateButtonStates()) + + friend class QWizardPage; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QWizard::WizardOptions) + +class QWizardPagePrivate; + +class Q_GUI_EXPORT QWizardPage : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString title READ title WRITE setTitle) + Q_PROPERTY(QString subTitle READ subTitle WRITE setSubTitle) + +public: + QWizardPage(QWidget *parent = 0); + + void setTitle(const QString &title); + QString title() const; + void setSubTitle(const QString &subTitle); + QString subTitle() const; + void setPixmap(QWizard::WizardPixmap which, const QPixmap &pixmap); + QPixmap pixmap(QWizard::WizardPixmap which) const; + void setFinalPage(bool finalPage); + bool isFinalPage() const; + void setCommitPage(bool commitPage); + bool isCommitPage() const; + void setButtonText(QWizard::WizardButton which, const QString &text); + QString buttonText(QWizard::WizardButton which) const; + + virtual void initializePage(); + virtual void cleanupPage(); + virtual bool validatePage(); + virtual bool isComplete() const; + virtual int nextId() const; + +Q_SIGNALS: + void completeChanged(); + +protected: + void setField(const QString &name, const QVariant &value); + QVariant field(const QString &name) const; + void registerField(const QString &name, QWidget *widget, const char *property = 0, + const char *changedSignal = 0); + QWizard *wizard() const; + +private: + Q_DISABLE_COPY(QWizardPage) + Q_DECLARE_PRIVATE(QWizardPage) + Q_PRIVATE_SLOT(d_func(), void _q_maybeEmitCompleteChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_updateCachedCompleteState()) + + friend class QWizard; + friend class QWizardPrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_WIZARD + +#endif // QWIZARD_H diff --git a/src/gui/dialogs/qwizard_win.cpp b/src/gui/dialogs/qwizard_win.cpp new file mode 100644 index 0000000000..64696de219 --- /dev/null +++ b/src/gui/dialogs/qwizard_win.cpp @@ -0,0 +1,739 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_WIZARD +#ifndef QT_NO_STYLE_WINDOWSVISTA + +#include "qwizard_win_p.h" +#include "qlibrary.h" +#include "qwizard.h" +#include "qpaintengine.h" +#include "qapplication.h" +#include <QtGui/QMouseEvent> +#include <QtGui/QDesktopWidget> + +// Note, these tests are duplicates in qwindowsxpstyle_p.h. +#ifdef Q_CC_GNU +# include <w32api.h> +# if (__W32API_MAJOR_VERSION >= 3 || (__W32API_MAJOR_VERSION == 2 && __W32API_MINOR_VERSION >= 5)) +# ifdef _WIN32_WINNT +# undef _WIN32_WINNT +# endif +# define _WIN32_WINNT 0x0501 +# include <commctrl.h> +# endif +#endif + +#include <uxtheme.h> + +QT_BEGIN_NAMESPACE + +//DWM related +typedef struct { //MARGINS + int cxLeftWidth; // width of left border that retains its size + int cxRightWidth; // width of right border that retains its size + int cyTopHeight; // height of top border that retains its size + int cyBottomHeight; // height of bottom border that retains its size +} WIZ_MARGINS; +typedef struct { //DTTOPTS + DWORD dwSize; + DWORD dwFlags; + COLORREF crText; + COLORREF crBorder; + COLORREF crShadow; + int eTextShadowType; + POINT ptShadowOffset; + int iBorderSize; + int iFontPropId; + int iColorPropId; + int iStateId; + BOOL fApplyOverlay; + int iGlowSize; +} WIZ_DTTOPTS; + +typedef struct { + DWORD dwFlags; + DWORD dwMask; +} WIZ_WTA_OPTIONS; + +#define WIZ_WM_THEMECHANGED 0x031A +#define WIZ_WM_DWMCOMPOSITIONCHANGED 0x031E + +enum WIZ_WINDOWTHEMEATTRIBUTETYPE { + WIZ_WTA_NONCLIENT = 1 +}; + +#define WIZ_WTNCA_NODRAWCAPTION 0x00000001 +#define WIZ_WTNCA_NODRAWICON 0x00000002 + +#define WIZ_DT_CENTER 0x00000001 //DT_CENTER +#define WIZ_DT_VCENTER 0x00000004 +#define WIZ_DT_SINGLELINE 0x00000020 +#define WIZ_DT_NOPREFIX 0x00000800 + +enum WIZ_NAVIGATIONPARTS { //NAVIGATIONPARTS + WIZ_NAV_BACKBUTTON = 1, + WIZ_NAV_FORWARDBUTTON = 2, + WIZ_NAV_MENUBUTTON = 3, +}; + +enum WIZ_NAV_BACKBUTTONSTATES { //NAV_BACKBUTTONSTATES + WIZ_NAV_BB_NORMAL = 1, + WIZ_NAV_BB_HOT = 2, + WIZ_NAV_BB_PRESSED = 3, + WIZ_NAV_BB_DISABLED = 4, +}; + +#define WIZ_TMT_CAPTIONFONT (801) //TMT_CAPTIONFONT +#define WIZ_DTT_COMPOSITED (1UL << 13) //DTT_COMPOSITED +#define WIZ_DTT_GLOWSIZE (1UL << 11) //DTT_GLOWSIZE + +#define WIZ_WM_NCMOUSELEAVE 674 //WM_NCMOUSELEAVE + +#define WIZ_WP_CAPTION 1 //WP_CAPTION +#define WIZ_CS_ACTIVE 1 //CS_ACTIVE +#define WIZ_TMT_FILLCOLORHINT 3821 //TMT_FILLCOLORHINT +#define WIZ_TMT_BORDERCOLORHINT 3822 //TMT_BORDERCOLORHINT + +typedef BOOL (WINAPI *PtrDwmDefWindowProc)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult); +typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL* pfEnabled); +typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const WIZ_MARGINS* pMarInset); +typedef HRESULT (WINAPI *PtrSetWindowThemeAttribute)(HWND hwnd, enum WIZ_WINDOWTHEMEATTRIBUTETYPE eAttribute, PVOID pvAttribute, DWORD cbAttribute); + +static PtrDwmDefWindowProc pDwmDefWindowProc = 0; +static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled = 0; +static PtrDwmExtendFrameIntoClientArea pDwmExtendFrameIntoClientArea = 0; +static PtrSetWindowThemeAttribute pSetWindowThemeAttribute = 0; + +//Theme related +typedef bool (WINAPI *PtrIsAppThemed)(); +typedef bool (WINAPI *PtrIsThemeActive)(); +typedef HANDLE (WINAPI *PtrOpenThemeData)(HWND hwnd, LPCWSTR pszClassList); +typedef HRESULT (WINAPI *PtrCloseThemeData)(HANDLE hTheme); +typedef HRESULT (WINAPI *PtrGetThemeSysFont)(HANDLE hTheme, int iFontId, LOGFONTW *plf); +typedef HRESULT (WINAPI *PtrDrawThemeTextEx)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int cchText, DWORD dwTextFlags, LPRECT pRect, const WIZ_DTTOPTS *pOptions); +typedef HRESULT (WINAPI *PtrDrawThemeBackground)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect); +typedef HRESULT (WINAPI *PtrGetThemePartSize)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, OPTIONAL RECT *prc, enum THEMESIZE eSize, OUT SIZE *psz); +typedef HRESULT (WINAPI *PtrGetThemeColor)(HANDLE hTheme, int iPartId, int iStateId, int iPropId, OUT COLORREF *pColor); + +static PtrIsAppThemed pIsAppThemed = 0; +static PtrIsThemeActive pIsThemeActive = 0; +static PtrOpenThemeData pOpenThemeData = 0; +static PtrCloseThemeData pCloseThemeData = 0; +static PtrGetThemeSysFont pGetThemeSysFont = 0; +static PtrDrawThemeTextEx pDrawThemeTextEx = 0; +static PtrDrawThemeBackground pDrawThemeBackground = 0; +static PtrGetThemePartSize pGetThemePartSize = 0; +static PtrGetThemeColor pGetThemeColor = 0; + +bool QVistaHelper::is_vista = false; +QVistaHelper::VistaState QVistaHelper::cachedVistaState = QVistaHelper::Dirty; + +/****************************************************************************** +** QVistaBackButton +*/ + +QVistaBackButton::QVistaBackButton(QWidget *widget) + : QAbstractButton(widget) +{ + setFocusPolicy(Qt::NoFocus); +} + +QSize QVistaBackButton::sizeHint() const +{ + ensurePolished(); + int width = 32, height = 32; +/* + HANDLE theme = pOpenThemeData(0, L"Navigation"); + SIZE size; + if (pGetThemePartSize(theme, 0, WIZ_NAV_BACKBUTTON, WIZ_NAV_BB_NORMAL, 0, TS_TRUE, &size) == S_OK) { + width = size.cx; + height = size.cy; + } +*/ + return QSize(width, height); +} + +void QVistaBackButton::enterEvent(QEvent *event) +{ + if (isEnabled()) + update(); + QAbstractButton::enterEvent(event); +} + +void QVistaBackButton::leaveEvent(QEvent *event) +{ + if (isEnabled()) + update(); + QAbstractButton::leaveEvent(event); +} + +void QVistaBackButton::paintEvent(QPaintEvent *) +{ + QPainter p(this); + QRect r = rect(); + HANDLE theme = pOpenThemeData(0, L"Navigation"); + //RECT rect; + RECT clipRect; + int xoffset = QWidget::mapToParent(r.topLeft()).x(); + int yoffset = QWidget::mapToParent(r.topLeft()).y(); + + clipRect.top = r.top() + yoffset; + clipRect.bottom = r.bottom() + yoffset; + clipRect.left = r.left() + xoffset; + clipRect.right = r.right() + xoffset; + + int state = WIZ_NAV_BB_NORMAL; + if (!isEnabled()) + state = WIZ_NAV_BB_DISABLED; + else if (isDown()) + state = WIZ_NAV_BB_PRESSED; + else if (underMouse()) + state = WIZ_NAV_BB_HOT; + + pDrawThemeBackground(theme, p.paintEngine()->getDC(), WIZ_NAV_BACKBUTTON, state, &clipRect, &clipRect); +} + +/****************************************************************************** +** QVistaHelper +*/ + +QVistaHelper::QVistaHelper(QWizard *wizard) + : pressed(false) + , wizard(wizard) +{ + is_vista = resolveSymbols(); + backButton_ = new QVistaBackButton(wizard); +} + +QVistaHelper::~QVistaHelper() +{ +} + +bool QVistaHelper::isCompositionEnabled() +{ + bool value = is_vista; + if (is_vista) { + HRESULT hr; + BOOL bEnabled; + + hr = pDwmIsCompositionEnabled(&bEnabled); + value = (SUCCEEDED(hr) && bEnabled); + } + return value; +} + +bool QVistaHelper::isThemeActive() +{ + return is_vista && pIsThemeActive(); +} + +QVistaHelper::VistaState QVistaHelper::vistaState() +{ + if (cachedVistaState == Dirty) + cachedVistaState = + isCompositionEnabled() ? VistaAero : isThemeActive() ? VistaBasic : Classic; + return cachedVistaState; +} + +QColor QVistaHelper::basicWindowFrameColor() +{ + DWORD rgb; + HANDLE hTheme = pOpenThemeData(qApp->desktop()->winId(), L"WINDOW"); + pGetThemeColor( + hTheme, WIZ_WP_CAPTION, WIZ_CS_ACTIVE, + wizard->isActiveWindow() ? WIZ_TMT_FILLCOLORHINT : WIZ_TMT_BORDERCOLORHINT, + &rgb); + BYTE r = GetRValue(rgb); + BYTE g = GetGValue(rgb); + BYTE b = GetBValue(rgb); + return QColor(r, g, b); +} + +bool QVistaHelper::setDWMTitleBar(TitleBarChangeType type) +{ + bool value = false; + if (vistaState() == VistaAero) { + WIZ_MARGINS mar = {0}; + if (type == NormalTitleBar) + mar.cyTopHeight = 0; + else + mar.cyTopHeight = titleBarSize() + topOffset(); + HRESULT hr = pDwmExtendFrameIntoClientArea(wizard->winId(), &mar); + value = SUCCEEDED(hr); + } + return value; +} + +void QVistaHelper::drawTitleBar(QPainter *painter) +{ + if (vistaState() == VistaAero) + drawBlackRect( + QRect(0, 0, wizard->width(), titleBarSize() + topOffset()), + painter->paintEngine()->getDC()); + + const int btnTop = backButton_->mapToParent(QPoint()).y(); + const int btnHeight = backButton_->size().height(); + const int verticalCenter = (btnTop + btnHeight / 2); + + wizard->windowIcon().paint( + painter, QRect(leftMargin(), verticalCenter - iconSize() / 2, iconSize(), iconSize())); + + const QString text = wizard->window()->windowTitle(); + const QFont font = QApplication::font("QWorkspaceTitleBar"); + const QFontMetrics fontMetrics(font); + const QRect brect = fontMetrics.boundingRect(text); + int textHeight = brect.height(); + int textWidth = brect.width(); + if (vistaState() == VistaAero) { + textHeight += 2 * glowSize(); + textWidth += 2 * glowSize(); + } + drawTitleText( + painter, text, + QRect(titleOffset(), verticalCenter - textHeight / 2, textWidth, textHeight), + painter->paintEngine()->getDC()); +} + +void QVistaHelper::setTitleBarIconAndCaptionVisible(bool visible) +{ + if (is_vista) { + WIZ_WTA_OPTIONS opt; + opt.dwFlags = WIZ_WTNCA_NODRAWICON | WIZ_WTNCA_NODRAWCAPTION; + if (visible) + opt.dwMask = 0; + else + opt.dwMask = WIZ_WTNCA_NODRAWICON | WIZ_WTNCA_NODRAWCAPTION; + pSetWindowThemeAttribute(wizard->winId(), WIZ_WTA_NONCLIENT, &opt, sizeof(WIZ_WTA_OPTIONS)); + } +} + +bool QVistaHelper::winEvent(MSG* msg, long* result) +{ + bool retval = true; + + switch (msg->message) { + case WM_NCHITTEST: { + LRESULT lResult; + pDwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult); + if (lResult == HTCLOSE || lResult == HTMAXBUTTON || lResult == HTMINBUTTON || lResult == HTHELP) + *result = lResult; + else + *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); + break; + } + case WM_NCMOUSEMOVE: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WIZ_WM_NCMOUSELEAVE: { + LRESULT lResult; + pDwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult); + *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); + break; + } + case WM_NCCALCSIZE: { + NCCALCSIZE_PARAMS* lpncsp = (NCCALCSIZE_PARAMS*)msg->lParam; + *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); + lpncsp->rgrc[0].top -= (vistaState() == VistaAero ? titleBarSize() : 0); + break; + } + default: + retval = false; + } + + return retval; +} + +void QVistaHelper::setMouseCursor(QPoint pos) +{ + if (rtTop.contains(pos)) + wizard->setCursor(Qt::SizeVerCursor); + else + wizard->setCursor(Qt::ArrowCursor); +} + +void QVistaHelper::mouseEvent(QEvent *event) +{ + switch (event->type()) { + case QEvent::MouseMove: + mouseMoveEvent(static_cast<QMouseEvent *>(event)); + break; + case QEvent::MouseButtonPress: + mousePressEvent(static_cast<QMouseEvent *>(event)); + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent(static_cast<QMouseEvent *>(event)); + break; + default: + break; + } +} + +// The following hack ensures that the titlebar is updated correctly +// when the wizard style changes to and from AeroStyle. Specifically, +// this function causes a Windows message of type WM_NCCALCSIZE to +// be triggered. +void QVistaHelper::setWindowPosHack() +{ + const int x = wizard->geometry().x(); // ignored by SWP_NOMOVE + const int y = wizard->geometry().y(); // ignored by SWP_NOMOVE + const int w = wizard->width(); + const int h = wizard->height(); + SetWindowPos(wizard->winId(), 0, x, y, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); +} + +// The following hack allows any QWidget subclass to access +// QWidgetPrivate::topData() without being declared as a +// friend by QWidget. +class QHackWidget : public QWidget +{ +public: + Q_DECLARE_PRIVATE(QWidget) + QTLWExtra* topData() { return d_func()->topData(); } +}; + +void QVistaHelper::collapseTopFrameStrut() +{ + QTLWExtra *top = ((QHackWidget *)wizard)->d_func()->topData(); + int x1, y1, x2, y2; + top->frameStrut.getCoords(&x1, &y1, &x2, &y2); + top->frameStrut.setCoords(x1, 0, x2, y2); +} + +bool QVistaHelper::handleWinEvent(MSG *message, long *result) +{ + if (message->message == WIZ_WM_THEMECHANGED || message->message == WIZ_WM_DWMCOMPOSITIONCHANGED) + cachedVistaState = Dirty; + + bool status = false; + if (wizard->wizardStyle() == QWizard::AeroStyle && vistaState() == VistaAero) { + status = winEvent(message, result); + if (message->message == WM_NCCALCSIZE) { + if (status) + collapseTopFrameStrut(); + } else if (message->message == WM_NCPAINT) { + wizard->update(); + } + } + return status; +} + +void QVistaHelper::resizeEvent(QResizeEvent * event) +{ + Q_UNUSED(event); + rtTop = QRect (0, 0, wizard->width(), frameSize()); + int height = captionSize() + topOffset(); + if (vistaState() == VistaBasic) + height -= titleBarSize(); + rtTitle = QRect (0, frameSize(), wizard->width(), height); +} + +void QVistaHelper::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter painter(wizard); + drawTitleBar(&painter); +} + +void QVistaHelper::mouseMoveEvent(QMouseEvent *event) +{ + if (wizard->windowState() & Qt::WindowMaximized) { + event->ignore(); + return; + } + + QRect rect = wizard->geometry(); + if (pressed) { + switch (change) { + case resizeTop: + { + const int dy = event->pos().y() - pressedPos.y(); + if ((dy > 0 && rect.height() > wizard->minimumHeight()) + || (dy < 0 && rect.height() < wizard->maximumHeight())) + rect.setTop(rect.top() + dy); + } + break; + case movePosition: { + QPoint newPos = event->pos() - pressedPos; + rect.moveLeft(rect.left() + newPos.x()); + rect.moveTop(rect.top() + newPos.y()); + break; } + default: + break; + } + wizard->setGeometry(rect); + + } else if (vistaState() == VistaAero) { + setMouseCursor(event->pos()); + } + event->ignore(); +} + +void QVistaHelper::mousePressEvent(QMouseEvent *event) +{ + change = noChange; + + if (wizard->windowState() & Qt::WindowMaximized) { + event->ignore(); + return; + } + + if (rtTitle.contains(event->pos())) { + change = movePosition; + } else if (rtTop.contains(event->pos())) + change = (vistaState() == VistaAero) ? resizeTop : movePosition; + + if (change != noChange) { + if (vistaState() == VistaAero) + setMouseCursor(event->pos()); + pressed = true; + pressedPos = event->pos(); + } else { + event->ignore(); + } +} + +void QVistaHelper::mouseReleaseEvent(QMouseEvent *event) +{ + change = noChange; + if (pressed) { + pressed = false; + wizard->releaseMouse(); + if (vistaState() == VistaAero) + setMouseCursor(event->pos()); + } + event->ignore(); +} + +bool QVistaHelper::eventFilter(QObject *obj, QEvent *event) +{ + if (obj != wizard) + return QObject::eventFilter(obj, event); + + if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); + long result; + MSG msg; + msg.message = WM_NCHITTEST; + msg.wParam = 0; + msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY()); + msg.hwnd = wizard->winId(); + winEvent(&msg, &result); + msg.wParam = result; + msg.message = WM_NCMOUSEMOVE; + winEvent(&msg, &result); + } else if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); + long result; + MSG msg; + msg.message = WM_NCHITTEST; + msg.wParam = 0; + msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY()); + msg.hwnd = wizard->winId(); + winEvent(&msg, &result); + msg.wParam = result; + msg.message = WM_NCLBUTTONDOWN; + winEvent(&msg, &result); + } else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); + long result; + MSG msg; + msg.message = WM_NCHITTEST; + msg.wParam = 0; + msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY()); + msg.hwnd = wizard->winId(); + winEvent(&msg, &result); + msg.wParam = result; + msg.message = WM_NCLBUTTONUP; + winEvent(&msg, &result); + } + + return false; +} + +HFONT QVistaHelper::getCaptionFont(HANDLE hTheme) +{ + LOGFONT lf = {0}; + + if (!hTheme) + pGetThemeSysFont(hTheme, WIZ_TMT_CAPTIONFONT, &lf); + else + { + NONCLIENTMETRICS ncm = {sizeof(NONCLIENTMETRICS)}; + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, false); + lf = ncm.lfMessageFont; + } + return CreateFontIndirect(&lf); +} + +bool QVistaHelper::drawTitleText(QPainter *painter, const QString &text, const QRect &rect, HDC hdc) +{ + bool value = false; + if (vistaState() == VistaAero) { + HANDLE hTheme = pOpenThemeData(qApp->desktop()->winId(), L"WINDOW"); + if (!hTheme) return false; + // Set up a memory DC and bitmap that we'll draw into + HDC dcMem; + HBITMAP bmp; + BITMAPINFO dib = {0}; + dcMem = CreateCompatibleDC(hdc); + + dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + dib.bmiHeader.biWidth = rect.width(); + dib.bmiHeader.biHeight = -rect.height(); + dib.bmiHeader.biPlanes = 1; + dib.bmiHeader.biBitCount = 32; + dib.bmiHeader.biCompression = BI_RGB; + + bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0); + + // Set up the DC + HFONT hCaptionFont = getCaptionFont(hTheme); + HBITMAP hOldBmp = (HBITMAP)SelectObject(dcMem, (HGDIOBJ) bmp); + HFONT hOldFont = (HFONT)SelectObject(dcMem, (HGDIOBJ) hCaptionFont); + + // Draw the text! + WIZ_DTTOPTS dto = { sizeof(WIZ_DTTOPTS) }; + const UINT uFormat = WIZ_DT_SINGLELINE|WIZ_DT_CENTER|WIZ_DT_VCENTER|WIZ_DT_NOPREFIX; + RECT rctext ={0,0, rect.width(), rect.height()}; + + dto.dwFlags = WIZ_DTT_COMPOSITED|WIZ_DTT_GLOWSIZE; + dto.iGlowSize = glowSize(); + + pDrawThemeTextEx(hTheme, dcMem, 0, 0, (LPCWSTR)text.utf16(), -1, uFormat, &rctext, &dto ); + BitBlt(hdc, rect.left(), rect.top(), rect.width(), rect.height(), dcMem, 0, 0, SRCCOPY); + SelectObject(dcMem, (HGDIOBJ) hOldBmp); + SelectObject(dcMem, (HGDIOBJ) hOldFont); + DeleteObject(bmp); + DeleteObject(hCaptionFont); + DeleteDC(dcMem); + //ReleaseDC(hwnd, hdc); + } else if (vistaState() == VistaBasic) { + painter->drawText(rect, text); + } + return value; +} + +bool QVistaHelper::drawBlackRect(const QRect &rect, HDC hdc) +{ + bool value = false; + if (vistaState() == VistaAero) { + // Set up a memory DC and bitmap that we'll draw into + HDC dcMem; + HBITMAP bmp; + BITMAPINFO dib = {0}; + dcMem = CreateCompatibleDC(hdc); + + dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + dib.bmiHeader.biWidth = rect.width(); + dib.bmiHeader.biHeight = -rect.height(); + dib.bmiHeader.biPlanes = 1; + dib.bmiHeader.biBitCount = 32; + dib.bmiHeader.biCompression = BI_RGB; + + bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0); + HBITMAP hOldBmp = (HBITMAP)SelectObject(dcMem, (HGDIOBJ) bmp); + + BitBlt(hdc, rect.left(), rect.top(), rect.width(), rect.height(), dcMem, 0, 0, SRCCOPY); + SelectObject(dcMem, (HGDIOBJ) hOldBmp); + + DeleteObject(bmp); + DeleteDC(dcMem); + } + return value; +} + +bool QVistaHelper::resolveSymbols() +{ + static bool tried = false; + if (!tried) { + tried = true; + QLibrary dwmLib(QString::fromAscii("dwmapi")); + pDwmIsCompositionEnabled = + (PtrDwmIsCompositionEnabled)dwmLib.resolve("DwmIsCompositionEnabled"); + if (pDwmIsCompositionEnabled) { + pDwmDefWindowProc = (PtrDwmDefWindowProc)dwmLib.resolve("DwmDefWindowProc"); + pDwmExtendFrameIntoClientArea = + (PtrDwmExtendFrameIntoClientArea)dwmLib.resolve("DwmExtendFrameIntoClientArea"); + } + QLibrary themeLib(QString::fromAscii("uxtheme")); + pIsAppThemed = (PtrIsAppThemed)themeLib.resolve("IsAppThemed"); + if (pIsAppThemed) { + pDrawThemeBackground = (PtrDrawThemeBackground)themeLib.resolve("DrawThemeBackground"); + pGetThemePartSize = (PtrGetThemePartSize)themeLib.resolve("GetThemePartSize"); + pGetThemeColor = (PtrGetThemeColor)themeLib.resolve("GetThemeColor"); + pIsThemeActive = (PtrIsThemeActive)themeLib.resolve("IsThemeActive"); + pOpenThemeData = (PtrOpenThemeData)themeLib.resolve("OpenThemeData"); + pCloseThemeData = (PtrCloseThemeData)themeLib.resolve("CloseThemeData"); + pGetThemeSysFont = (PtrGetThemeSysFont)themeLib.resolve("GetThemeSysFont"); + pDrawThemeTextEx = (PtrDrawThemeTextEx)themeLib.resolve("DrawThemeTextEx"); + pSetWindowThemeAttribute = (PtrSetWindowThemeAttribute)themeLib.resolve("SetWindowThemeAttribute"); + } + } + + return ( + pDwmIsCompositionEnabled != 0 + && pDwmDefWindowProc != 0 + && pDwmExtendFrameIntoClientArea != 0 + && pIsAppThemed != 0 + && pDrawThemeBackground != 0 + && pGetThemePartSize != 0 + && pGetThemeColor != 0 + && pIsThemeActive != 0 + && pOpenThemeData != 0 + && pCloseThemeData != 0 + && pGetThemeSysFont != 0 + && pDrawThemeTextEx != 0 + && pSetWindowThemeAttribute != 0 + ); +} + +int QVistaHelper::titleOffset() +{ + int iconOffset = wizard ->windowIcon().isNull() ? 0 : iconSize() + padding(); + return leftMargin() + iconOffset; +} + +QT_END_NAMESPACE + +#endif // QT_NO_STYLE_WINDOWSVISTA + +#endif // QT_NO_WIZARD diff --git a/src/gui/dialogs/qwizard_win_p.h b/src/gui/dialogs/qwizard_win_p.h new file mode 100644 index 0000000000..cbb3b17f62 --- /dev/null +++ b/src/gui/dialogs/qwizard_win_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWIZARD_WIN_P_H +#define QWIZARD_WIN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QT_NO_WIZARD +#ifndef QT_NO_STYLE_WINDOWSVISTA + +#include <windows.h> +#include <qobject.h> +#include <qwidget.h> +#include <qabstractbutton.h> +#include <QtGui/private/qwidget_p.h> + +QT_BEGIN_NAMESPACE + +class QVistaBackButton : public QAbstractButton +{ +public: + QVistaBackButton(QWidget *widget); + + QSize sizeHint() const; + inline QSize minimumSizeHint() const + { return sizeHint(); } + + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + void paintEvent(QPaintEvent *event); +}; + +class QWizard; + +class QVistaHelper : public QObject +{ + Q_OBJECT +public: + QVistaHelper(QWizard *wizard); + ~QVistaHelper(); + enum TitleBarChangeType { NormalTitleBar, ExtendedTitleBar }; + bool setDWMTitleBar(TitleBarChangeType type); + void setTitleBarIconAndCaptionVisible(bool visible); + void mouseEvent(QEvent *event); + bool handleWinEvent(MSG *message, long *result); + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + QVistaBackButton *backButton() const { return backButton_; } + void setWindowPosHack(); + QColor basicWindowFrameColor(); + enum VistaState { VistaAero, VistaBasic, Classic, Dirty }; + static VistaState vistaState(); + static int titleBarSize() { return frameSize() + captionSize(); } + static int topPadding() { return 8; } + static int topOffset() { return titleBarSize() + (vistaState() == VistaAero ? 13 : 3); } + +private: + static HFONT getCaptionFont(HANDLE hTheme); + bool drawTitleText(QPainter *painter, const QString &text, const QRect &rect, HDC hdc); + static bool drawBlackRect(const QRect &rect, HDC hdc); + + static int frameSize() { return GetSystemMetrics(SM_CYSIZEFRAME); } + static int captionSize() { return GetSystemMetrics(SM_CYCAPTION); } + + static int backButtonSize() { return 31; } // ### should be queried from back button itself + static int iconSize() { return 16; } // Standard Aero + static int padding() { return 7; } // Standard Aero + static int leftMargin() { return backButtonSize() + padding(); } + static int glowSize() { return 10; } + + int titleOffset(); + bool resolveSymbols(); + void drawTitleBar(QPainter *painter); + void setMouseCursor(QPoint pos); + void collapseTopFrameStrut(); + bool winEvent(MSG *message, long *result); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + bool eventFilter(QObject *obj, QEvent *event); + + static bool is_vista; + static VistaState cachedVistaState; + static bool isCompositionEnabled(); + static bool isThemeActive(); + enum Changes { resizeTop, movePosition, noChange } change; + QPoint pressedPos; + bool pressed; + QRect rtTop; + QRect rtTitle; + QWizard *wizard; + QVistaBackButton *backButton_; +}; + + +QT_END_NAMESPACE + +#endif // QT_NO_STYLE_WINDOWSVISTA +#endif // QT_NO_WIZARD +#endif // QWIZARD_WIN_P_H |