From e5fcad302d86d316390c6b0f62759a067313e8a9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 23 Mar 2009 10:18:55 +0100 Subject: Long live Qt 4.5! --- src/qt3support/dialogs/dialogs.pri | 16 + src/qt3support/dialogs/q3filedialog.cpp | 6203 +++++++++++++++++++++++++++ src/qt3support/dialogs/q3filedialog.h | 346 ++ src/qt3support/dialogs/q3filedialog_mac.cpp | 569 +++ src/qt3support/dialogs/q3filedialog_win.cpp | 749 ++++ src/qt3support/dialogs/q3progressdialog.cpp | 850 ++++ src/qt3support/dialogs/q3progressdialog.h | 149 + src/qt3support/dialogs/q3tabdialog.cpp | 1076 +++++ src/qt3support/dialogs/q3tabdialog.h | 142 + src/qt3support/dialogs/q3wizard.cpp | 906 ++++ src/qt3support/dialogs/q3wizard.h | 141 + 11 files changed, 11147 insertions(+) create mode 100644 src/qt3support/dialogs/dialogs.pri create mode 100644 src/qt3support/dialogs/q3filedialog.cpp create mode 100644 src/qt3support/dialogs/q3filedialog.h create mode 100644 src/qt3support/dialogs/q3filedialog_mac.cpp create mode 100644 src/qt3support/dialogs/q3filedialog_win.cpp create mode 100644 src/qt3support/dialogs/q3progressdialog.cpp create mode 100644 src/qt3support/dialogs/q3progressdialog.h create mode 100644 src/qt3support/dialogs/q3tabdialog.cpp create mode 100644 src/qt3support/dialogs/q3tabdialog.h create mode 100644 src/qt3support/dialogs/q3wizard.cpp create mode 100644 src/qt3support/dialogs/q3wizard.h (limited to 'src/qt3support/dialogs') diff --git a/src/qt3support/dialogs/dialogs.pri b/src/qt3support/dialogs/dialogs.pri new file mode 100644 index 0000000000..7ea5fb27fa --- /dev/null +++ b/src/qt3support/dialogs/dialogs.pri @@ -0,0 +1,16 @@ +# Qt compat module + +HEADERS += \ + dialogs/q3filedialog.h \ + dialogs/q3tabdialog.h \ + dialogs/q3progressdialog.h \ + dialogs/q3wizard.h + +SOURCES += \ + dialogs/q3filedialog.cpp \ + dialogs/q3progressdialog.cpp \ + dialogs/q3tabdialog.cpp \ + dialogs/q3wizard.cpp + +win32:SOURCES += dialogs/q3filedialog_win.cpp +!embedded:mac:SOURCES += dialogs/q3filedialog_mac.cpp diff --git a/src/qt3support/dialogs/q3filedialog.cpp b/src/qt3support/dialogs/q3filedialog.cpp new file mode 100644 index 0000000000..4874b6d940 --- /dev/null +++ b/src/qt3support/dialogs/q3filedialog.cpp @@ -0,0 +1,6203 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 "q3filedialog.h" + +#ifndef QT_NO_FILEDIALOG + +#include "private/qapplication_p.h" +#include "q3buttongroup.h" +#include "q3header.h" +#include "q3listview.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "qcheckbox.h" +#include "q3cleanuphandler.h" +#include "qcombobox.h" +#include "q3combobox.h" +#include "q3cstring.h" +#include "qcursor.h" +#include "qdesktopwidget.h" +#include "q3dragobject.h" +#include "qevent.h" +#include "qfile.h" +#include "qlabel.h" +#include "qlayout.h" +#include "qlibrary.h" +#include "qlineedit.h" +#include "q3listbox.h" +#include "qmap.h" +#include "qmessagebox.h" +#include "qmime.h" +#include "qpainter.h" +#include "qpointer.h" +#include "q3popupmenu.h" +#include "q3progressbar.h" +#include "q3ptrvector.h" +#include "qpushbutton.h" +#include "qregexp.h" +#include "qsplitter.h" +#include "q3strlist.h" +#include "qstyle.h" +#include "qtimer.h" +#include "qtoolbutton.h" +#include "qtooltip.h" +#include "q3widgetstack.h" +#include "q3urloperator.h" +#include "q3vbox.h" +#include "qurlinfo.h" + +#ifdef Q_WS_WIN +#ifndef QT_NO_THREAD +# include "qwindowsstyle.h" +# include "private/qmutexpool_p.h" +#endif +#endif // Q_WS_WIN + +#ifndef Q_OS_WINCE +#include +#else +#include +#endif // Q_OS_WINCE +#include +#include +#include + +#ifdef Q_WS_MAC +#include "qmacstyle_mac.h" +#include "private/qt_mac_p.h" +#include "private/qunicodetables_p.h" +#undef check +#endif + +#if defined(Q_OS_OPENBSD) +#include +#endif + +QT_BEGIN_NAMESPACE + +/* XPM */ +static const char * const start_xpm[]={ + "16 15 8 1", + "a c #cec6bd", + "# c #000000", + "e c #ffff00", + "b c #999999", + "f c #cccccc", + "d c #dcdcdc", + "c c #ffffff", + ". c None", + ".....######aaaaa", + "...bb#cccc##aaaa", + "..bcc#cccc#d#aaa", + ".bcef#cccc#dd#aa", + ".bcfe#cccc#####a", + ".bcef#ccccccccc#", + "bbbbbbbbbbbbccc#", + "bccccccccccbbcc#", + "bcefefefefee#bc#", + ".bcefefefefef#c#", + ".bcfefefefefe#c#", + "..bcfefefefeeb##", + "..bbbbbbbbbbbbb#", + "...#############", + "................"}; + +/* XPM */ +static const char * const end_xpm[]={ + "16 15 9 1", + "d c #a0a0a0", + "c c #c3c3c3", + "# c #cec6bd", + ". c #000000", + "f c #ffff00", + "e c #999999", + "g c #cccccc", + "b c #ffffff", + "a c None", + "......####aaaaaa", + ".bbbb..###aaaaaa", + ".bbbb.c.##aaaaaa", + ".bbbb....ddeeeea", + ".bbbbbbb.bbbbbe.", + ".bbbbbbb.bcfgfe.", + "eeeeeeeeeeeeefe.", + "ebbbbbbbbbbeege.", + "ebfgfgfgfgff.ee.", + "aebfgfgfgfgfg.e.", + "aebgfgfgfgfgf.e.", + "aaebgfgfgfgffe..", + "aaeeeeeeeeeeeee.", + "aaa.............", + "aaaaaaaaaaaaaaaa"}; + +/* XPM */ +static const char* const open_xpm[]={ + "16 16 6 1", + ". c None", + "b c #ffff00", + "d c #000000", + "* c #999999", + "c c #cccccc", + "a c #ffffff", + "................", + "................", + "...*****........", + "..*aaaaa*.......", + ".*abcbcba******.", + ".*acbcbcaaaaaa*d", + ".*abcbcbcbcbcb*d", + "*************b*d", + "*aaaaaaaaaa**c*d", + "*abcbcbcbcbbd**d", + ".*abcbcbcbcbcd*d", + ".*acbcbcbcbcbd*d", + "..*acbcbcbcbb*dd", + "..*************d", + "...ddddddddddddd", + "................"}; + +/* XPM */ +static const char * const link_dir_xpm[]={ + "16 16 10 1", + "h c #808080", + "g c #a0a0a0", + "d c #000000", + "b c #ffff00", + "f c #303030", + "# c #999999", + "a c #cccccc", + "e c #585858", + "c c #ffffff", + ". c None", + "................", + "................", + "..#####.........", + ".#ababa#........", + "#abababa######..", + "#cccccccccccc#d.", + "#cbababababab#d.", + "#cabababababa#d.", + "#cbababdddddddd.", + "#cababadccccccd.", + "#cbababdcececcd.", + "#cababadcefdfcd.", + "#cbababdccgdhcd.", + "#######dccchccd.", + ".dddddddddddddd.", + "................"}; + +/* XPM */ +static const char * const link_file_xpm[]={ + "16 16 10 1", + "h c #808080", + "g c #a0a0a0", + "d c #c3c3c3", + ". c #7f7f7f", + "c c #000000", + "b c #bfbfbf", + "f c #303030", + "e c #585858", + "a c #ffffff", + "# c None", + "################", + "..........######", + ".aaaaaaaab.#####", + ".aaaaaaaaba.####", + ".aaaaaaaacccc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaadc###", + ".aaaaaaaaaadc###", + ".aaaacccccccc###", + ".aaaacaaaaaac###", + ".aaaacaeaeaac###", + ".aaaacaefcfac###", + ".aaaacaagchac###", + ".ddddcaaahaac###", + "ccccccccccccc###"}; + +/* XPM */ +static const char* const file_xpm[]={ + "16 16 5 1", + ". c #7f7f7f", + "# c None", + "c c #000000", + "b c #bfbfbf", + "a c #ffffff", + "################", + "..........######", + ".aaaaaaaab.#####", + ".aaaaaaaaba.####", + ".aaaaaaaacccc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".aaaaaaaaaabc###", + ".bbbbbbbbbbbc###", + "ccccccccccccc###"}; + +/* XPM */ +static const char * const closed_xpm[]={ + "16 16 6 1", + ". c None", + "b c #ffff00", + "d c #000000", + "* c #999999", + "a c #cccccc", + "c c #ffffff", + "................", + "................", + "..*****.........", + ".*ababa*........", + "*abababa******..", + "*cccccccccccc*d.", + "*cbababababab*d.", + "*cabababababa*d.", + "*cbababababab*d.", + "*cabababababa*d.", + "*cbababababab*d.", + "*cabababababa*d.", + "*cbababababab*d.", + "**************d.", + ".dddddddddddddd.", + "................"}; + + +/* XPM */ +static const char* const cdtoparent_xpm[]={ + "15 13 3 1", + ". c None", + "* c #000000", + "a c #ffff99", + "..*****........", + ".*aaaaa*.......", + "***************", + "*aaaaaaaaaaaaa*", + "*aaaa*aaaaaaaa*", + "*aaa***aaaaaaa*", + "*aa*****aaaaaa*", + "*aaaa*aaaaaaaa*", + "*aaaa*aaaaaaaa*", + "*aaaa******aaa*", + "*aaaaaaaaaaaaa*", + "*aaaaaaaaaaaaa*", + "***************"}; + + +/* XPM */ +static const char* const newfolder_xpm[] = { + "15 14 4 1", + " c None", + ". c #000000", + "+ c #FFFF00", + "@ c #FFFFFF", + " . ", + " ", + " . ", + " . . ", + " .... . . . ", + " .+@+@. . . ", + ".......... . .", + ".@+@+@+@+@.. ", + ".+@+@+@+@+. . ", + ".@+@+@+@+@. . ", + ".+@+@+@+@+. ", + ".@+@+@+@+@. ", + ".+@+@+@+@+. ", + "........... "}; + +/* XPM */ +static const char* const detailedview_xpm[]={ + "14 11 3 1", + ". c None", + "* c #000000", + "a c #000099", + ".****.***.***.", + "..............", + "aaaaaaaaaaaaaa", + "..............", + ".****.***.***.", + "..............", + ".****.***.***.", + "..............", + ".****.***.***.", + "..............", + ".****.***.***."}; + +/* XPM */ +static const char* const previewinfoview_xpm[]={ + "13 13 4 1", + ". c #00007f", + "a c black", + "# c #cec6bd", + "b c #000000", + "..#####aaaaaa", + ".#.#bb#a#####", + "...####a#bbb#", + "#######a#####", + "#######a#bb##", + "..#####a#####", + ".#.#bb#a#bbb#", + "...####a#####", + "#######a#bb##", + "#######a#####", + "..#####a#bbb#", + ".#.#bb#a#####", + "...####aaaaaa"}; + +/* XPM */ +static const char* const previewcontentsview_xpm[]={ + "14 13 5 1", + ". c #00007f", + "a c black", + "c c #7f007f", + "# c #cec6bd", + "b c #000000", + "..#####aaaaaaa", + ".#.#bb#a#####a", + "...####a#ccc#a", + "#######a#ccc#a", + "#######a#####a", + "..#####a#bbb#a", + ".#.#bb#a#####a", + "...####a#bbb#a", + "#######a#####a", + "#######a#bbb#a", + "..#####a#####a", + ".#.#bb#a#####a", + "...####aaaaaaa"}; + +/* XPM */ +static const char* const mclistview_xpm[]={ + "15 11 4 1", + "* c None", + "b c #000000", + ". c #000099", + "a c #ffffff", + "...*****...****", + ".a.*bbb*.a.*bbb", + "...*****...****", + "***************", + "...*****...****", + ".a.*bbb*.a.*bbb", + "...*****...****", + "***************", + "...*****...****", + ".a.*bbb*.a.*bbb", + "...*****...****"}; + +/* XPM */ +static const char * const back_xpm [] = { + "13 11 3 1", + "a c #00ffff", + "# c #000000", + ". c None", + ".....#.......", + "....##.......", + "...#a#.......", + "..#aa########", + ".#aaaaaaaaaa#", + "#aaaaaaaaaaa#", + ".#aaaaaaaaaa#", + "..#aa########", + "...#a#.......", + "....##.......", + ".....#......."}; + +static QPixmap * openFolderIcon = 0; +static QPixmap * closedFolderIcon = 0; +static QPixmap * detailViewIcon = 0; +static QPixmap * multiColumnListViewIcon = 0; +static QPixmap * cdToParentIcon = 0; +static QPixmap * newFolderIcon = 0; +static QPixmap * fifteenTransparentPixels = 0; +static QPixmap * symLinkDirIcon = 0; +static QPixmap * symLinkFileIcon = 0; +static QPixmap * fileIcon = 0; +static QPixmap * startCopyIcon = 0; +static QPixmap * endCopyIcon = 0; +static QPixmap * previewContentsViewIcon = 0; +static QPixmap * previewInfoViewIcon = 0; +static QPixmap *goBackIcon = 0; +static Q3FileIconProvider * fileIconProvider = 0; +static int lastWidth = 0; +static int lastHeight = 0; +static QString * workingDirectory = 0; + +static bool bShowHiddenFiles = false; +static int sortFilesBy = (int)QDir::Name; +static bool sortAscending = true; +static bool detailViewMode = false; + +static Q3CleanupHandler qfd_cleanup_pixmap; +static Q3CleanupHandler qfd_cleanup_string; + +static QString toRootIfNotExists( const QString &path ) +{ + if ( !path.isEmpty() ) + return path; + + QFileInfoList drives = QDir::drives(); + Q_ASSERT( !drives.isEmpty() ); + return drives.first().filePath(); +} + +static bool isDirectoryMode(int m) +{ + return m == Q3FileDialog::Directory || m == Q3FileDialog::DirectoryOnly; +} + +static void updateLastSize(Q3FileDialog *that) +{ + int extWidth = 0; + int extHeight = 0; + if (that->extension() && that->extension()->isVisible()) { + if (that->orientation() == Qt::Vertical) + extHeight = that->extension()->height(); + else + extWidth = that->extension()->width(); + } + lastWidth = that->width() - extWidth; + lastHeight = that->height() - extHeight; +} + +// 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 getEsistingDirectory(), etc. +#if defined(Q_WS_WIN) + +typedef UINT (WINAPI *PtrExtractIconEx)(LPCTSTR,int,HICON*,HICON*,UINT); +static PtrExtractIconEx ptrExtractIconEx = 0; + +static void resolveLibs() +{ +#ifndef Q_OS_WINCE + 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 (qt_winUnicode()) { + QLibrary lib(QLatin1String("shell32")); + ptrExtractIconEx = (PtrExtractIconEx) lib.resolve("ExtractIconExW"); + } + } +#endif +} +#ifdef Q_OS_WINCE +#define PtrExtractIconEx ExtractIconEx +#endif + +class QWindowsIconProvider : public Q3FileIconProvider +{ +public: + QWindowsIconProvider(QObject *parent=0, const char *name=0); + ~QWindowsIconProvider(); + + const QPixmap * pixmap(const QFileInfo &fi); + +private: + QPixmap defaultFolder; + QPixmap defaultFile; + QPixmap defaultExe; + QPixmap pix; + int pixw, pixh; + QMap< QString, QPixmap > cache; + +}; +#endif + +static void makeVariables() { + if (!openFolderIcon) { + workingDirectory = new QString(toRootIfNotExists( QDir::currentDirPath() )); + qfd_cleanup_string.add(&workingDirectory); + + openFolderIcon = new QPixmap((const char **)open_xpm); + qfd_cleanup_pixmap.add(&openFolderIcon); + symLinkDirIcon = new QPixmap((const char **)link_dir_xpm); + qfd_cleanup_pixmap.add(&symLinkDirIcon); + symLinkFileIcon = new QPixmap((const char **)link_file_xpm); + qfd_cleanup_pixmap.add(&symLinkFileIcon); + fileIcon = new QPixmap((const char **)file_xpm); + qfd_cleanup_pixmap.add(&fileIcon); + closedFolderIcon = new QPixmap((const char **)closed_xpm); + qfd_cleanup_pixmap.add(&closedFolderIcon); + detailViewIcon = new QPixmap((const char **)detailedview_xpm); + qfd_cleanup_pixmap.add(&detailViewIcon); + multiColumnListViewIcon = new QPixmap((const char **)mclistview_xpm); + qfd_cleanup_pixmap.add(&multiColumnListViewIcon); + cdToParentIcon = new QPixmap((const char **)cdtoparent_xpm); + qfd_cleanup_pixmap.add(&cdToParentIcon); + newFolderIcon = new QPixmap((const char **)newfolder_xpm); + qfd_cleanup_pixmap.add(&newFolderIcon); + previewInfoViewIcon + = new QPixmap((const char **)previewinfoview_xpm); + qfd_cleanup_pixmap.add(&previewInfoViewIcon); + previewContentsViewIcon + = new QPixmap((const char **)previewcontentsview_xpm); + qfd_cleanup_pixmap.add(&previewContentsViewIcon); + startCopyIcon = new QPixmap((const char **)start_xpm); + qfd_cleanup_pixmap.add(&startCopyIcon); + endCopyIcon = new QPixmap((const char **)end_xpm); + qfd_cleanup_pixmap.add(&endCopyIcon); + goBackIcon = new QPixmap((const char **)back_xpm); + qfd_cleanup_pixmap.add(&goBackIcon); + fifteenTransparentPixels = new QPixmap(closedFolderIcon->width(), 1); + qfd_cleanup_pixmap.add(&fifteenTransparentPixels); + QBitmap m(fifteenTransparentPixels->width(), 1); + m.fill(Qt::color0); + fifteenTransparentPixels->setMask(m); + bShowHiddenFiles = false; + sortFilesBy = (int)QDir::Name; + detailViewMode = false; +#if defined(Q_WS_WIN) + if (!fileIconProvider) + fileIconProvider = new QWindowsIconProvider(qApp); +#endif + } +} + +/****************************************************************** + * + * Definitions of view classes + * + ******************************************************************/ + +class QRenameEdit : public QLineEdit +{ + Q_OBJECT + +public: + QRenameEdit(QWidget *parent); + +protected: + void keyPressEvent(QKeyEvent *e); + void focusOutEvent(QFocusEvent *e); + +signals: + void cancelRename(); + void doRename(); + +private slots: + void slotReturnPressed(); + +private: + bool doRenameAlreadyEmitted; +}; + +QRenameEdit::QRenameEdit(QWidget *parent) + : QLineEdit(parent, "qt_rename_edit"), doRenameAlreadyEmitted(false) +{ + connect(this, SIGNAL(returnPressed()), SLOT(slotReturnPressed())); +} + +class QFileListBox : public Q3ListBox +{ + friend class Q3FileDialog; + + Q_OBJECT + +private: + QFileListBox(QWidget *parent, Q3FileDialog *d); + + void clear(); + void show(); + void startRename(bool check = true); + void viewportMousePressEvent(QMouseEvent *e); + void viewportMouseReleaseEvent(QMouseEvent *e); + void viewportMouseDoubleClickEvent(QMouseEvent *e); + void viewportMouseMoveEvent(QMouseEvent *e); +#ifndef QT_NO_DRAGANDDROP + void viewportDragEnterEvent(QDragEnterEvent *e); + void viewportDragMoveEvent(QDragMoveEvent *e); + void viewportDragLeaveEvent(QDragLeaveEvent *e); + void viewportDropEvent(QDropEvent *e); + bool acceptDrop(const QPoint &pnt, QWidget *source); + void setCurrentDropItem(const QPoint &pnt); +#endif + void keyPressEvent(QKeyEvent *e); + +private slots: + void rename(); + void cancelRename(); + void doubleClickTimeout(); + void changeDirDuringDrag(); + void dragObjDestroyed(); + void contentsMoved(int, int); + +private: + QRenameEdit *lined; + Q3FileDialog *filedialog; + bool renaming; + QTimer* renameTimer; + Q3ListBoxItem *renameItem, *dragItem; + QPoint pressPos, oldDragPos; + bool mousePressed; + int urls; + QString startDragDir; + Q3ListBoxItem *currDropItem; + QTimer *changeDirTimer; + bool firstMousePressEvent; + Q3UrlOperator startDragUrl; + +}; + + +class Q3FileDialogQFileListView : public Q3ListView +{ + Q_OBJECT + +public: + Q3FileDialogQFileListView(QWidget *parent, Q3FileDialog *d); + + void clear(); + void startRename(bool check = true); + void setSorting(int column, bool increasing = true); + + QRenameEdit *lined; + bool renaming; + Q3ListViewItem *renameItem; + +private: + void viewportMousePressEvent(QMouseEvent *e); + void viewportMouseDoubleClickEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void viewportMouseReleaseEvent(QMouseEvent *e); + void viewportMouseMoveEvent(QMouseEvent *e); +#ifndef QT_NO_DRAGANDDROP + void viewportDragEnterEvent(QDragEnterEvent *e); + void viewportDragMoveEvent(QDragMoveEvent *e); + void viewportDragLeaveEvent(QDragLeaveEvent *e); + void viewportDropEvent(QDropEvent *e); + bool acceptDrop(const QPoint &pnt, QWidget *source); + void setCurrentDropItem(const QPoint &pnt); +#endif + +private slots: + void rename(); + void cancelRename(); + void changeSortColumn2(int column); + void doubleClickTimeout(); + void changeDirDuringDrag(); + void dragObjDestroyed(); + void contentsMoved(int, int); + +private: + Q3FileDialog *filedialog; + QTimer* renameTimer; + QPoint pressPos, oldDragPos; + bool mousePressed; + int urls; + QString startDragDir; + Q3ListViewItem *currDropItem, *dragItem; + QTimer *changeDirTimer; + bool firstMousePressEvent; + bool ascending; + int sortcolumn; + Q3UrlOperator startDragUrl; + +}; + +/**************************************************************************** + * + * Classes for copy progress dialog + * + ****************************************************************************/ + +class QFDProgressAnimation : public QWidget +{ + Q_OBJECT + +public: + QFDProgressAnimation(QWidget *parent); + void start(); + +private slots: + void next(); + +protected: + void paintEvent(QPaintEvent *e); + +private: + int step; + QTimer *timer; + +}; + +QFDProgressAnimation::QFDProgressAnimation(QWidget *parent) + : QWidget(parent, "qt_progressanimation") +{ + setFixedSize(300, 50); + step = -1; + next(); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), + this, SLOT(next())); +} + +void QFDProgressAnimation::start() +{ + timer->start(150, false); +} + +void QFDProgressAnimation::next() +{ + ++step; + if (step > 10) + step = 0; + repaint(); +} + +void QFDProgressAnimation::paintEvent(QPaintEvent *) +{ + erase(); + + QPainter p; + p.begin(this); + if (step == 0) { + p.drawPixmap(5, (height() - startCopyIcon->height()) / 2, + *startCopyIcon); + p.drawPixmap(width() - 5 - openFolderIcon->width(), + (height() - openFolderIcon->height()) / 2 , *openFolderIcon); + } else if (step == 10) { + p.drawPixmap(5, (height() - openFolderIcon->height()) / 2, + *openFolderIcon); + p.drawPixmap(width() - 5 - endCopyIcon->width(), + (height() - endCopyIcon->height()) / 2 , *endCopyIcon); + } else { + p.drawPixmap(5, (height() - openFolderIcon->height()) / 2, + *openFolderIcon); + p.drawPixmap(width() - 5 - openFolderIcon->width(), + (height() - openFolderIcon->height()) / 2 , *openFolderIcon); + int x = 10 + openFolderIcon->width(); + int w = width() - 2 * x; + int s = w / 9; + p.drawPixmap(x + s * step, (height() - fileIcon->height()) / 2 - fileIcon->height(), + *fileIcon); + } +} + + +class QFDProgressDialog : public QDialog +{ + Q_OBJECT + +public: + QFDProgressDialog(QWidget *parent, const QString &fn, int steps); + + void setReadProgress(int p); + void setWriteProgress(int p); + void setWriteLabel(const QString &s); + +signals: + void cancelled(); + +private: + Q3ProgressBar *readBar; + Q3ProgressBar *writeBar; + QLabel *writeLabel; + QFDProgressAnimation *animation; + +}; + +QFDProgressDialog::QFDProgressDialog(QWidget *parent, const QString &fn, int steps) + : QDialog(parent, "", true) +{ + setWindowTitle(Q3FileDialog::tr("Copy or Move a File")); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setSpacing(5); + layout->setMargin(5); + + animation = new QFDProgressAnimation(this); + layout->addWidget(animation); + + layout->addWidget(new QLabel(Q3FileDialog::tr("Read: %1").arg(fn), + this, "qt_read_lbl")); + readBar = new Q3ProgressBar(steps, this, "qt_readbar"); + readBar->reset(); + readBar->setProgress(0); + layout->addWidget(readBar); + writeLabel = new QLabel(Q3FileDialog::tr("Write: %1").arg(QString()), + this, "qt_write_lbl"); + layout->addWidget(writeLabel); + writeBar = new Q3ProgressBar(steps, this, "qt_writebar"); + writeBar->reset(); + writeBar->setProgress(0); + layout->addWidget(writeBar); + + QPushButton *b = new QPushButton(Q3FileDialog::tr("Cancel"), this, + "qt_cancel_btn"); + b->setFixedSize(b->sizeHint()); + layout->addWidget(b); + connect(b, SIGNAL(clicked()), + this, SIGNAL(cancelled())); + + animation->start(); +} + +void QFDProgressDialog::setReadProgress(int p) +{ + readBar->setProgress(p); +} + +void QFDProgressDialog::setWriteProgress(int p) +{ + writeBar->setProgress(p); +} + +void QFDProgressDialog::setWriteLabel(const QString &s) +{ + writeLabel->setText(Q3FileDialog::tr("Write: %1").arg(s)); +} + +/************************************************************************ + * + * Private Q3FileDialog members + * + ************************************************************************/ + +class Q3FileDialogPrivate { +public: + ~Q3FileDialogPrivate(); + + QStringList history; + + bool geometryDirty; + Q3ComboBox * paths; + QComboBox * types; + QLabel * pathL; + QLabel * fileL; + QLabel * typeL; + + QVBoxLayout * topLevelLayout; + QHBoxLayout *buttonLayout, *leftLayout, *rightLayout; + Q3PtrList extraWidgetsLayouts; + Q3PtrList extraLabels; + Q3PtrList extraWidgets; + Q3PtrList extraButtons; + Q3PtrList toolButtons; + + Q3WidgetStack * stack; + + QToolButton * cdToParent, *newFolder, * detailView, * mcView, + *previewInfo, *previewContents, *goBack; + Q3ButtonGroup * modeButtons; + + QString currentFileName; + Q3ListViewItem *last; + + Q3ListBoxItem *lastEFSelected; + + struct File: public Q3ListViewItem { + File(Q3FileDialogPrivate * dlgp, + const QUrlInfo * fi, Q3ListViewItem * parent) + : Q3ListViewItem(parent, dlgp->last), info(*fi), d(dlgp), i(0), hasMimePixmap(false) + { setup(); dlgp->last = this; } + File(Q3FileDialogPrivate * dlgp, + const QUrlInfo * fi, Q3ListView * parent) + : Q3ListViewItem(parent, dlgp->last), info(*fi), d(dlgp), i(0), hasMimePixmap(false) + { setup(); dlgp->last = this; } + File(Q3FileDialogPrivate * dlgp, + const QUrlInfo * fi, Q3ListView * parent, Q3ListViewItem * after) + : Q3ListViewItem(parent, after), info(*fi), d(dlgp), i(0), hasMimePixmap(false) + { setup(); if (!nextSibling()) dlgp->last = this; } + ~File(); + + QString text(int column) const; + const QPixmap * pixmap(int) const; + + QUrlInfo info; + Q3FileDialogPrivate * d; + Q3ListBoxItem *i; + bool hasMimePixmap; + }; + + class MCItem: public Q3ListBoxItem { + public: + MCItem(Q3ListBox *, Q3ListViewItem * item); + MCItem(Q3ListBox *, Q3ListViewItem * item, Q3ListBoxItem *after); + QString text() const; + const QPixmap *pixmap() const; + int height(const Q3ListBox *) const; + int width(const Q3ListBox *) const; + void paint(QPainter *); + Q3ListViewItem * i; + }; + + class UrlInfoList : public Q3PtrList { + public: + UrlInfoList() { setAutoDelete(true); } + int compareItems(Q3PtrCollection::Item n1, Q3PtrCollection::Item n2) { + if (!n1 || !n2) + return 0; + + QUrlInfo *i1 = (QUrlInfo *)n1; + QUrlInfo *i2 = (QUrlInfo *)n2; + + if (i1->isDir() && !i2->isDir()) + return -1; + if (!i1->isDir() && i2->isDir()) + return 1; + + if (i1->name() == QLatin1String("..")) + return -1; + if (i2->name() == QLatin1String("..")) + return 1; + + if (sortFilesBy == QDir::Name) { +#if defined(Q_OS_WIN32) + QString name1 = i1->name().lower(); + QString name2 = i2->name().lower(); + return name1.localeAwareCompare( name2 ); +#else + QString name1 = i1->name(); + QString name2 = i2->name(); + return name1.localeAwareCompare( name2 ); +#endif + } + if (QUrlInfo::equal(*i1, *i2, sortFilesBy)) + return 0; + else if (QUrlInfo::greaterThan(*i1, *i2, sortFilesBy)) + return 1; + else if (QUrlInfo::lessThan(*i1, *i2, sortFilesBy)) + return -1; + // can't happen... + return 0; + } + QUrlInfo *operator[](int i) { + return at(i); + } + }; + + UrlInfoList sortedList; + Q3PtrList pendingItems; + + QFileListBox * moreFiles; + + Q3FileDialog::Mode mode; + + QString rw; + QString ro; + QString wo; + QString inaccessible; + + QString symLinkToFile; + QString file; + QString symLinkToDir; + QString dir; + QString symLinkToSpecial; + QString special; + Q3WidgetStack *preview; + bool infoPreview, contentsPreview; + QSplitter *splitter; + Q3UrlOperator url, oldUrl; + QWidget *infoPreviewWidget, *contentsPreviewWidget; + Q3FilePreview *infoPreviewer, *contentsPreviewer; + bool hadDotDot; + + bool ignoreNextKeyPress; + // ignores the next refresh operation in case the user forced a selection + bool ignoreNextRefresh; + QFDProgressDialog *progressDia; + bool checkForFilter; + bool ignoreStop; + + QTimer *mimeTypeTimer; + const Q3NetworkOperation *currListChildren; + + // this is similar to QUrl::encode but does encode "*" and + // doesn't encode whitespaces + static QString encodeFileName(const QString& fName) { + + QString newStr; + Q3CString cName = fName.utf8(); + const Q3CString sChars( +#ifdef Q_WS_WIN + "#%" +#else + "<>#@\"&%$:,;?={}|^~[]\'`\\*" +#endif + ); + + int len = cName.length(); + if (!len) + return QString(); + for (int i = 0; i < len ;++i) { + uchar inCh = (uchar)cName[i]; + if (inCh >= 128 || sChars.contains(inCh)) + { + newStr += QLatin1Char('%'); + ushort c = inCh / 16; + c += c > 9 ? 'A' - 10 : '0'; + newStr += QLatin1Char((char)c); + c = inCh % 16; + c += c > 9 ? 'A' - 10 : '0'; + newStr += QLatin1Char((char)c); + } else { + newStr += QLatin1Char((char)inCh); + } + } + return newStr; + } + + static bool fileExists(const Q3UrlOperator &url, const QString& name) + { + Q3Url u(url, Q3FileDialogPrivate::encodeFileName(name)); + if (u.isLocalFile()) { + QFileInfo f(u.path()); + return f.exists(); + } else { + Q3NetworkProtocol *p = Q3NetworkProtocol::getNetworkProtocol(url.protocol()); + if (p && (p->supportedOperations()&Q3NetworkProtocol::OpListChildren)) { + QUrlInfo ui(url.info(name.isEmpty() ? QString::fromLatin1(".") : name)); + return ui.isValid(); + } + } + return true; + } + +#ifndef Q_NO_CURSOR + bool cursorOverride; // Remember if the cursor was overridden or not. +#endif +}; + +Q3FileDialogPrivate::~Q3FileDialogPrivate() +{ + delete modeButtons; +} + + + +/************************************************************************ + * + * Internal class QRenameEdit + * + ************************************************************************/ + +void QRenameEdit::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Escape) + emit cancelRename(); + else + QLineEdit::keyPressEvent(e); + e->accept(); +} + +void QRenameEdit::focusOutEvent(QFocusEvent *) +{ + if (!doRenameAlreadyEmitted) { + doRenameAlreadyEmitted = true; + emit doRename(); + } +} + +void QRenameEdit::slotReturnPressed() +{ + doRenameAlreadyEmitted = true; + emit doRename(); +} + +/************************************************************************ + * + * Internal class QFileListBox + * + ************************************************************************/ + +QFileListBox::QFileListBox(QWidget *parent, Q3FileDialog *dlg) + : Q3ListBox(parent, "filelistbox"), filedialog(dlg), + renaming(false), renameItem(0), mousePressed(false), + firstMousePressEvent(true) +{ + changeDirTimer = new QTimer(this); + Q3VBox *box = new Q3VBox(viewport(), "qt_vbox"); + box->setFrameStyle(QFrame::Box | QFrame::Plain); + lined = new QRenameEdit(box); + lined->setFixedHeight(lined->sizeHint().height()); + box->hide(); + box->setBackgroundRole(QPalette::Base); + renameTimer = new QTimer(this); + connect(lined, SIGNAL(doRename()), + this, SLOT (rename())); + connect(lined, SIGNAL(cancelRename()), + this, SLOT(cancelRename())); + connect(renameTimer, SIGNAL(timeout()), + this, SLOT(doubleClickTimeout())); + connect(changeDirTimer, SIGNAL(timeout()), + this, SLOT(changeDirDuringDrag())); + connect(this, SIGNAL(contentsMoving(int,int)), + this, SLOT(contentsMoved(int,int))); + viewport()->setAcceptDrops(true); + dragItem = 0; +} + +void QFileListBox::show() +{ + setBackgroundRole(QPalette::Base); + viewport()->setBackgroundRole(QPalette::Base); + Q3ListBox::show(); +} + +void QFileListBox::keyPressEvent(QKeyEvent *e) +{ + if ((e->key() == Qt::Key_Enter || + e->key() == Qt::Key_Return) && + renaming) + return; + + QString keyPressed = ((QKeyEvent *)e)->text().toLower(); + QChar keyChar = keyPressed[0]; + if (keyChar.isLetterOrNumber()) { + Q3ListBoxItem * i = 0; + if (currentItem() != -1) + i = item(currentItem()); + else + i = firstItem(); + if (i->next()) + i = i->next(); + else + i = firstItem(); + while (i != item(currentItem())) { + QString it = text(index(i)); + if (it[0].toLower() == keyChar) { + clearSelection(); + setCurrentItem(i); + } else { + if (i->next()) + i = i->next(); + else { + if (!item(currentItem())) { + clearSelection(); + break; + } + i = firstItem(); + } + } + } + } + cancelRename(); + Q3ListBox::keyPressEvent(e); +} + +void QFileListBox::viewportMousePressEvent(QMouseEvent *e) +{ + pressPos = e->pos(); + mousePressed = false; + + bool didRename = renaming; + + cancelRename(); + if (!hasFocus() && !viewport()->hasFocus()) + setFocus(); + + if (e->button() != Qt::LeftButton) { + Q3ListBox::viewportMousePressEvent(e); + firstMousePressEvent = false; + return; + } + + int i = currentItem(); + bool wasSelected = false; + if (i != -1) + wasSelected = item(i)->isSelected(); + Q3ListBox::mousePressEvent(e); + + Q3FileDialogPrivate::MCItem *i1 = (Q3FileDialogPrivate::MCItem*)item(currentItem()); + if (i1) + mousePressed = (!((Q3FileDialogPrivate::File*)i1->i)->info.isDir()) + || (filedialog->mode() == Q3FileDialog::Directory) || (filedialog->mode() == Q3FileDialog::DirectoryOnly); + + if (itemAt(e->pos()) != item(i)) { + firstMousePressEvent = false; + return; + } + + if (!firstMousePressEvent && !didRename && i == currentItem() && currentItem() != -1 && + wasSelected && QUrlInfo(filedialog->d->url.info(QString(QLatin1Char('.')))).isWritable() && item(currentItem())->text() != QLatin1String("..")) { + renameTimer->start(QApplication::doubleClickInterval(), true); + renameItem = item(i); + } + + firstMousePressEvent = false; +} + +void QFileListBox::viewportMouseReleaseEvent(QMouseEvent *e) +{ + dragItem = 0; + Q3ListBox::viewportMouseReleaseEvent(e); + mousePressed = false; +} + +void QFileListBox::viewportMouseDoubleClickEvent(QMouseEvent *e) +{ + renameTimer->stop(); + Q3ListBox::viewportMouseDoubleClickEvent(e); +} + +void QFileListBox::viewportMouseMoveEvent(QMouseEvent *e) +{ + if (!dragItem) + dragItem = itemAt(e->pos()); + renameTimer->stop(); +#ifndef QT_NO_DRAGANDDROP + if ( (pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance() && mousePressed) { + Q3ListBoxItem *item = dragItem; + dragItem = 0; + if (item) { + if (!itemRect(item).contains(e->pos())) + return; + Q3UriDrag* drag = new Q3UriDrag(viewport()); + QStringList files; + if (filedialog->mode() == Q3FileDialog::ExistingFiles) + files = filedialog->selectedFiles(); + else + files = QStringList(filedialog->selectedFile()); + drag->setFileNames(files); + + if (lined->parentWidget()->isVisible()) + cancelRename(); + + connect(drag, SIGNAL(destroyed()), + this, SLOT(dragObjDestroyed())); + drag->drag(); + + mousePressed = false; + } + } else +#endif + { + Q3ListBox::viewportMouseMoveEvent(e); + } + +} + +void QFileListBox::dragObjDestroyed() +{ +#ifndef QT_NO_DRAGANDDROP + //####### + //filedialog->rereadDir(); +#endif +} + +#ifndef QT_NO_DRAGANDDROP +void QFileListBox::viewportDragEnterEvent(QDragEnterEvent *e) +{ + startDragUrl = filedialog->d->url; + startDragDir = filedialog->dirPath(); + currDropItem = 0; + + if (!Q3UriDrag::canDecode(e)) { + e->ignore(); + return; + } + + QStringList l; + Q3UriDrag::decodeLocalFiles(e, l); + urls = (int)l.count(); + + if (acceptDrop(e->pos(), e->source())) { + e->accept(); + setCurrentDropItem(e->pos()); + } else { + e->ignore(); + setCurrentDropItem(QPoint(-1, -1)); + } + + oldDragPos = e->pos(); +} + +void QFileListBox::viewportDragMoveEvent(QDragMoveEvent *e) +{ + if (acceptDrop(e->pos(), e->source())) { + switch (e->action()) { + case QDropEvent::Copy: + e->acceptAction(); + break; + case QDropEvent::Move: + e->acceptAction(); + break; + case QDropEvent::Link: + break; + default: + break; + } + if (oldDragPos != e->pos()) + setCurrentDropItem(e->pos()); + } else { + changeDirTimer->stop(); + e->ignore(); + setCurrentDropItem(QPoint(-1, -1)); + } + + oldDragPos = e->pos(); +} + +void QFileListBox::viewportDragLeaveEvent(QDragLeaveEvent *) +{ + changeDirTimer->stop(); + setCurrentDropItem(QPoint(-1, -1)); +//######## +// if (startDragDir != filedialog->d->url) +// filedialog->setUrl(startDragUrl); +} + +void QFileListBox::viewportDropEvent(QDropEvent *e) +{ + changeDirTimer->stop(); + + if (!Q3UriDrag::canDecode(e)) { + e->ignore(); + return; + } + + Q3StrList l; + Q3UriDrag::decode(e, l); + + bool move = e->action() == QDropEvent::Move; +// bool supportAction = move || e->action() == QDropEvent::Copy; + + Q3UrlOperator dest; + if (currDropItem) + dest = Q3UrlOperator(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text())); + else + dest = filedialog->d->url; + QStringList lst; + for (uint i = 0; i < l.count(); ++i) { + lst << QLatin1String(l.at(i)); + } + + filedialog->d->url.copy(lst, dest, move); + + // ##### what is supportAction for? + e->acceptAction(); + currDropItem = 0; +} + +bool QFileListBox::acceptDrop(const QPoint &pnt, QWidget *source) +{ + Q3ListBoxItem *item = itemAt(pnt); + if (!item || (item && !itemRect(item).contains(pnt))) { + if (source == viewport() && startDragDir == filedialog->dirPath()) + return false; + return true; + } + + QUrlInfo fi(filedialog->d->url.info(item->text().isEmpty() ? QString::fromLatin1(".") : item->text())); + + if (fi.isDir() && itemRect(item).contains(pnt)) + return true; + return false; +} + +void QFileListBox::setCurrentDropItem(const QPoint &pnt) +{ + changeDirTimer->stop(); + + Q3ListBoxItem *item = 0; + if (pnt != QPoint(-1, -1)) + item = itemAt(pnt); + if (item && !QUrlInfo(filedialog->d->url.info(item->text().isEmpty() ? QString::fromLatin1(".") : item->text())).isDir()) + item = 0; + if (item && !itemRect(item).contains(pnt)) + item = 0; + + currDropItem = item; + if (currDropItem) + setCurrentItem(currDropItem); + changeDirTimer->start(750); +} +#endif // QT_NO_DRAGANDDROP + +void QFileListBox::changeDirDuringDrag() +{ +#ifndef QT_NO_DRAGANDDROP + if (!currDropItem) + return; + changeDirTimer->stop(); + Q3Url u(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text())); + filedialog->setDir(u); + currDropItem = 0; +#endif +} + +void QFileListBox::doubleClickTimeout() +{ + startRename(); + renameTimer->stop(); +} + +void QFileListBox::startRename(bool check) +{ + if (check && (!renameItem || renameItem != item(currentItem()))) + return; + + int i = currentItem(); + setSelected(i, true); + QRect r = itemRect(item(i)); + int bdr = item(i)->pixmap() ? + item(i)->pixmap()->width() : 16; + int x = r.x() + bdr; + int y = r.y(); + int w = item(i)->width(this) - bdr; + int h = qMax(lined->height() + 2, r.height()); + y = y + r.height() / 2 - h / 2; + + lined->parentWidget()->setGeometry(x, y, w + 6, h); + lined->setFocus(); + lined->setText(item(i)->text()); + lined->selectAll(); + lined->setFrame(false); + lined->parentWidget()->show(); + viewport()->setFocusProxy(lined); + renaming = true; +} + +void QFileListBox::clear() +{ + cancelRename(); + Q3ListBox::clear(); +} + +void QFileListBox::rename() +{ + if (!lined->text().isEmpty()) { + QString file = currentText(); + + if (lined->text() != file) + filedialog->d->url.rename(file, lined->text()); + } + cancelRename(); +} + +void QFileListBox::cancelRename() +{ + renameItem = 0; + lined->parentWidget()->hide(); + viewport()->setFocusProxy(this); + renaming = false; + updateItem(currentItem()); + if (lined->hasFocus()) + viewport()->setFocus(); +} + +void QFileListBox::contentsMoved(int, int) +{ + changeDirTimer->stop(); +#ifndef QT_NO_DRAGANDDROP + setCurrentDropItem(QPoint(-1, -1)); +#endif +} + +/************************************************************************ + * + * Internal class QFileListView + * + ************************************************************************/ + +Q3FileDialogQFileListView::Q3FileDialogQFileListView(QWidget *parent, Q3FileDialog *dlg) + : Q3ListView(parent, "qt_filedlg_listview"), renaming(false), renameItem(0), + filedialog(dlg), mousePressed(false), + firstMousePressEvent(true) +{ + changeDirTimer = new QTimer(this); + Q3VBox *box = new Q3VBox(viewport(), "qt_vbox"); + box->setFrameStyle(QFrame::Box | QFrame::Plain); + lined = new QRenameEdit(box); + lined->setFixedHeight(lined->sizeHint().height()); + box->hide(); + box->setBackgroundRole(QPalette::Base); + renameTimer = new QTimer(this); + connect(lined, SIGNAL(doRename()), + this, SLOT (rename())); + connect(lined, SIGNAL(cancelRename()), + this, SLOT(cancelRename())); + header()->setMovingEnabled(false); + connect(renameTimer, SIGNAL(timeout()), + this, SLOT(doubleClickTimeout())); + connect(changeDirTimer, SIGNAL(timeout()), + this, SLOT(changeDirDuringDrag())); + disconnect(header(), SIGNAL(sectionClicked(int)), + this, SLOT(changeSortColumn(int))); + connect(header(), SIGNAL(sectionClicked(int)), + this, SLOT(changeSortColumn2(int))); + connect(this, SIGNAL(contentsMoving(int,int)), + this, SLOT(contentsMoved(int,int))); + + viewport()->setAcceptDrops(true); + sortcolumn = 0; + ascending = true; + dragItem = 0; +} + +void Q3FileDialogQFileListView::setSorting(int column, bool increasing) +{ + if (column == -1) { + Q3ListView::setSorting(column, increasing); + return; + } + + sortAscending = ascending = increasing; + sortcolumn = column; + switch (column) { + case 0: + sortFilesBy = QDir::Name; + break; + case 1: + sortFilesBy = QDir::Size; + break; + case 3: + sortFilesBy = QDir::Time; + break; + default: + sortFilesBy = QDir::Name; // #### ??? + break; + } + + filedialog->resortDir(); +} + +void Q3FileDialogQFileListView::changeSortColumn2(int column) +{ + int lcol = header()->mapToLogical(column); + setSorting(lcol, sortcolumn == lcol ? !ascending : true); +} + +void Q3FileDialogQFileListView::keyPressEvent(QKeyEvent *e) +{ + if ((e->key() == Qt::Key_Enter || + e->key() == Qt::Key_Return) && + renaming) + return; + + QString keyPressed = e->text().toLower(); + QChar keyChar = keyPressed[0]; + if (keyChar.isLetterOrNumber()) { + Q3ListViewItem * i = 0; + if (currentItem()) + i = currentItem(); + else + i = firstChild(); + if (i->nextSibling()) + i = i->nextSibling(); + else + i = firstChild(); + while (i != currentItem()) { + QString it = i->text(0); + if (it[0].toLower() == keyChar) { + clearSelection(); + ensureItemVisible(i); + setCurrentItem(i); + } else { + if (i->nextSibling()) + i = i->nextSibling(); + else + i = firstChild(); + } + } + return; + } + + cancelRename(); + Q3ListView::keyPressEvent(e); +} + +void Q3FileDialogQFileListView::viewportMousePressEvent(QMouseEvent *e) +{ + pressPos = e->pos(); + mousePressed = false; + + bool didRename = renaming; + cancelRename(); + if (!hasFocus() && !viewport()->hasFocus()) + setFocus(); + + if (e->button() != Qt::LeftButton) { + Q3ListView::viewportMousePressEvent(e); + firstMousePressEvent = false; + return; + } + + Q3ListViewItem *i = currentItem(); + Q3ListView::viewportMousePressEvent(e); + + Q3FileDialogPrivate::File *i1 = (Q3FileDialogPrivate::File*)currentItem(); + if (i1) + mousePressed = !i1->info.isDir() || (filedialog->mode() == Q3FileDialog::Directory) || (filedialog->mode() == Q3FileDialog::DirectoryOnly); + + + if (itemAt(e->pos()) != i || + e->x() + contentsX() > columnWidth(0)) { + firstMousePressEvent = false; + return; + } + + if (!firstMousePressEvent && !didRename && i == currentItem() && currentItem() && + QUrlInfo(filedialog->d->url.info(QString(QLatin1Char('.')))).isWritable() && currentItem()->text(0) != QLatin1String("..")) { + renameTimer->start(QApplication::doubleClickInterval(), true); + renameItem = currentItem(); + } + + firstMousePressEvent = false; +} + +void Q3FileDialogQFileListView::viewportMouseDoubleClickEvent(QMouseEvent *e) +{ + renameTimer->stop(); + Q3ListView::viewportMouseDoubleClickEvent(e); +} + +void Q3FileDialogQFileListView::viewportMouseReleaseEvent(QMouseEvent *e) +{ + Q3ListView::viewportMouseReleaseEvent(e); + mousePressed = false; + dragItem = 0; +} + +void Q3FileDialogQFileListView::viewportMouseMoveEvent(QMouseEvent *e) +{ + renameTimer->stop(); + if (!dragItem) + dragItem = itemAt(e->pos()); +#ifndef QT_NO_DRAGANDDROP + if ( (pressPos - e->pos()).manhattanLength() > QApplication::startDragDistance() && mousePressed) { + Q3ListViewItem *item = dragItem; + dragItem = 0; + if (item) { + Q3UriDrag* drag = new Q3UriDrag(viewport()); + QStringList files; + if (filedialog->mode() == Q3FileDialog::ExistingFiles) + files = filedialog->selectedFiles(); + else + files = QStringList(filedialog->selectedFile()); + drag->setFileNames(files); + + if (lined->isVisible()) + cancelRename(); + + connect(drag, SIGNAL(destroyed()), + this, SLOT(dragObjDestroyed())); + drag->drag(); + + mousePressed = false; + } + } +#endif +} + +void Q3FileDialogQFileListView::dragObjDestroyed() +{ +#ifndef QT_NO_DRAGANDDROP + //###### + //filedialog->rereadDir(); +#endif +} + +#ifndef QT_NO_DRAGANDDROP +void Q3FileDialogQFileListView::viewportDragEnterEvent(QDragEnterEvent *e) +{ + startDragUrl = filedialog->d->url; + startDragDir = filedialog->dirPath(); + currDropItem = 0; + + if (!Q3UriDrag::canDecode(e)) { + e->ignore(); + return; + } + + QStringList l; + Q3UriDrag::decodeLocalFiles(e, l); + urls = (int)l.count(); + + if (acceptDrop(e->pos(), e->source())) { + e->accept(); + setCurrentDropItem(e->pos()); + } else { + e->ignore(); + setCurrentDropItem(QPoint(-1, -1)); + } + + oldDragPos = e->pos(); +} + +void Q3FileDialogQFileListView::viewportDragMoveEvent(QDragMoveEvent *e) +{ + if (acceptDrop(e->pos(), e->source())) { + if (oldDragPos != e->pos()) + setCurrentDropItem(e->pos()); + switch (e->action()) { + case QDropEvent::Copy: + e->acceptAction(); + break; + case QDropEvent::Move: + e->acceptAction(); + break; + case QDropEvent::Link: + break; + default: + break; + } + } else { + changeDirTimer->stop(); + e->ignore(); + setCurrentDropItem(QPoint(-1, -1)); + } + + oldDragPos = e->pos(); +} + +void Q3FileDialogQFileListView::viewportDragLeaveEvent(QDragLeaveEvent *) +{ + changeDirTimer->stop(); + setCurrentDropItem(QPoint(-1, -1)); +//######## +// if (startDragDir != filedialog->d->url) +// filedialog->setUrl(startDragUrl); +} + +void Q3FileDialogQFileListView::viewportDropEvent(QDropEvent *e) +{ + changeDirTimer->stop(); + + if (!Q3UriDrag::canDecode(e)) { + e->ignore(); + return; + } + + QStringList l; + Q3UriDrag::decodeToUnicodeUris(e, l); + + bool move = e->action() == QDropEvent::Move; +// bool supportAction = move || e->action() == QDropEvent::Copy; + + Q3UrlOperator dest; + if (currDropItem) + dest = Q3UrlOperator(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text(0))); + else + dest = filedialog->d->url; + filedialog->d->url.copy(l, dest, move); + + // ##### what is supportAction for? + e->acceptAction(); + currDropItem = 0; +} + +bool Q3FileDialogQFileListView::acceptDrop(const QPoint &pnt, QWidget *source) +{ + Q3ListViewItem *item = itemAt(pnt); + if (!item || (item && !itemRect(item).contains(pnt))) { + if (source == viewport() && startDragDir == filedialog->dirPath()) + return false; + return true; + } + + QUrlInfo fi(filedialog->d->url.info(item->text(0).isEmpty() ? QString::fromLatin1(".") : item->text(0))); + + if (fi.isDir() && itemRect(item).contains(pnt)) + return true; + return false; +} + +void Q3FileDialogQFileListView::setCurrentDropItem(const QPoint &pnt) +{ + changeDirTimer->stop(); + + Q3ListViewItem *item = itemAt(pnt); + if (pnt == QPoint(-1, -1)) + item = 0; + if (item && !QUrlInfo(filedialog->d->url.info(item->text(0).isEmpty() ? QString::fromLatin1(".") : item->text(0))).isDir()) + item = 0; + + if (item && !itemRect(item).contains(pnt)) + item = 0; + + currDropItem = item; + + if (currDropItem) + setCurrentItem(currDropItem); + + changeDirTimer->start(750); +} +#endif // QT_NO_DRAGANDDROP + +void Q3FileDialogQFileListView::changeDirDuringDrag() +{ +#ifndef QT_NO_DRAGANDDROP + if (!currDropItem) + return; + changeDirTimer->stop(); + Q3Url u(filedialog->d->url, Q3FileDialogPrivate::encodeFileName(currDropItem->text(0))); + filedialog->setDir(u); + currDropItem = 0; +#endif // QT_NO_DRAGANDDROP +} + + +void Q3FileDialogQFileListView::doubleClickTimeout() +{ + startRename(); + renameTimer->stop(); +} + +void Q3FileDialogQFileListView::startRename(bool check) +{ + if (check && (!renameItem || renameItem != currentItem())) + return; + + Q3ListViewItem *i = currentItem(); + setSelected(i, true); + + QRect r = itemRect(i); + int bdr = i->pixmap(0) ? + i->pixmap(0)->width() : 16; + int x = r.x() + bdr; + int y = r.y(); + int w = columnWidth(0) - bdr; + int h = qMax(lined->height() + 2, r.height()); + y = y + r.height() / 2 - h / 2; + + lined->parentWidget()->setGeometry(x, y, w + 6, h); + lined->setFocus(); + lined->setText(i->text(0)); + lined->selectAll(); + lined->setFrame(false); + lined->parentWidget()->show(); + viewport()->setFocusProxy(lined); + renaming = true; +} + +void Q3FileDialogQFileListView::clear() +{ + cancelRename(); + Q3ListView::clear(); +} + +void Q3FileDialogQFileListView::rename() +{ + if (!lined->text().isEmpty()) { + QString file = currentItem()->text(0); + + if (lined->text() != file) + filedialog->d->url.rename(file, lined->text()); + } + cancelRename(); +} + +void Q3FileDialogQFileListView::cancelRename() +{ + renameItem = 0; + lined->parentWidget()->hide(); + viewport()->setFocusProxy(this); + renaming = false; + if (currentItem()) + currentItem()->repaint(); + if (lined->hasFocus()) + viewport()->setFocus(); +} + +void Q3FileDialogQFileListView::contentsMoved(int, int) +{ + changeDirTimer->stop(); +#ifndef QT_NO_DRAGANDDROP + setCurrentDropItem(QPoint(-1, -1)); +#endif +} + + +Q3FileDialogPrivate::File::~File() +{ + if (d->pendingItems.findRef(this)) + d->pendingItems.removeRef(this); +} + +QString Q3FileDialogPrivate::File::text(int column) const +{ + makeVariables(); + + switch(column) { + case 0: + return info.name(); + case 1: + if (info.isFile()) { + QIODevice::Offset size = info.size(); + return QString::number(size); + } else { + return QString::fromLatin1(""); + } + case 2: + if (info.isFile() && info.isSymLink()) { + return d->symLinkToFile; + } else if (info.isFile()) { + return d->file; + } else if (info.isDir() && info.isSymLink()) { + return d->symLinkToDir; + } else if (info.isDir()) { + return d->dir; + } else if (info.isSymLink()) { + return d->symLinkToSpecial; + } else { + return d->special; + } + case 3: { + return info.lastModified().toString(Qt::LocalDate); + } + case 4: + if (info.isReadable()) + return info.isWritable() ? d->rw : d->ro; + else + return info.isWritable() ? d->wo : d->inaccessible; + } + + return QString::fromLatin1("<--->"); +} + +const QPixmap * Q3FileDialogPrivate::File::pixmap(int column) const +{ + if (column) { + return 0; + } else if (Q3ListViewItem::pixmap(column)) { + return Q3ListViewItem::pixmap(column); + } else if (info.isSymLink()) { + if (info.isFile()) + return symLinkFileIcon; + else + return symLinkDirIcon; + } else if (info.isDir()) { + return closedFolderIcon; + } else if (info.isFile()) { + return fileIcon; + } else { + return fifteenTransparentPixels; + } +} + +Q3FileDialogPrivate::MCItem::MCItem(Q3ListBox * lb, Q3ListViewItem * item) + : Q3ListBoxItem() +{ + i = item; + if (lb) + lb->insertItem(this); +} + +Q3FileDialogPrivate::MCItem::MCItem(Q3ListBox * lb, Q3ListViewItem * item, Q3ListBoxItem *after) + : Q3ListBoxItem() +{ + i = item; + if (lb) + lb->insertItem(this, after); +} + +QString Q3FileDialogPrivate::MCItem::text() const +{ + return i->text(0); +} + + +const QPixmap *Q3FileDialogPrivate::MCItem::pixmap() const +{ + return i->pixmap(0); +} + + +int Q3FileDialogPrivate::MCItem::height(const Q3ListBox * lb) const +{ + int hf = lb->fontMetrics().height(); + int hp = pixmap() ? pixmap()->height() : 0; + return qMax(hf, hp) + 2; +} + + +int Q3FileDialogPrivate::MCItem::width(const Q3ListBox * lb) const +{ + QFontMetrics fm = lb->fontMetrics(); + int w = 2; + if (pixmap()) + w += pixmap()->width() + 4; + else + w += 18; + w += fm.width(text()); + w += -fm.minLeftBearing(); + w += -fm.minRightBearing(); + w += 6; + return w; +} + + +void Q3FileDialogPrivate::MCItem::paint(QPainter * ptr) +{ + QFontMetrics fm = ptr->fontMetrics(); + + int h; + + if (pixmap()) + h = qMax(fm.height(), pixmap()->height()) + 2; + else + h = fm.height() + 2; + + const QPixmap * pm = pixmap(); + if (pm) + ptr->drawPixmap(2, 1, *pm); + + ptr->drawText(pm ? pm->width() + 4 : 22, h - fm.descent() - 2, + text()); +} + +static QStringList makeFiltersList(const QString &filter) +{ + if (filter.isEmpty()) + return QStringList(); + + int i = filter.indexOf(QLatin1String(";;"), 0); + QString sep(QLatin1String(";;")); + if (i == -1) { + if (filter.contains(QLatin1Char('\n'))) { + sep = QLatin1Char('\n'); + i = filter.indexOf(sep); + } + } + + return QStringList::split(sep, filter); +} + +/*! + \class Q3FileDialog + \brief The Q3FileDialog class provides dialogs that allow users to select files or directories. + + \compat + + The Q3FileDialog class enables a user to traverse their file system in + order to select one or many files or a directory. + + The easiest way to create a Q3FileDialog 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_qt3support_dialogs_q3filedialog.cpp 0 + + In the above example, a modal Q3FileDialog is created using a static + function. The startup directory is set to "/home". The file filter + is set to "Images (*.png *.xpm *.jpg)". The parent of the file dialog + is set to \e this and it is given the identification name - "open file + dialog". The caption at the top of file dialog is set to "Choose a + file". If you want to use multiple filters, separate each one with + \e two semicolons, e.g. + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 1 + + You can create your own Q3FileDialog without using the static + functions. By calling setMode(), you can set what can be returned by + the Q3FileDialog. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 2 + + In the above example, the mode of the file dialog is set to \l + 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 "File Save + As" file dialog. Use \l ExistingFile if the user must select an + existing file or \l Directory if only a directory may be selected. + (See the \l Q3FileDialog::Mode enum for the complete list of modes.) + + You can retrieve the dialog's mode with mode(). Use setFilter() to set + the dialog's file filter, e.g. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 3 + + In the above example, the filter is set to "Images (*.png *.xpm + *.jpg)", this means that only files with the extension \c png, \c xpm + or \c jpg will be shown in the Q3FileDialog. You can apply + several filters by using setFilters() and add additional filters with + addFilter(). Use setSelectedFilter() to select one of the filters + you've given as the file dialog's default filter. Whenever the user + changes the filter the filterSelected() signal is emitted. + + The file dialog has two view modes, Q3FileDialog::List which simply + lists file and directory names and Q3FileDialog::Detail which + displays additional information alongside each name, e.g. file size, + modification date, etc. Set the mode with setViewMode(). + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 4 + + The last important function you will need to use when creating your + own file dialog is selectedFile(). + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 5 + + In the above example, a modal file dialog is created and shown. If + the user clicked OK, then the file they selected is put in \c + fileName. + + If you are using the \l ExistingFiles mode then you will need to use + selectedFiles() which will return the selected files in a QStringList. + + The dialog's working directory can be set with setDir(). The display + of hidden files is controlled with setShowHiddenFiles(). The dialog + can be forced to re-read the directory with rereadDir() and re-sort + the directory with resortDir(). All the files in the current directory + can be selected with selectAll(). + + \section1 Creating and using preview widgets + + There are two kinds of preview widgets that can be used with + Q3FileDialogs: \e content preview widgets and \e information preview + widgets. They are created and used in the same way except that the + function names differ, e.g. setContentsPreview() and setInfoPreview(). + + A preview widget is a widget that is placed inside a Q3FileDialog so + that the user can see either the contents of the file, or information + about the file. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 6 + + In the above snippet, we create a preview widget which inherits from + QLabel and Q3FilePreview. File preview widgets \e must inherit from + Q3FilePreview. + + Inside the class we reimplement Q3FilePreview::previewUrl(), this is + where we determine what happens when a file is selected. In the + above example we only show a preview of the file if it is a valid + pixmap. Here's how to make a file dialog use a preview widget: + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 7 + + The first line creates an instance of our preview widget. We then + create our file dialog and call setContentsPreviewEnabled(true), + this tell the file dialog to preview the contents of the currently + selected file. We then call setContentsPreview() -- note that we pass + the same preview widget twice. Finally, before showing the file + dialog, we call setPreviewMode() setting the mode to \e Contents which + will show the contents preview of the file that the user has selected. + + If you create another preview widget that is used for displaying + information about a file, create it in the same way as the contents + preview widget and call setInfoPreviewEnabled(), and + setInfoPreview(). Then the user will be able to switch between the + two preview modes. + + For more information about creating a Q3FilePreview widget see + \l{Q3FilePreview}. +*/ + + +/*! \enum Q3FileDialog::Mode + + 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 DirectoryOnly The name of a directory. The file dialog will only display directories. + \value ExistingFiles The names of zero or more existing files. + + See setMode(). +*/ + +/*! + \enum Q3FileDialog::ViewMode + + This enum describes the view mode of the file dialog, i.e. what + information about each file will be displayed. + + \value List Display file and directory names with icons. + \value Detail Display file and directory names with icons plus + additional information, such as file size and modification date. + + See setViewMode(). +*/ + +/*! + \enum Q3FileDialog::PreviewMode + + This enum describes the preview mode of the file dialog. + + \value NoPreview No preview is shown at all. + \value Contents Show a preview of the contents of the current file + using the contents preview widget. + \value Info Show information about the current file using the + info preview widget. + + See setPreviewMode(), setContentsPreview() and setInfoPreview(). +*/ + +/*! + \fn void Q3FileDialog::detailViewSelectionChanged() + \internal +*/ + +/*! + \fn void Q3FileDialog::listBoxSelectionChanged() + \internal +*/ + +extern const char qt3_file_dialog_filter_reg_exp[] = "([a-zA-Z0-9]*)\\(([a-zA-Z0-9_.*? +;#\\[\\]]*)\\)$"; + +/*! + Constructs a file dialog called \a name, with the parent, \a parent. + If \a modal is true then the file dialog is modal; otherwise it is + modeless. +*/ + +Q3FileDialog::Q3FileDialog(QWidget *parent, const char *name, bool modal) + : QDialog(parent, name, modal, + (modal ? + (Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu) : Qt::WindowFlags(0))) +{ + init(); + d->mode = ExistingFile; + d->types->insertItem(tr("All Files (*)")); + d->cursorOverride = false; + emit dirEntered(d->url.dirPath()); + rereadDir(); +} + + +/*! + Constructs a file dialog called \a name with the parent, \a parent. + If \a modal is true then the file dialog is modal; otherwise it is + modeless. + + If \a dirName is specified then it will be used as the dialog's + working directory, i.e. it will be the directory that is shown when + the dialog appears. If \a filter is specified it will be used as the + dialog's file filter. + +*/ + +Q3FileDialog::Q3FileDialog(const QString& dirName, const QString & filter, + QWidget *parent, const char *name, bool modal) + : QDialog(parent, name, modal, + (modal ? (Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu) + : Qt::WindowFlags(0))) +{ + init(); + d->mode = ExistingFile; + rereadDir(); + Q3UrlOperator u(dirName); + if (!dirName.isEmpty() && (!u.isLocalFile() || QDir(dirName).exists())) + setSelection(dirName); + else if (workingDirectory && !workingDirectory->isEmpty()) + setDir(*workingDirectory); + + if (!filter.isEmpty()) { + setFilters(filter); + if (!dirName.isEmpty()) { + int dotpos = dirName.indexOf(QLatin1Char('.'), 0, Qt::CaseInsensitive); + if (dotpos != -1) { + for (int b=0 ; btypes->count() ; b++) { + if (d->types->text(b).contains(dirName.right(dirName.length() - dotpos))) { + d->types->setCurrentItem(b); + setFilter(d->types->text(b)); + return; + } + } + } + } + } else { + d->types->insertItem(tr("All Files (*)")); + } +} + + +/*! + \internal + Initializes the file dialog. +*/ + +void Q3FileDialog::init() +{ + setSizeGripEnabled(true); + d = new Q3FileDialogPrivate(); + d->mode = AnyFile; + d->last = 0; + d->lastEFSelected = 0; + d->moreFiles = 0; + d->infoPreview = false; + d->contentsPreview = false; + d->hadDotDot = false; + d->ignoreNextKeyPress = false; + d->progressDia = 0; + d->checkForFilter = false; + d->ignoreNextRefresh = false; + d->ignoreStop = false; + d->mimeTypeTimer = new QTimer(this); + d->cursorOverride = false; + connect(d->mimeTypeTimer, SIGNAL(timeout()), + this, SLOT(doMimeTypeLookup())); + + d->url = Q3UrlOperator(toRootIfNotExists( QDir::currentDirPath() )); + d->oldUrl = d->url; + d->currListChildren = 0; + + connect(&d->url, SIGNAL(start(Q3NetworkOperation*)), + this, SLOT(urlStart(Q3NetworkOperation*))); + connect(&d->url, SIGNAL(finished(Q3NetworkOperation*)), + this, SLOT(urlFinished(Q3NetworkOperation*))); + connect(&d->url, SIGNAL(newChildren(Q3ValueList,Q3NetworkOperation*)), + this, SLOT(insertEntry(Q3ValueList,Q3NetworkOperation*))); + connect(&d->url, SIGNAL(removed(Q3NetworkOperation*)), + this, SLOT(removeEntry(Q3NetworkOperation*))); + connect(&d->url, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)), + this, SLOT(createdDirectory(QUrlInfo,Q3NetworkOperation*))); + connect(&d->url, SIGNAL(itemChanged(Q3NetworkOperation*)), + this, SLOT(itemChanged(Q3NetworkOperation*))); + connect(&d->url, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)), + this, SLOT(dataTransferProgress(int,int,Q3NetworkOperation*))); + + nameEdit = new QLineEdit(this, "name/filter editor"); + nameEdit->setMaxLength(255); //_POSIX_MAX_PATH + connect(nameEdit, SIGNAL(textChanged(QString)), + this, SLOT(fileNameEditDone())); + nameEdit->installEventFilter(this); + + d->splitter = new QSplitter(this, "qt_splitter"); + + d->stack = new Q3WidgetStack(d->splitter, "files and more files"); + + d->splitter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + + files = new Q3FileDialogQFileListView(d->stack, this); + QFontMetrics fm = fontMetrics(); + files->addColumn(tr("Name")); + files->addColumn(tr("Size")); + files->setColumnAlignment(1, Qt::AlignRight); + files->addColumn(tr("Type")); + files->addColumn(tr("Date")); + files->addColumn(tr("Attributes")); + files->header()->setStretchEnabled(true, 0); + + files->setMinimumSize(50, 25 + 2*fm.lineSpacing()); + + connect(files, SIGNAL(selectionChanged()), + this, SLOT(detailViewSelectionChanged())); + connect(files, SIGNAL(currentChanged(Q3ListViewItem*)), + this, SLOT(updateFileNameEdit(Q3ListViewItem*))); + connect(files, SIGNAL(doubleClicked(Q3ListViewItem*)), + this, SLOT(selectDirectoryOrFile(Q3ListViewItem*))); + connect(files, SIGNAL(returnPressed(Q3ListViewItem*)), + this, SLOT(selectDirectoryOrFile(Q3ListViewItem*))); + connect(files, SIGNAL(contextMenuRequested(Q3ListViewItem*,QPoint,int)), + this, SLOT(popupContextMenu(Q3ListViewItem*,QPoint,int))); + + files->installEventFilter(this); + files->viewport()->installEventFilter(this); + + d->moreFiles = new QFileListBox(d->stack, this); + d->moreFiles->setRowMode(Q3ListBox::FitToHeight); + d->moreFiles->setVariableWidth(true); + + connect(d->moreFiles, SIGNAL(selected(Q3ListBoxItem*)), + this, SLOT(selectDirectoryOrFile(Q3ListBoxItem*))); + connect(d->moreFiles, SIGNAL(selectionChanged()), + this, SLOT(listBoxSelectionChanged())); + connect(d->moreFiles, SIGNAL(highlighted(Q3ListBoxItem*)), + this, SLOT(updateFileNameEdit(Q3ListBoxItem*))); + connect(d->moreFiles, SIGNAL(contextMenuRequested(Q3ListBoxItem*,QPoint)), + this, SLOT(popupContextMenu(Q3ListBoxItem*,QPoint))); + + d->moreFiles->installEventFilter(this); + d->moreFiles->viewport()->installEventFilter(this); + + okB = new QPushButton(tr("&OK"), this, "OK"); //### Or "Save (see other "OK") + okB->setDefault(true); + okB->setEnabled(false); + connect(okB, SIGNAL(clicked()), this, SLOT(okClicked())); + cancelB = new QPushButton(tr("Cancel") , this, "Cancel"); + connect(cancelB, SIGNAL(clicked()), this, SLOT(cancelClicked())); + + d->paths = new Q3ComboBox(true, this, "directory history/editor"); + d->paths->setDuplicatesEnabled(false); + d->paths->setInsertionPolicy(Q3ComboBox::NoInsertion); + makeVariables(); + + QFileInfoList rootDrives = QDir::drives(); + for (int i = 0; i < rootDrives.size(); ++i) { + QFileInfo fi = rootDrives.at(i); + d->paths->insertItem(*openFolderIcon, fi.absFilePath()); + } + + if (QDir::homeDirPath().size()) { + if (!d->paths->listBox()->findItem(QDir::homeDirPath())) + d->paths->insertItem(*openFolderIcon, QDir::homeDirPath()); + } + + connect(d->paths, SIGNAL(activated(QString)), + this, SLOT(setDir(QString))); + + d->paths->installEventFilter(this); + QObjectList ol = d->paths->queryList("QLineEdit"); + if (ol.size()) + ol.at(0)->installEventFilter(this); + + d->geometryDirty = true; + d->types = new QComboBox(true, this, "file types"); + d->types->setDuplicatesEnabled(false); + d->types->setEditable(false); + connect(d->types, SIGNAL(activated(QString)), + this, SLOT(setFilter(QString))); + connect(d->types, SIGNAL(activated(QString)), + this, SIGNAL(filterSelected(QString))); + + d->pathL = new QLabel(d->paths, tr("Look &in:"), this, "qt_looin_lbl"); + d->fileL = new QLabel(nameEdit, tr("File &name:"), this, "qt_filename_lbl"); + d->typeL = new QLabel(d->types, tr("File &type:"), this, "qt_filetype_lbl"); + + d->goBack = new QToolButton(this, "go back"); + d->goBack->setEnabled(false); + d->goBack->setFocusPolicy(Qt::TabFocus); + connect(d->goBack, SIGNAL(clicked()), this, SLOT(goBack())); +#ifndef QT_NO_TOOLTIP + QToolTip::add(d->goBack, tr("Back")); +#endif + d->goBack->setIconSet(*goBackIcon); + + d->cdToParent = new QToolButton(this, "cd to parent"); + d->cdToParent->setFocusPolicy(Qt::TabFocus); +#ifndef QT_NO_TOOLTIP + QToolTip::add(d->cdToParent, tr("One directory up")); +#endif + d->cdToParent->setIconSet(*cdToParentIcon); + connect(d->cdToParent, SIGNAL(clicked()), + this, SLOT(cdUpClicked())); + + d->newFolder = new QToolButton(this, "new folder"); + d->newFolder->setFocusPolicy(Qt::TabFocus); +#ifndef QT_NO_TOOLTIP + QToolTip::add(d->newFolder, tr("Create New Folder")); +#endif + d->newFolder->setIconSet(*newFolderIcon); + connect(d->newFolder, SIGNAL(clicked()), + this, SLOT(newFolderClicked())); + + d->modeButtons = new Q3ButtonGroup(0, "invisible group"); + connect(d->modeButtons, SIGNAL(destroyed()), + this, SLOT(modeButtonsDestroyed())); + d->modeButtons->setExclusive(true); + connect(d->modeButtons, SIGNAL(clicked(int)), + d->stack, SLOT(raiseWidget(int))); + connect(d->modeButtons, SIGNAL(clicked(int)), + this, SLOT(changeMode(int))); + + d->mcView = new QToolButton(this, "mclistbox view"); + d->mcView->setFocusPolicy(Qt::TabFocus); +#ifndef QT_NO_TOOLTIP + QToolTip::add(d->mcView, tr("List View")); +#endif + d->mcView->setIconSet(*multiColumnListViewIcon); + d->mcView->setToggleButton(true); + d->stack->addWidget(d->moreFiles, d->modeButtons->insert(d->mcView)); + d->detailView = new QToolButton(this, "list view"); + d->detailView->setFocusPolicy(Qt::TabFocus); +#ifndef QT_NO_TOOLTIP + QToolTip::add(d->detailView, tr("Detail View")); +#endif + d->detailView->setIconSet(*detailViewIcon); + d->detailView->setToggleButton(true); + d->stack->addWidget(files, d->modeButtons->insert(d->detailView)); + + d->previewInfo = new QToolButton(this, "preview info view"); + d->previewInfo->setFocusPolicy(Qt::TabFocus); +#ifndef QT_NO_TOOLTIP + QToolTip::add(d->previewInfo, tr("Preview File Info")); +#endif + d->previewInfo->setIconSet(*previewInfoViewIcon); + d->previewInfo->setToggleButton(true); + d->modeButtons->insert(d->previewInfo); + + d->previewContents = new QToolButton(this, "preview info view"); +#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) + if ((qWinVersion() & Qt::WV_NT_based) > Qt::WV_NT) +#else + if (!qstrcmp(style()->className(), "QWindowsStyle")) +#endif + { + d->goBack->setAutoRaise(true); + d->cdToParent->setAutoRaise(true); + d->newFolder->setAutoRaise(true); + d->mcView->setAutoRaise(true); + d->detailView->setAutoRaise(true); + d->previewInfo->setAutoRaise(true); + d->previewContents->setAutoRaise(true); + } + d->previewContents->setFocusPolicy(Qt::TabFocus); +#ifndef QT_NO_TOOLTIP + QToolTip::add(d->previewContents, tr("Preview File Contents")); +#endif + d->previewContents->setIconSet(*previewContentsViewIcon); + d->previewContents->setToggleButton(true); + d->modeButtons->insert(d->previewContents); + + connect(d->detailView, SIGNAL(clicked()), + d->moreFiles, SLOT(cancelRename())); + connect(d->detailView, SIGNAL(clicked()), + files, SLOT(cancelRename())); + connect(d->mcView, SIGNAL(clicked()), + d->moreFiles, SLOT(cancelRename())); + connect(d->mcView, SIGNAL(clicked()), + files, SLOT(cancelRename())); + + d->stack->raiseWidget(d->moreFiles); + d->mcView->setOn(true); + + QHBoxLayout *lay = new QHBoxLayout(this); + lay->setMargin(6); + d->leftLayout = new QHBoxLayout(lay, 5); + d->topLevelLayout = new QVBoxLayout((QWidget*)0, 5); + lay->addLayout(d->topLevelLayout, 1); + + QHBoxLayout * h; + + d->preview = new Q3WidgetStack(d->splitter, "qt_preview"); + + d->infoPreviewWidget = new QWidget(d->preview, "qt_preview_info"); + d->contentsPreviewWidget = new QWidget(d->preview, "qt_preview_contents"); + d->infoPreviewer = d->contentsPreviewer = 0; + + h = new QHBoxLayout(0); + d->buttonLayout = h; + d->topLevelLayout->addLayout(h); + h->addWidget(d->pathL); + h->addSpacing(8); + h->addWidget(d->paths); + h->addSpacing(8); + if (d->goBack) + h->addWidget(d->goBack); + h->addWidget(d->cdToParent); + h->addSpacing(2); + h->addWidget(d->newFolder); + h->addSpacing(4); + h->addWidget(d->mcView); + h->addWidget(d->detailView); + h->addWidget(d->previewInfo); + h->addWidget(d->previewContents); + + d->topLevelLayout->addWidget(d->splitter); + + h = new QHBoxLayout(); + d->topLevelLayout->addLayout(h); + h->addWidget(d->fileL); + h->addWidget(nameEdit); + h->addSpacing(15); + h->addWidget(okB); + + h = new QHBoxLayout(); + d->topLevelLayout->addLayout(h); + h->addWidget(d->typeL); + h->addWidget(d->types); + h->addSpacing(15); + h->addWidget(cancelB); + + d->rightLayout = new QHBoxLayout(lay, 5); + d->topLevelLayout->setStretchFactor(d->mcView, 1); + d->topLevelLayout->setStretchFactor(files, 1); + + updateGeometries(); + + if (d->goBack) { + setTabOrder(d->paths, d->goBack); + setTabOrder(d->goBack, d->cdToParent); + } else { + setTabOrder(d->paths, d->cdToParent); + } + setTabOrder(d->cdToParent, d->newFolder); + setTabOrder(d->newFolder, d->mcView); + setTabOrder(d->mcView, d->detailView); + setTabOrder(d->detailView, d->moreFiles); + setTabOrder(d->moreFiles, files); + setTabOrder(files, nameEdit); + setTabOrder(nameEdit, d->types); + setTabOrder(d->types, okB); + setTabOrder(okB, cancelB); + + d->rw = tr("Read-write"); + d->ro = tr("Read-only"); + d->wo = tr("Write-only"); + d->inaccessible = tr("Inaccessible"); + + d->symLinkToFile = tr("Symlink to File"); + d->symLinkToDir = tr("Symlink to Directory"); + d->symLinkToSpecial = tr("Symlink to Special"); + d->file = tr("File"); + d->dir = tr("Dir"); + d->special = tr("Special"); + + if (lastWidth == 0) { + QRect screen = QApplication::desktop()->screenGeometry(pos()); + if (screen.width() < 1024 || screen.height() < 768) { + resize(qMin(screen.width(), 420), qMin(screen.height(), 236)); + } else { + QSize s = files->sizeHint(); + s = QSize(s.width() + 300, s.height() + 82); + + if (s.width() * 3 > screen.width() * 2) + s.setWidth(screen.width() * 2 / 3); + + if (s.height() * 3 > screen.height() * 2) + s.setHeight(screen.height() * 2 / 3); + else if (s.height() * 3 < screen.height()) + s.setHeight(screen.height() / 3); + + resize(s); + } + updateLastSize(this); + } else { + resize(lastWidth, lastHeight); + } + + if (detailViewMode) { + d->stack->raiseWidget(files); + d->mcView->setOn(false); + d->detailView->setOn(true); + } + + d->preview->hide(); + nameEdit->setFocus(); + + connect(nameEdit, SIGNAL(returnPressed()), + this, SLOT(fileNameEditReturnPressed())); +} + +/*! + \internal +*/ + +void Q3FileDialog::fileNameEditReturnPressed() +{ + d->oldUrl = d->url; + if (!isDirectoryMode(d->mode)) { + okClicked(); + } else { + d->currentFileName.clear(); + if (nameEdit->text().isEmpty()) { + emit fileSelected(selectedFile()); + accept(); + } else { + QUrlInfo f; + Q3FileDialogPrivate::File * c + = (Q3FileDialogPrivate::File *)files->currentItem(); + if (c && files->isSelected(c)) + f = c->info; + else + f = QUrlInfo(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text())); + if (f.isDir()) { + setUrl(Q3UrlOperator(d->url, + Q3FileDialogPrivate::encodeFileName(nameEdit->text() + QLatin1Char('/')))); + d->checkForFilter = true; + trySetSelection(true, d->url, true); + d->checkForFilter = false; + } + } + nameEdit->setText(QString()); + } +} + +/*! + \internal + Update the info and content preview widgets to display \a u. +*/ + +void Q3FileDialog::updatePreviews(const Q3Url &u) +{ + if (d->infoPreviewer) + d->infoPreviewer->previewUrl(u); + if (d->contentsPreviewer) + d->contentsPreviewer->previewUrl(u); +} + +/*! + \internal + Changes the preview mode to the mode specified at \a id. +*/ + +void Q3FileDialog::changeMode(int id) +{ + if (!d->infoPreview && !d->contentsPreview) + return; + + QAbstractButton*btn = d->modeButtons->find(id); + if (!btn) + return; + + if (btn == d->previewContents && !d->contentsPreview) + return; + if (btn == d->previewInfo && !d->infoPreview) + return; + + if (btn != d->previewContents && btn != d->previewInfo) { + d->preview->hide(); + } else { + if (files->currentItem()) + updatePreviews(Q3Url(d->url, files->currentItem()->text(0))); + if (btn == d->previewInfo) + d->preview->raiseWidget(d->infoPreviewWidget); + else + d->preview->raiseWidget(d->contentsPreviewWidget); + d->preview->show(); + } +} + +/*! + Destroys the file dialog. +*/ + +Q3FileDialog::~Q3FileDialog() +{ + // since clear might call setContentsPos which would emit + // a signal and thus cause a recompute of sizes... + files->blockSignals(true); + d->moreFiles->blockSignals(true); + files->clear(); + d->moreFiles->clear(); + d->moreFiles->blockSignals(false); + files->blockSignals(false); + +#ifndef QT_NO_CURSOR + if (d->cursorOverride) + QApplication::restoreOverrideCursor(); +#endif + + delete d; + d = 0; +} + + +/*! + \property Q3FileDialog::selectedFile + + \brief the name of the selected file + + If a file was selected selectedFile contains the file's name including + its absolute path; otherwise selectedFile is empty. + + \sa QString::isEmpty(), selectedFiles, selectedFilter +*/ + +QString Q3FileDialog::selectedFile() const +{ + QString s = d->currentFileName; + // remove the protocol because we do not want to encode it... + QString prot = Q3Url(s).protocol(); + if (!prot.isEmpty()) { + prot += QLatin1Char(':'); + s.remove(0, prot.length()); + } + Q3Url u(prot + Q3FileDialogPrivate::encodeFileName(s)); + if (u.isLocalFile()) { + QString s = u.toString(); + if (s.left(5) == QLatin1String("file:")) + s.remove((uint)0, 5); + return s; + } + return d->currentFileName; +} + +/*! + \property Q3FileDialog::selectedFilter + + \brief the filter which the user has selected in the file dialog + + \sa filterSelected(), selectedFiles, selectedFile +*/ + +QString Q3FileDialog::selectedFilter() const +{ + return d->types->currentText(); +} + +/*! \overload + + Sets the current filter selected in the file dialog to the + \a{n}-th filter in the filter list. + + \sa filterSelected(), selectedFilter(), selectedFiles(), selectedFile() +*/ + +void Q3FileDialog::setSelectedFilter(int n) +{ + d->types->setCurrentItem(n); + QString f = d->types->currentText(); + QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp)); + int index = r.indexIn(f); + if (index >= 0) + f = r.cap(2); + d->url.setNameFilter(f); + rereadDir(); +} + +/*! + Sets the current filter selected in the file dialog to the first + one that contains the text \a mask. +*/ + +void Q3FileDialog::setSelectedFilter(const QString& mask) +{ + int n; + + for (n = 0; n < d->types->count(); n++) { + if (d->types->text(n).contains(mask, Qt::CaseInsensitive)) { + d->types->setCurrentItem(n); + QString f = mask; + QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp)); + int index = r.indexIn(f); + if (index >= 0) + f = r.cap(2); + d->url.setNameFilter(f); + rereadDir(); + return; + } + } +} + +/*! + \property Q3FileDialog::selectedFiles + + \brief the list of selected files + + If one or more files are selected, selectedFiles contains their + names including their absolute paths. If no files are selected or + the mode isn't ExistingFiles selectedFiles is an empty list. + + It is more convenient to use selectedFile() if the mode is + \l ExistingFile, \c Directory or \c DirectoryOnly. + + Note that if you want to iterate over the list, you should + iterate over a copy, e.g. + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 8 + + \sa selectedFile, selectedFilter, QList::isEmpty() +*/ + +QStringList Q3FileDialog::selectedFiles() const +{ + QStringList lst; + + if (mode() == ExistingFiles) { + QStringList selectedLst; + QString selectedFiles = nameEdit->text(); + if (selectedFiles.lastIndexOf(QLatin1Char('\"')) == -1) { + //probably because Enter was pressed on the nameEdit, so we have one file + //not in "" but raw + selectedLst.append(selectedFiles); + } else { + selectedFiles.truncate(selectedFiles.lastIndexOf(QLatin1Char('\"'))); + selectedLst = selectedLst.split(QLatin1String("\" "), selectedFiles); + } + for (QStringList::Iterator it = selectedLst.begin(); it != selectedLst.end(); ++it) { + Q3Url u; + if ((*it)[0] == QLatin1Char('\"')) { + u = Q3Url(d->url, Q3FileDialogPrivate::encodeFileName((*it).mid(1))); + } else { + u = Q3Url(d->url, Q3FileDialogPrivate::encodeFileName((*it))); + } + if (u.isLocalFile()) { + QString s = u.toString(); + if (s.left(5) == QLatin1String("file:")) + s.remove((uint)0, 5); + lst << s; + } else { + lst << u.toString(); + } + } + } + + return lst; +} + +/*! + Sets the default selection to \a filename. If \a filename is + absolute, setDir() is also called to set the file dialog's working + directory to the filename's directory. + + \omit + Only for external use. Not useful inside Q3FileDialog. + \endomit +*/ + +void Q3FileDialog::setSelection(const QString & filename) +{ + d->oldUrl = d->url; + QString nf = d->url.nameFilter(); + if (Q3Url::isRelativeUrl(filename)) + d->url = Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(filename)); + else + d->url = Q3UrlOperator(filename); + d->url.setNameFilter(nf); + d->checkForFilter = true; + bool isDirOk; + bool isDir = d->url.isDir(&isDirOk); + if (!isDirOk) + isDir = d->url.path().right(1) == QString(QLatin1Char('/')); + if (!isDir) { + Q3UrlOperator u(d->url); + d->url.setPath(d->url.dirPath()); + trySetSelection(false, u, true); + d->ignoreNextRefresh = true; + nameEdit->selectAll(); + rereadDir(); + emit dirEntered(d->url.dirPath()); + } else { + if (!d->url.path().isEmpty() && + d->url.path().right(1) != QString(QLatin1Char('/'))) { + QString p = d->url.path(); + p += QLatin1Char('/'); + d->url.setPath(p); + } + trySetSelection(true, d->url, false); + rereadDir(); + emit dirEntered(d->url.dirPath()); + nameEdit->setText(QString::fromLatin1("")); + } + d->checkForFilter = false; +} + +/*! + \property Q3FileDialog::dirPath + + \brief the file dialog's working directory + + \sa dir(), setDir() +*/ + +QString Q3FileDialog::dirPath() const +{ + return d->url.dirPath(); +} + + +/*! + + Sets the filter used in the file dialog to \a newFilter. + + If \a newFilter contains a pair of parentheses containing one or more + of "anything*something" separated by spaces or by + semicolons 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_qt3support_dialogs_q3filedialog.cpp 9 + + \sa setFilters() +*/ + +void Q3FileDialog::setFilter(const QString & newFilter) +{ + if (newFilter.isEmpty()) + return; + QString f = newFilter; + QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp)); + int index = r.indexIn(f); + if (index >= 0) + f = r.cap(2); + d->url.setNameFilter(f); + if (d->types->count() == 1) { + d->types->clear(); + d->types->insertItem(newFilter); + } else { + for (int i = 0; i < d->types->count(); ++i) { + if (d->types->text(i).left(newFilter.length()) == newFilter || + d->types->text(i).left(f.length()) == f) { + d->types->setCurrentItem(i); + break; + } + } + } + rereadDir(); +} + + +/*! \overload + Sets the file dialog's working directory to \a pathstr. + + \sa dir() +*/ + +void Q3FileDialog::setDir(const QString & pathstr) +{ + QString dr = pathstr; + if (dr.isEmpty()) + return; + +#if defined(Q_OS_UNIX) + if (dr.length() && dr[0] == QLatin1Char('~')) { + int i = 0; + while(i < (int)dr.length() && dr[i] != QLatin1Char('/')) + i++; + Q3CString user; + if (i == 1) { +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + +# ifndef _POSIX_LOGIN_NAME_MAX +# define _POSIX_LOGIN_NAME_MAX 9 +# endif + + char name[_POSIX_LOGIN_NAME_MAX]; + if (::getlogin_r(name, _POSIX_LOGIN_NAME_MAX) == 0) + user = name; + else +#else + user = ::getlogin(); + if (user.isEmpty()) +#endif + user = qgetenv("LOGNAME"); + } else + user = dr.mid(1, i-1).local8Bit(); + dr = dr.mid(i, dr.length()); + struct passwd *pw; +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_FREEBSD) && !defined(Q_OS_OPENBSD) + struct passwd mt_pw; + char buffer[2048]; + if (::getpwnam_r(user, &mt_pw, buffer, 2048, &pw) == 0 && pw == &mt_pw) +#else + pw = ::getpwnam(user); + if (pw) +#endif + dr.prepend(QString::fromLocal8Bit(pw->pw_dir)); + } +#endif + + setUrl(dr); +} + +/*! + Returns the current directory shown in the file dialog. + + The ownership of the QDir pointer is transferred to the caller, so + it must be deleted by the caller when no longer required. + + \sa setDir() +*/ + +const QDir *Q3FileDialog::dir() const +{ + if (d->url.isLocalFile()) + return new QDir(d->url.path()); + else + return 0; +} + +/*! + Sets the file dialog's working directory to \a dir. + \sa dir() +*/ + +void Q3FileDialog::setDir(const QDir &dir) +{ + d->oldUrl = d->url; + QString nf(d->url.nameFilter()); + d->url = dir.canonicalPath(); + d->url.setNameFilter(nf); + QUrlInfo i(d->url.info(nameEdit->text().isEmpty()? QString::fromLatin1(".") : nameEdit->text())); + d->checkForFilter = true; + trySetSelection(i.isDir(), Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(nameEdit->text())), false); + d->checkForFilter = false; + rereadDir(); + emit dirEntered(d->url.path()); +} + +/*! + Sets the file dialog's working directory to the directory specified at \a url. + + \sa url() +*/ + +void Q3FileDialog::setUrl(const Q3UrlOperator &url) +{ + d->oldUrl = d->url; + QString nf = d->url.nameFilter(); + + QString operatorPath = url.toString(false, false); + if (Q3Url::isRelativeUrl(operatorPath)) { + d->url = Q3Url(d->url, operatorPath); + } else { + d->url = url; + } + d->url.setNameFilter(nf); + + d->checkForFilter = true; + if (!d->url.isDir()) { + Q3UrlOperator u = d->url; + d->url.setPath(d->url.dirPath()); + trySetSelection(false, u, false); + rereadDir(); + emit dirEntered(d->url.dirPath()); + QString fn = u.fileName(); + nameEdit->setText(fn); + } else { + trySetSelection(true, d->url, false); + rereadDir(); + emit dirEntered(d->url.dirPath()); + } + d->checkForFilter = false; +} + +/*! + \property Q3FileDialog::showHiddenFiles + + \brief whether hidden files are shown in the file dialog + + The default is false, i.e. don't show hidden files. +*/ + +void Q3FileDialog::setShowHiddenFiles(bool s) +{ + if (s == bShowHiddenFiles) + return; + + bShowHiddenFiles = s; + rereadDir(); +} + +bool Q3FileDialog::showHiddenFiles() const +{ + return bShowHiddenFiles; +} + +/*! + Rereads the current directory shown in the file dialog. + + The only time you will need to call this function is if the contents of + the directory change and you wish to refresh the file dialog to reflect + the change. + + \sa resortDir() +*/ + +void Q3FileDialog::rereadDir() +{ +#ifndef QT_NO_CURSOR + if (!d->cursorOverride) { + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + d->cursorOverride = true; + } +#endif + d->pendingItems.clear(); + if (d->mimeTypeTimer->isActive()) + d->mimeTypeTimer->stop(); + d->currListChildren = d->url.listChildren(); +#ifndef QT_NO_CURSOR + if (d->cursorOverride) { + QApplication::restoreOverrideCursor(); + d->cursorOverride = false; + } +#endif +} + + +/*! + \fn void Q3FileDialog::fileHighlighted(const QString& file) + + This signal is emitted when the user highlights the given \a file, + i.e. makes it the current file. + + \sa fileSelected(), filesSelected() +*/ + +/*! + \fn void Q3FileDialog::fileSelected(const QString& file) + + This signal is emitted when the user selects the given \a file. + + \sa filesSelected(), fileHighlighted(), selectedFile() +*/ + +/*! + \fn void Q3FileDialog::filesSelected(const QStringList& files) + + This signal is emitted when the user selects the given \a files in \e + ExistingFiles mode. + + \sa fileSelected(), fileHighlighted(), selectedFiles() +*/ + +/*! + \fn void Q3FileDialog::dirEntered(const QString& directory) + + This signal is emitted when the user enters the given \a directory. + + \sa dir() +*/ + +/*! + \fn void Q3FileDialog::filterSelected(const QString& filter) + + This signal is emitted when the user selects the given \a filter. + + \sa selectedFilter() +*/ + +extern bool qt_resolve_symlinks; // defined in q3url.cpp +extern Q_GUI_EXPORT bool qt_use_native_dialogs; //qtgui + +/*! + This is a convenience static function that returns an existing file + selected by the user. If the user pressed Cancel, it returns a null + string. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 10 + + The function creates a modal file dialog called \a name, with + parent, \a parent. If a parent is not 0, the dialog will be shown + centered over the parent. + + The file dialog's working directory will be set to \a startWith. If \a + startWith 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 startWith, \a selectedFilter and \a filter may be an empty string. + + 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 Q3FileDialog, unless the style of the application + is set to something other than the native style (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 /usr/tmp is a symlink to /var/tmp, + the file dialog will change to /var/tmp after entering /usr/tmp. + If \a resolveSymlinks is false, the file dialog will treat + symlinks as regular directories. + + \sa getOpenFileNames(), getSaveFileName(), getExistingDirectory() +*/ + +QString Q3FileDialog::getOpenFileName(const QString & startWith, + const QString& filter, + QWidget *parent, const char* name, + const QString& caption, + QString *selectedFilter, + bool resolveSymlinks) +{ + bool save_qt_resolve_symlinks = qt_resolve_symlinks; + qt_resolve_symlinks = resolveSymlinks; + + QStringList filters; + if (!filter.isEmpty()) + filters = makeFiltersList(filter); + + makeVariables(); + QString initialSelection; + //### Problem with the logic here: If a startWith is given and a file + // with that name exists in D->URL, the box will be opened at D->URL instead of + // the last directory used ('workingDirectory'). + // + // hm... isn't that problem exactly the documented behaviour? the + // documented behaviour sounds meaningful. + if (!startWith.isEmpty()) { + Q3UrlOperator u(Q3FileDialogPrivate::encodeFileName(startWith)); + if (u.isLocalFile() && QFileInfo(u.path()).isDir()) { + *workingDirectory = startWith; + } else { + if (u.isLocalFile()) { + QFileInfo fi(u.dirPath()); + if (fi.exists()) { + *workingDirectory = u.dirPath(); + initialSelection = u.fileName(); + } + } else { + *workingDirectory = u.toString(); + initialSelection.clear(); + } + } + } + + if (workingDirectory->isNull()) + *workingDirectory = toRootIfNotExists( QDir::currentDirPath() ); + +#if defined(Q_WS_WIN) + if (qt_use_native_dialogs && qobject_cast(qApp->style())) + return winGetOpenFileName(initialSelection, filter, workingDirectory, + parent, name, caption, selectedFilter); +#elif defined(Q_WS_MAC) + if(qt_use_native_dialogs && qobject_cast(qApp->style())) { + QStringList files = macGetOpenFileNames(filter, startWith.isEmpty() ? 0 : workingDirectory, + parent, name, caption, selectedFilter, false); + return files.isEmpty() ? QString() : files.first().normalized(QString::NormalizationForm_C); + } +#endif + + Q3FileDialog *dlg = new Q3FileDialog(*workingDirectory, QString(), parent, name ? name : "qt_filedlg_gofn", true); + + if (!caption.isNull()) + dlg->setWindowTitle(caption); + else + dlg->setWindowTitle(Q3FileDialog::tr("Open")); + + dlg->setFilters(filters); + if (selectedFilter) + dlg->setFilter(*selectedFilter); + dlg->setMode(Q3FileDialog::ExistingFile); + QString result; + if (!initialSelection.isEmpty()) + dlg->setSelection(initialSelection); + if (dlg->exec() == QDialog::Accepted) { + result = dlg->selectedFile(); + *workingDirectory = dlg->d->url; + if (selectedFilter) + *selectedFilter = dlg->selectedFilter(); + } + delete dlg; + + qt_resolve_symlinks = save_qt_resolve_symlinks; + + return result; +} + +/*! + 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 called \a name, with parent, \a parent. + If a parent is not 0, the dialog will be shown centered over the + parent. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 11 + + The file dialog's working directory will be set to \a startWith. If \a + startWith 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 startWith, \a selectedFilter and \a filter may be an empty string. + + 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 Q3FileDialog, unless the style of the application + is set to something other than the native style. (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. And on the Mac the filter argument is ignored). + + Under Unix/X11, the normal behavior of the file dialog is to resolve + and follow symlinks. For example, if /usr/tmp is a symlink to /var/tmp, + the file dialog will change to /var/tmp after entering /usr/tmp. + If \a resolveSymlinks is false, the file dialog will treat + symlinks as regular directories. + + \sa getOpenFileName(), getOpenFileNames(), getExistingDirectory() +*/ + +QString Q3FileDialog::getSaveFileName(const QString & startWith, + const QString& filter, + QWidget *parent, const char* name, + const QString& caption, + QString *selectedFilter, + bool resolveSymlinks) +{ + bool save_qt_resolve_symlinks = qt_resolve_symlinks; + qt_resolve_symlinks = resolveSymlinks; + + QStringList filters; + if (!filter.isEmpty()) + filters = makeFiltersList(filter); + + makeVariables(); + QString initialSelection; + if (!startWith.isEmpty()) { + Q3UrlOperator u(Q3FileDialogPrivate::encodeFileName(startWith)); + if (u.isLocalFile() && QFileInfo(u.path()).isDir()) { + *workingDirectory = startWith; + } else { + if (u.isLocalFile()) { + QFileInfo fi(u.dirPath()); + if (fi.exists()) { + *workingDirectory = u.dirPath(); + initialSelection = u.fileName(); + } + } else { + *workingDirectory = u.toString(); + initialSelection.clear(); + } + } + } + + if (workingDirectory->isNull()) + *workingDirectory = toRootIfNotExists( QDir::currentDirPath() ); + +#if defined(Q_WS_WIN) + if (qt_use_native_dialogs && qobject_cast(qApp->style())) + return winGetSaveFileName(initialSelection, filter, workingDirectory, + parent, name, caption, selectedFilter); +#elif defined(Q_WS_MAC) + if(qt_use_native_dialogs && qobject_cast(qApp->style())) + return macGetSaveFileName(initialSelection.isNull() ? startWith : initialSelection, + filter, startWith.isEmpty() ? 0 : workingDirectory, parent, name, + caption, selectedFilter).normalized(QString::NormalizationForm_C); +#endif + + Q3FileDialog *dlg = new Q3FileDialog(*workingDirectory, QString(), parent, name ? name : "qt_filedlg_gsfn", true); + + if (!caption.isNull()) + dlg->setWindowTitle(caption); + else + dlg->setWindowTitle(Q3FileDialog::tr("Save As")); + + QString result; + dlg->setFilters(filters); + if (selectedFilter) + dlg->setFilter(*selectedFilter); + dlg->setMode(Q3FileDialog::AnyFile); + if (!initialSelection.isEmpty()) + dlg->setSelection(initialSelection); + if (dlg->exec() == QDialog::Accepted) { + result = dlg->selectedFile(); + *workingDirectory = dlg->d->url; + if (selectedFilter) + *selectedFilter = dlg->selectedFilter(); + } + delete dlg; + + qt_resolve_symlinks = save_qt_resolve_symlinks; + + return result; +} + +/*! + \internal + Activated when the "OK" button is clicked. +*/ + +void Q3FileDialog::okClicked() +{ + QString fn(nameEdit->text()); + +#if defined(Q_WS_WIN) + QFileInfo fi(d->url.path() + fn); + if (fi.isSymLink()) { + nameEdit->setText(fi.symLinkTarget()); + } +#endif + + if (fn.contains(QLatin1Char('*'))) { + addFilter(fn); + nameEdit->blockSignals(true); + nameEdit->setText(QString::fromLatin1("")); + nameEdit->blockSignals(false); + return; + } + + *workingDirectory = d->url; + detailViewMode = files->isVisible(); + updateLastSize(this); + + if (isDirectoryMode(d->mode)) { + QUrlInfo f(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text())); + if (f.isDir()) { + d->currentFileName = d->url; + if (d->currentFileName.right(1) != QString(QLatin1Char('/'))) + d->currentFileName += QLatin1Char('/'); + if (f.name() != QString(QLatin1Char('.'))) + d->currentFileName += f.name(); + accept(); + return; + } + // Since it's not a directory and we clicked ok, we + // don't really want to do anything else + return; + } + + // if we're in multi-selection mode and something is selected, + // accept it and be done. + if (mode() == ExistingFiles) { + if (! nameEdit->text().isEmpty()) { + QStringList sf = selectedFiles(); + bool isdir = false; + if (sf.count() == 1) { + Q3UrlOperator u(d->url, sf[0]); + bool ok; + isdir = u.isDir(&ok) && ok; + } + if (!isdir) { + emit filesSelected(sf); + accept(); + return; + } + } + } + + if (mode() == AnyFile) { + Q3UrlOperator u(d->url, Q3FileDialogPrivate::encodeFileName(nameEdit->text())); + if (!u.isDir()) { + d->currentFileName = u; + emit fileSelected(selectedFile()); + accept(); + return; + } + } + + if (mode() == ExistingFile) { + if (!Q3FileDialogPrivate::fileExists(d->url, nameEdit->text())) + return; + } + + // If selection is valid, return it, else try + // using selection as a directory to change to. + if (!d->currentFileName.isNull() && !d->currentFileName.contains(QLatin1Char('*'))) { + emit fileSelected(selectedFile()); + accept(); + } else { + QUrlInfo f; + Q3FileDialogPrivate::File * c + = (Q3FileDialogPrivate::File *)files->currentItem(); + Q3FileDialogPrivate::MCItem * m + = (Q3FileDialogPrivate::MCItem *)d->moreFiles->item(d->moreFiles->currentItem()); + if ((c && files->isVisible() && files->hasFocus()) + || (m && d->moreFiles->isVisible())) { + if (c && files->isVisible()) + f = c->info; + else + f = ((Q3FileDialogPrivate::File*)m->i)->info; + } else { + f = QUrlInfo(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text())); + } + if (f.isDir()) { +#if defined(Q_WS_WIN) + if (f.isSymLink()) + setUrl(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(fn + QLatin1Char('/')))); + else +#else + setUrl(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(f.name() + QLatin1Char('/')))); +#endif + d->checkForFilter = true; + trySetSelection(true, d->url, true); + d->checkForFilter = false; + } else { + if (!nameEdit->text().contains(QLatin1Char('/')) && + !nameEdit->text().contains(QLatin1String("\\")) +#if defined(Q_OS_WIN32) + && nameEdit->text()[1] != QLatin1Char(':') +#endif + ) + addFilter(nameEdit->text()); + else if (nameEdit->text()[0] == QLatin1Char('/') || + nameEdit->text()[0] == QLatin1Char('\\') +#if defined(Q_OS_WIN32) + || nameEdit->text()[1] == QLatin1Char(':') +#endif + ) + setDir(nameEdit->text()); + else if (nameEdit->text().left(3) == QLatin1String("../") || nameEdit->text().left(3) == QLatin1String("..\\")) + setDir(Q3Url(d->url.toString(), Q3FileDialogPrivate::encodeFileName(nameEdit->text())).toString()); + } + nameEdit->setText(QLatin1String("")); + } +} + +/*! + \internal + Activated when the "Filter" button is clicked. +*/ + +void Q3FileDialog::filterClicked() +{ + // unused +} + +/*! + \internal + Activated when the "Cancel" button is clicked. +*/ + +void Q3FileDialog::cancelClicked() +{ + *workingDirectory = d->url; + detailViewMode = files->isVisible(); + updateLastSize(this); + reject(); +} + + +/*!\reimp +*/ + +void Q3FileDialog::resizeEvent(QResizeEvent * e) +{ + QDialog::resizeEvent(e); + updateGeometries(); +} + +/* + \internal + The only correct way to try to set currentFileName +*/ +bool Q3FileDialog::trySetSelection(bool isDir, const Q3UrlOperator &u, bool updatelined) +{ + if (!isDir && !u.path().isEmpty() && u.path().right(1) == QString(QLatin1Char('/'))) + isDir = true; + if (u.fileName().contains(QLatin1Char('*')) && d->checkForFilter) { + QString fn(u.fileName()); + if (fn.contains(QLatin1Char('*'))) { + addFilter(fn); + d->currentFileName.clear(); + d->url.setFileName(QString()); + nameEdit->setText(QString::fromLatin1("")); + return false; + } + } + + if (isDir && d->preview && d->preview->isVisible()) + updatePreviews(u); + + QString old = d->currentFileName; + + if (isDirectoryMode(mode())) { + if (isDir) + d->currentFileName = u; + else + d->currentFileName.clear(); + } else if (!isDir && mode() == ExistingFiles) { + d->currentFileName = u; + } else if (!isDir || (mode() == AnyFile && !isDir)) { + d->currentFileName = u; + } else { + d->currentFileName.clear(); + } + if (updatelined && !d->currentFileName.isEmpty()) { + // If the selection is valid, or if its a directory, allow OK. + if (!d->currentFileName.isNull() || isDir) { + if (u.fileName() != QLatin1String("..")) { + QString fn = u.fileName(); + nameEdit->setText(fn); + } else { + nameEdit->setText(QLatin1String("")); + } + } else + nameEdit->setText(QString::fromLatin1("")); + } + + if (!d->currentFileName.isNull() || isDir) { + okB->setEnabled(true); + } else if (!isDirectoryMode(d->mode)) { + okB->setEnabled(false); + } + + if (d->currentFileName.length() && old != d->currentFileName) + emit fileHighlighted(selectedFile()); + + return !d->currentFileName.isNull(); +} + + +/*! Make sure the minimum and maximum sizes of everything are sane. +*/ + +void Q3FileDialog::updateGeometries() +{ + if (!d || !d->geometryDirty) + return; + + d->geometryDirty = false; + + QSize r, t; + + // we really should use QSize::expandedTo() +#define RM r.setWidth(qMax(r.width(),t.width())); \ +r.setHeight(qMax(r.height(),t.height())) + + // labels first + r = d->pathL->sizeHint(); + t = d->fileL->sizeHint(); + RM; + t = d->typeL->sizeHint(); + RM; + d->pathL->setFixedSize(d->pathL->sizeHint()); + d->fileL->setFixedSize(r); + d->typeL->setFixedSize(r); + + // single-line input areas + r = d->paths->sizeHint(); + t = nameEdit->sizeHint(); + RM; + t = d->types->sizeHint(); + RM; + r.setWidth(t.width() * 2 / 3); + t.setWidth(QWIDGETSIZE_MAX); + t.setHeight(r.height()); + d->paths->setMinimumSize(r); + d->paths->setMaximumSize(t); + nameEdit->setMinimumSize(r); + nameEdit->setMaximumSize(t); + d->types->setMinimumSize(r); + d->types->setMaximumSize(t); + + // buttons on top row + r = QSize(0, d->paths->minimumSize().height()); + t = QSize(21, 20); + RM; + if (r.height()+1 > r.width()) + r.setWidth(r.height()+1); + if (d->goBack) + d->goBack->setFixedSize(r); + d->cdToParent->setFixedSize(r); + d->newFolder->setFixedSize(r); + d->mcView->setFixedSize(r); + d->detailView->setFixedSize(r); + + QAbstractButton *b = 0; + if (!d->toolButtons.isEmpty()) { + for (b = d->toolButtons.first(); b; b = d->toolButtons.next()) + b->setFixedSize(b->sizeHint().width(), r.height()); + } + + if (d->infoPreview) { + d->previewInfo->show(); + d->previewInfo->setFixedSize(r); + } else { + d->previewInfo->hide(); + d->previewInfo->setFixedSize(QSize(0, 0)); + } + + if (d->contentsPreview) { + d->previewContents->show(); + d->previewContents->setFixedSize(r); + } else { + d->previewContents->hide(); + d->previewContents->setFixedSize(QSize(0, 0)); + } + + // open/save, cancel + r = QSize(75, 20); + t = okB->sizeHint(); + RM; + t = cancelB->sizeHint(); + RM; + + okB->setFixedSize(r); + cancelB->setFixedSize(r); + + d->topLevelLayout->activate(); + +#undef RM +} + + +/*! Updates the file name edit box to \a newItem in the file dialog + when the cursor moves in the listview. +*/ + +void Q3FileDialog::updateFileNameEdit(Q3ListViewItem * newItem) +{ + if (!newItem) + return; + + if (mode() == ExistingFiles) { + detailViewSelectionChanged(); + Q3Url u(d->url, Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)files->currentItem())->info.name())); + QFileInfo fi(u.toString(false, false)); + if (!fi.isDir()) + emit fileHighlighted(u.toString(false, false)); + } else if (files->isSelected(newItem)) { + Q3FileDialogPrivate::File * i = (Q3FileDialogPrivate::File *)newItem; + if (i && i->i && !i->i->isSelected()) { + d->moreFiles->blockSignals(true); + d->moreFiles->setSelected(i->i, true); + d->moreFiles->blockSignals(false); + } + // Encode the filename in case it had any special characters in it + QString encFile = Q3FileDialogPrivate::encodeFileName(newItem->text(0)); + trySetSelection(i->info.isDir(), Q3UrlOperator(d->url, encFile), true); + } +} + +void Q3FileDialog::detailViewSelectionChanged() +{ + if (d->mode != ExistingFiles) + return; + + nameEdit->clear(); + QString str; + Q3ListViewItem * i = files->firstChild(); + d->moreFiles->blockSignals(true); + while(i) { + if (d->moreFiles && isVisible()) { + Q3FileDialogPrivate::File *f = (Q3FileDialogPrivate::File *)i; + if (f->i && f->i->isSelected() != i->isSelected()) + d->moreFiles->setSelected(f->i, i->isSelected()); + } + if (i->isSelected() && !((Q3FileDialogPrivate::File *)i)->info.isDir()) + str += QString(QLatin1String("\"%1\" ")).arg(i->text(0)); + i = i->nextSibling(); + } + d->moreFiles->blockSignals(false); + nameEdit->setText(str); + nameEdit->setCursorPosition(str.length()); + okB->setEnabled(true); + if (d->preview && d->preview->isVisible() && files->currentItem()) { + Q3Url u = Q3Url(d->url, Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)files->currentItem())->info.name())); + updatePreviews(u); + } +} + +void Q3FileDialog::listBoxSelectionChanged() +{ + if (d->mode != ExistingFiles) + return; + + if (d->ignoreNextRefresh) { + d->ignoreNextRefresh = false; + return; + } + + nameEdit->clear(); + QString str; + Q3ListBoxItem * i = d->moreFiles->item(0); + Q3ListBoxItem * j = 0; + int index = 0; + files->blockSignals(true); + while(i) { + Q3FileDialogPrivate::MCItem *mcitem = (Q3FileDialogPrivate::MCItem *)i; + if (files && isVisible()) { + if (mcitem->i->isSelected() != mcitem->isSelected()) { + files->setSelected(mcitem->i, mcitem->isSelected()); + + // What happens here is that we want to emit signal highlighted for + // newly added items. But Q3ListBox apparently emits selectionChanged even + // when a user clicks on the same item twice. So, basically emulate the behaivor + // we have in the "Details" view which only emits highlighted the first time we + // click on the item. Perhaps at some point we should have a call to + // updateFileNameEdit(Q3ListViewItem) which also emits fileHighlighted() for + // ExistingFiles. For better or for worse, this clones the behaivor of the + // "Details" view quite well. + if (mcitem->isSelected() && i != d->lastEFSelected) { + Q3Url u(d->url, Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)(mcitem)->i)->info.name())); + d->lastEFSelected = i; + emit fileHighlighted(u.toString(false, false)); + } + } + } + if (d->moreFiles->isSelected(i) + && !((Q3FileDialogPrivate::File*)(mcitem)->i)->info.isDir()) { + str += QString(QLatin1String("\"%1\" ")).arg(i->text()); + if (j == 0) + j = i; + } + i = d->moreFiles->item(++index); + } + + files->blockSignals(false); + nameEdit->setText(str); + nameEdit->setCursorPosition(str.length()); + okB->setEnabled(true); + if (d->preview && d->preview->isVisible() && j) { + Q3Url u = Q3Url(d->url, + Q3FileDialogPrivate::encodeFileName(((Q3FileDialogPrivate::File*)((Q3FileDialogPrivate::MCItem*)j)->i)->info.name())); + updatePreviews(u); + } +} + +/*! \overload */ + +void Q3FileDialog::updateFileNameEdit(Q3ListBoxItem * newItem) +{ + if (!newItem) + return; + Q3FileDialogPrivate::MCItem * i = (Q3FileDialogPrivate::MCItem *)newItem; + if (i->i) { + i->i->listView()->setSelected(i->i, i->isSelected()); + updateFileNameEdit(i->i); + } +} + + +/*! Updates the dialog when the file name edit changes. */ + +void Q3FileDialog::fileNameEditDone() +{ + QUrlInfo f(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text())); + if (mode() != Q3FileDialog::ExistingFiles) { + Q3UrlOperator u(d->url, Q3FileDialogPrivate::encodeFileName(nameEdit->text())); + trySetSelection(f.isDir(), u, false); + if (d->preview && d->preview->isVisible()) + updatePreviews(u); + } +} + + + +/*! This private slot reacts to double-clicks in the list view. The item that +was double-clicked is specified in \a newItem */ + +void Q3FileDialog::selectDirectoryOrFile(Q3ListViewItem * newItem) +{ + + *workingDirectory = d->url; + detailViewMode = files->isVisible(); + updateLastSize(this); + + if (!newItem) + return; + + if (d->url.isLocalFile()) { + QFileInfo fi(d->url.path() + newItem->text(0)); +#if defined(Q_WS_WIN) + if (fi.isSymLink()) { + nameEdit->setText(fi.symLinkTarget()); + okClicked(); + return; + } +#endif + } + + Q3FileDialogPrivate::File * i = (Q3FileDialogPrivate::File *)newItem; + + QString oldName = nameEdit->text(); + if (i->info.isDir()) { + setUrl(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(i->info.name()) + QLatin1Char('/'))); + if (isDirectoryMode(mode())) { + QUrlInfo f (d->url.info(QString::fromLatin1("."))); + trySetSelection(f.isDir(), d->url, true); + } + } else if (newItem->isSelectable() && + trySetSelection(i->info.isDir(), Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(i->info.name())), true)) { + if (!isDirectoryMode(mode())) { + if (mode() == ExistingFile) { + if (Q3FileDialogPrivate::fileExists(d->url, nameEdit->text())) { + emit fileSelected(selectedFile()); + accept(); + } + } else { + emit fileSelected(selectedFile()); + accept(); + } + } + } else if (isDirectoryMode(d->mode)) { + d->currentFileName = d->url; + accept(); + } + if (!oldName.isEmpty() && !isDirectoryMode(mode())) + nameEdit->setText(oldName); +} + + +void Q3FileDialog::selectDirectoryOrFile(Q3ListBoxItem * newItem) +{ + if (!newItem) + return; + + Q3FileDialogPrivate::MCItem * i = (Q3FileDialogPrivate::MCItem *)newItem; + if (i->i) { + i->i->listView()->setSelected(i->i, i->isSelected()); + selectDirectoryOrFile(i->i); + } +} + + +void Q3FileDialog::popupContextMenu(Q3ListViewItem *item, const QPoint &p, + int) +{ + if (item) { + files->setCurrentItem(item); + files->setSelected(item, true); + } + + PopupAction action; + popupContextMenu(item ? item->text(0) : QString(), true, action, p); + + if (action == PA_Open) + selectDirectoryOrFile(item); + else if (action == PA_Rename) + files->startRename(false); + else if (action == PA_Delete) + deleteFile(item ? item->text(0) : QString()); + else if (action == PA_Reload) + rereadDir(); + else if (action == PA_Hidden) { + bShowHiddenFiles = !bShowHiddenFiles; + rereadDir(); + } else if (action == PA_SortName) { + sortFilesBy = (int)QDir::Name; + sortAscending = true; + resortDir(); + } else if (action == PA_SortSize) { + sortFilesBy = (int)QDir::Size; + sortAscending = true; + resortDir(); + } else if (action == PA_SortDate) { + sortFilesBy = (int)QDir::Time; + sortAscending = true; + resortDir(); + } else if (action == PA_SortUnsorted) { + sortFilesBy = (int)QDir::Unsorted; + sortAscending = true; + resortDir(); + } + +} + +void Q3FileDialog::popupContextMenu(Q3ListBoxItem *item, const QPoint & p) +{ + PopupAction action; + popupContextMenu(item ? item->text() : QString(), false, action, p); + + if (action == PA_Open) + selectDirectoryOrFile(item); + else if (action == PA_Rename) + d->moreFiles->startRename(false); + else if (action == PA_Delete) + deleteFile(item->text()); + else if (action == PA_Reload) + rereadDir(); + else if (action == PA_Hidden) { + bShowHiddenFiles = !bShowHiddenFiles; + rereadDir(); + } else if (action == PA_SortName) { + sortFilesBy = (int)QDir::Name; + sortAscending = true; + resortDir(); + } else if (action == PA_SortSize) { + sortFilesBy = (int)QDir::Size; + sortAscending = true; + resortDir(); + } else if (action == PA_SortDate) { + sortFilesBy = (int)QDir::Time; + sortAscending = true; + resortDir(); + } else if (action == PA_SortUnsorted) { + sortFilesBy = (int)QDir::Unsorted; + sortAscending = true; + resortDir(); + } +} + +void Q3FileDialog::popupContextMenu(const QString &filename, bool, + PopupAction &action, const QPoint &p) +{ + action = PA_Cancel; + + bool glob = filename.isEmpty(); + + Q3PopupMenu m(0, "file dialog context menu"); + m.setCheckable(true); + + if (!glob) { + QString okt; + if (QUrlInfo(d->url.info(filename.isEmpty() ? QString::fromLatin1(".") : fileName)).isDir()) { + okt = tr("&Open"); + } else { + if (mode() == AnyFile) + okt = tr("&Save"); + else + okt = tr("&Open"); + } + int ok = m.insertItem(okt); + + m.insertSeparator(); + int rename = m.insertItem(tr("&Rename")); + int del = m.insertItem(tr("&Delete")); + + if (filename.isEmpty() || !QUrlInfo(d->url.info(filename)).isWritable() || + filename == QLatin1String("..")) { + if (filename.isEmpty() || !QUrlInfo(d->url.info(filename)).isReadable()) + m.setItemEnabled(ok, false); + m.setItemEnabled(rename, false); + m.setItemEnabled(del, false); + } + + m.move(p); + int res = m.exec(QCursor::pos(), -1); + + if (res == ok) + action = PA_Open; + else if (res == rename) + action = PA_Rename; + else if (res == del) + action = PA_Delete; + } else { + int reload = m.insertItem(tr("R&eload")); + + Q3PopupMenu m2(0, "sort menu"); + + int sname = m2.insertItem(tr("Sort by &Name")); + //int stype = m2.insertItem(tr("Sort by &Type")); + int ssize = m2.insertItem(tr("Sort by &Size")); + int sdate = m2.insertItem(tr("Sort by &Date")); + m2.insertSeparator(); + int sunsorted = m2.insertItem(tr("&Unsorted")); + + //m2.setItemEnabled(stype, false); + + if (sortFilesBy == (int)QDir::Name) + m2.setItemChecked(sname, true); + else if (sortFilesBy == (int)QDir::Size) + m2.setItemChecked(ssize, true); +// else if (sortFilesBy == 0x16) +// m2.setItemChecked(stype, true); + else if (sortFilesBy == (int)QDir::Time) + m2.setItemChecked(sdate, true); + else if (sortFilesBy == (int)QDir::Unsorted) + m2.setItemChecked(sunsorted, true); + + m.insertItem(tr("Sort"), &m2); + + m.insertSeparator(); + + int hidden = m.insertItem(tr("Show &hidden files")); + m.setItemChecked(hidden, bShowHiddenFiles); + + m.move(p); + int res = m.exec(QCursor::pos(), -1); + + if (res == reload) + action = PA_Reload; + else if (res == hidden) + action = PA_Hidden; + else if (res == sname) + action = PA_SortName; +// else if (res == stype) +// action = PA_SortType; + else if (res == sdate) + action = PA_SortDate; + else if (res == ssize) + action = PA_SortSize; + else if (res == sunsorted) + action = PA_SortUnsorted; + } + +} + +void Q3FileDialog::deleteFile(const QString &filename) +{ + if (filename.isEmpty()) + return; + + QString encoded = Q3FileDialogPrivate::encodeFileName(filename); + QUrlInfo fi(d->url.info(encoded.isEmpty() ? QString::fromLatin1(".") : encoded)); + QString t = tr("the file"); + if (fi.isDir()) + t = tr("the directory"); + if (fi.isSymLink()) + t = tr("the symlink"); + + if (QMessageBox::warning(this, + tr("Delete %1").arg(t), + tr("Are you sure you wish to delete %1 \"%2\"?") + .arg(t).arg(filename), + tr("&Yes"), tr("&No"), QString(), 1) == 0) + d->url.remove(Q3FileDialogPrivate::encodeFileName(filename)); + +} + +void Q3FileDialog::fileSelected(int ) +{ + // unused +} + +void Q3FileDialog::fileHighlighted(int) +{ + // unused +} + +void Q3FileDialog::dirSelected(int) +{ + // unused +} + +void Q3FileDialog::pathSelected(int) +{ + // unused +} + + +void Q3FileDialog::cdUpClicked() +{ + QString oldName = nameEdit->text(); + setUrl(Q3UrlOperator(d->url, QLatin1String(".."))); + if (!oldName.isEmpty()) + nameEdit->setText(oldName); +} + +void Q3FileDialog::newFolderClicked() +{ + QString foldername(tr("New Folder 1")); + int i = 0; + QStringList lst; + Q3ListViewItemIterator it(files); + for (; it.current(); ++it) + if (it.current()->text(0).contains(tr("New Folder"))) + lst.append(it.current()->text(0)); + + if (!lst.count() == 0) + while (lst.contains(foldername)) + foldername = tr("New Folder %1").arg(++i); + + d->url.mkdir(foldername); +} + +void Q3FileDialog::createdDirectory(const QUrlInfo &info, Q3NetworkOperation *) +{ + resortDir(); + if (d->moreFiles->isVisible()) { + for (uint i = 0; i < d->moreFiles->count(); ++i) { + if (d->moreFiles->text(i) == info.name()) { + d->moreFiles->setCurrentItem(i); + d->moreFiles->startRename(false); + break; + } + } + } else { + Q3ListViewItem *item = files->firstChild(); + while (item) { + if (item->text(0) == info.name()) { + files->setSelected(item, true); + files->setCurrentItem(item); + files->startRename(false); + break; + } + item = item->nextSibling(); + } + } +} + + +/*! + This is a convenience static function that will return an existing directory + selected by the user. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 12 + + This function creates a modal file dialog called \a name, with + parent, \a parent. If parent is not 0, the dialog will be shown + centered over the parent. + + 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. + + If \a dirOnly is true, then only directories will be shown in + the file dialog; otherwise both directories and files will be shown. + + Under Unix/X11, the normal behavior of the file dialog is to resolve + and follow symlinks. For example, if /usr/tmp is a symlink to /var/tmp, + the file dialog will change to /var/tmp after entering /usr/tmp. + If \a resolveSymlinks is false, 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. + + \sa getOpenFileName(), getOpenFileNames(), getSaveFileName() +*/ + +QString Q3FileDialog::getExistingDirectory(const QString & dir, + QWidget *parent, + const char* name, + const QString& caption, + bool dirOnly, + bool resolveSymlinks) +{ + bool save_qt_resolve_symlinks = qt_resolve_symlinks; + qt_resolve_symlinks = resolveSymlinks; + + makeVariables(); + QString wd; + if (workingDirectory) + wd = *workingDirectory; + +#if defined(Q_WS_WIN) + QString initialDir; + if (!dir.isEmpty()) { + Q3UrlOperator u(dir); + if (QFileInfo(u.path()).isDir()) + initialDir = dir; + } else + initialDir.clear(); + if (qt_use_native_dialogs && qobject_cast(qApp->style()) && dirOnly) + return winGetExistingDirectory(initialDir, parent, name, caption); +#endif +#if defined(Q_WS_MAC) + if(qt_use_native_dialogs && qobject_cast(qApp->style())) + return macGetOpenFileNames(QLatin1String(""), 0, parent, name, caption, + 0, false, true).first().normalized(QString::NormalizationForm_C); +#endif + + Q3FileDialog *dlg = new Q3FileDialog(parent, name ? name : "qt_filedlg_ged", true); + + if (!caption.isNull()) + dlg->setWindowTitle(caption); + else + dlg->setWindowTitle(Q3FileDialog::tr("Find Directory")); + + dlg->setMode(dirOnly ? DirectoryOnly : Directory); + + dlg->d->types->clear(); + dlg->d->types->insertItem(Q3FileDialog::tr("Directories")); + dlg->d->types->setEnabled(false); + + QString dir_(dir); + dir_ = dir_.simplified(); + if (dir_.isEmpty() && !wd.isEmpty()) + dir_ = wd; + Q3UrlOperator u(dir_); + if (u.isLocalFile()) { + if (!dir_.isEmpty()) { + QFileInfo f(u.path()); + if (f.exists()) + if (f.isDir()) { + dlg->setDir(dir_); + wd = dir_; + } + } else if (!wd.isEmpty()) { + Q3Url tempUrl(wd); + QFileInfo f(tempUrl.path()); + if (f.isDir()) { + dlg->setDir(wd); + } + } else { + QString theDir = dir_; + if (theDir.isEmpty()) { + theDir = toRootIfNotExists( QDir::currentDirPath() ); + } if (!theDir.isEmpty()) { + Q3Url tempUrl(theDir); + QFileInfo f(tempUrl.path()); + if (f.isDir()) { + wd = theDir; + dlg->setDir(theDir); + } + } + } + } else { + dlg->setUrl(dir_); + } + + QString result; + dlg->setSelection(dlg->d->url.toString()); + + if (dlg->exec() == QDialog::Accepted) { + result = dlg->selectedFile(); + wd = result; + } + delete dlg; + + if (!result.isEmpty() && result.right(1) != QString(QLatin1Char('/'))) + result += QLatin1Char('/'); + + qt_resolve_symlinks = save_qt_resolve_symlinks; + + return result; +} + + +/*! + \property Q3FileDialog::mode + \brief the file dialog's mode + + The default mode is \l ExistingFile. +*/ + +void Q3FileDialog::setMode(Mode newMode) +{ + if (d->mode != newMode) { + d->mode = newMode; + QString sel = d->currentFileName; + int maxnamelen = 255; // _POSIX_MAX_PATH + if (isDirectoryMode(newMode)) { + files->setSelectionMode(Q3ListView::Single); + d->moreFiles->setSelectionMode(Q3ListBox::Single); + if (sel.isNull()) + sel = QString::fromLatin1("."); + d->types->setEnabled(false); + } else if (newMode == ExistingFiles) { + maxnamelen = INT_MAX; + files->setSelectionMode(Q3ListView::Extended); + d->moreFiles->setSelectionMode(Q3ListBox::Extended); + d->types->setEnabled(true); + } else { + files->setSelectionMode(Q3ListView::Single); + d->moreFiles->setSelectionMode(Q3ListBox::Single); + d->types->setEnabled(true); + } + nameEdit->setMaxLength(maxnamelen); + rereadDir(); + QUrlInfo f(d->url.info(QString(QLatin1Char('.')))); + trySetSelection(f.isDir(), d->url, false); + } + + QString okt; + bool changeFilters = false; + if (mode() == AnyFile) { + okt = tr("&Save"); + d->fileL->setText(tr("File &name:")); + if (d->types->count() == 1) { + d->types->setCurrentItem(0); + if (d->types->currentText() == QLatin1String("Directories")) { + changeFilters = true; + } + } + } + else if (mode() == Directory || mode() == DirectoryOnly) { + okt = tr("&OK"); + d->fileL->setText(tr("Directory:")); + d->types->clear(); + d->types->insertItem(tr("Directories")); + } + else { + okt = tr("&Open"); + d->fileL->setText(tr("File &name:")); + if (d->types->count() == 1) { + d->types->setCurrentItem(0); + if (d->types->currentText() == QLatin1String("Directories")) { + changeFilters = true; + } + } + } + + if (changeFilters) { + d->types->clear(); + d->types->insertItem(tr("All Files (*)")); + } + + okB->setText(okt); +} + +Q3FileDialog::Mode Q3FileDialog::mode() const +{ + return d->mode; +} + +/*! \reimp +*/ + +void Q3FileDialog::done(int i) +{ + if (i == QDialog::Accepted && (d->mode == ExistingFile || d->mode == ExistingFiles)) { + QStringList selection = selectedFiles(); + for (int f = 0; f < selection.count(); f++) { + QString file = selection[f]; + if (file.isNull()) + continue; + if (d->url.isLocalFile() && !QFile::exists(file)) { + QMessageBox::information(this, tr("Error"), + tr("%1\nFile not found.\nCheck path and filename.").arg(file)); + return; + } + } + } + QDialog::done(i); +} + +/*! + \property Q3FileDialog::viewMode + + \brief the file dialog's view mode + + If you set the view mode to be \e Detail (the default), then you + will see the file's details, such as the size of the file and the + date the file was last modified in addition to the file's name. + + If you set the view mode to be \e List, then you will just + see a list of the files and folders. + + See \l Q3FileDialog::ViewMode +*/ + + +Q3FileDialog::ViewMode Q3FileDialog::viewMode() const +{ + if (detailViewMode) + return Detail; + else + return List; +} + +void Q3FileDialog::setViewMode(ViewMode m) +{ + if (m == Detail) { + detailViewMode = true; + d->stack->raiseWidget(files); + d->detailView->setOn(true); + d->mcView->setOn(false); + } else if (m == List) { + detailViewMode = false; + d->stack->raiseWidget(d->moreFiles); + d->detailView->setOn(false); + d->mcView->setOn(true); + } +} + + +/*! + \property Q3FileDialog::previewMode + + \brief the preview mode for the file dialog + + If you set the mode to be a mode other than \e NoPreview, you must + use setInfoPreview() or setContentsPreview() to set the dialog's + preview widget to your preview widget and enable the preview + widget(s) with setInfoPreviewEnabled() or + setContentsPreviewEnabled(). + + \sa infoPreview, contentsPreview, viewMode +*/ + +void Q3FileDialog::setPreviewMode(PreviewMode m) +{ + if (m == NoPreview) { + d->previewInfo->setOn(false); + d->previewContents->setOn(false); + } else if (m == Info && d->infoPreview) { + d->previewInfo->setOn(true); + d->previewContents->setOn(false); + changeMode(d->modeButtons->id(d->previewInfo)); + } else if (m == Contents && d->contentsPreview) { + d->previewInfo->setOn(false); + d->previewContents->setOn(true); + changeMode(d->modeButtons->id(d->previewContents)); + } +} +Q3FileDialog::PreviewMode Q3FileDialog::previewMode() const +{ + if (d->infoPreview && d->infoPreviewWidget->isVisible()) + return Info; + else if (d->contentsPreview && d->contentsPreviewWidget->isVisible()) + return Contents; + + return NoPreview; +} + + +/*! + Adds the specified widgets to the bottom of the file dialog. The + label \a l is placed underneath the "file name" and the "file types" + labels. The widget \a w is placed underneath the file types combobox. + The button \a b is placed underneath the Cancel push button. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 13 + + If you don't want to have one of the widgets added, pass 0 in that + widget's position. + + Every time you call this function, a new row of widgets will be added + to the bottom of the file dialog. + + \sa addToolButton(), addLeftWidget(), addRightWidget() +*/ + +void Q3FileDialog::addWidgets(QLabel * l, QWidget * w, QPushButton * b) +{ + if (!l && !w && !b) + return; + + d->geometryDirty = true; + + QHBoxLayout *lay = new QHBoxLayout(); + d->extraWidgetsLayouts.append(lay); + d->topLevelLayout->addLayout(lay); + + if (!l) + l = new QLabel(this, "qt_intern_lbl"); + d->extraLabels.append(l); + lay->addWidget(l); + + if (!w) + w = new QWidget(this, "qt_intern_widget"); + d->extraWidgets.append(w); + lay->addWidget(w); + lay->addSpacing(15); + + if (b) { + d->extraButtons.append(b); + lay->addWidget(b); + } else { + QWidget *wid = new QWidget(this, "qt_extrabuttons_widget"); + d->extraButtons.append(wid); + lay->addWidget(wid); + } + + updateGeometries(); +} + +/*! + Adds the tool button \a b to the row of tool buttons at the top of the + file dialog. The button is appended to the right of + this row. If \a separator is true, a small space is inserted between the + last button of the row and the new button \a b. + + \sa addWidgets(), addLeftWidget(), addRightWidget() +*/ + +void Q3FileDialog::addToolButton(QAbstractButton *b, bool separator) +{ + if (!b || !d->buttonLayout) + return; + + d->geometryDirty = true; + + d->toolButtons.append(b); + if (separator) + d->buttonLayout->addSpacing(8); + d->buttonLayout->addWidget(b); + + updateGeometries(); +} + +/*! + Adds the widget \a w to the left-hand side of the file dialog. + + \sa addRightWidget(), addWidgets(), addToolButton() +*/ + +void Q3FileDialog::addLeftWidget(QWidget *w) +{ + if (!w) + return; + d->geometryDirty = true; + + d->leftLayout->addWidget(w); + d->leftLayout->addSpacing(5); + + updateGeometries(); +} + +/*! + Adds the widget \a w to the right-hand side of the file dialog. + + \sa addLeftWidget(), addWidgets(), addToolButton() +*/ + +void Q3FileDialog::addRightWidget(QWidget *w) +{ + if (!w) + return; + d->geometryDirty = true; + + d->rightLayout->addSpacing(5); + d->rightLayout->addWidget(w); + + updateGeometries(); +} + +/*! \reimp */ + +void Q3FileDialog::keyPressEvent(QKeyEvent * ke) +{ + if (!d->ignoreNextKeyPress && + ke && (ke->key() == Qt::Key_Enter || + ke->key() == Qt::Key_Return)) { + ke->ignore(); + if (d->paths->hasFocus()) { + ke->accept(); + if (d->url == Q3Url(d->paths->currentText())) + nameEdit->setFocus(); + } else if (d->types->hasFocus()) { + ke->accept(); + // ### is there a suitable condition for this? only valid + // wildcards? + nameEdit->setFocus(); + } else if (nameEdit->hasFocus()) { + if (d->currentFileName.isNull()) { + // maybe change directory + QUrlInfo i(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") :nameEdit->text())); + if (i.isDir()) { + nameEdit->setText(QString::fromLatin1("")); + setDir(Q3UrlOperator(d->url, Q3FileDialogPrivate::encodeFileName(i.name()))); + } + ke->accept(); + } else if (mode() == ExistingFiles) { + QUrlInfo i(d->url.info(nameEdit->text().isEmpty() ? QString::fromLatin1(".") : nameEdit->text())); + if (i.isFile()) { + Q3ListViewItem * i = files->firstChild(); + while (i && nameEdit->text() != i->text(0)) + i = i->nextSibling(); + if (i) + files->setSelected(i, true); + else + ke->accept(); // strangely, means to ignore that event + } + } + } else if (files->hasFocus() || d->moreFiles->hasFocus()) { + ke->accept(); + } + } else if (ke->key() == Qt::Key_Escape) { + ke->ignore(); + } + + d->ignoreNextKeyPress = false; + + if (!ke->isAccepted()) { + QDialog::keyPressEvent(ke); + } +} + + +/*! \class Q3FileIconProvider + + \brief The Q3FileIconProvider class provides icons for Q3FileDialog to + use. + + \compat + + By default Q3FileIconProvider is not used, but any application or + library can subclass it, reimplement pixmap() to return a suitable + icon, and make all Q3FileDialog objects use it by calling the static + function Q3FileDialog::setIconProvider(). + + It is advisable to make all the icons that Q3FileIconProvider returns be + the same size or at least the same width. This makes the list view + look much better. + + \sa Q3FileDialog +*/ + + +/*! Constructs an empty file icon provider called \a name, with the + parent \a parent. +*/ + +Q3FileIconProvider::Q3FileIconProvider(QObject * parent, const char* name) + : QObject(parent, name) +{ + // nothing necessary +} + + +/*! + Returns a pointer to a pixmap that should be used to + signify the file with the information \a info. + + If pixmap() returns 0, Q3FileDialog draws the default pixmap. + + The default implementation returns particular icons for files, directories, + link-files and link-directories. It returns a blank "icon" for other types. + + If you return a pixmap here, it should measure 16x16 pixels. +*/ + +const QPixmap * Q3FileIconProvider::pixmap(const QFileInfo & info) +{ + if (info.isSymLink()) { + if (info.isFile()) + return symLinkFileIcon; + else + return symLinkDirIcon; + } else if (info.isDir()) { + return closedFolderIcon; + } else if (info.isFile()) { + return fileIcon; + } else { + return fifteenTransparentPixels; + } +} + +/*! + Sets the Q3FileIconProvider used by the file dialog to \a provider. + + The default is that there is no Q3FileIconProvider and Q3FileDialog + just draws a folder icon next to each directory and nothing next + to files. + + \sa Q3FileIconProvider, iconProvider() +*/ + +void Q3FileDialog::setIconProvider(Q3FileIconProvider * provider) +{ + fileIconProvider = provider; +} + + +/*! + Returns a pointer to the icon provider currently set on the file dialog. + By default there is no icon provider, and this function returns 0. + + \sa setIconProvider(), Q3FileIconProvider +*/ + +Q3FileIconProvider * Q3FileDialog::iconProvider() +{ + return fileIconProvider; +} + + +#if defined(Q_WS_WIN) + +// ### FIXME: this code is duplicated in qdns.cpp +static QString getWindowsRegString(HKEY key, const QString &subKey) +{ + QString s; + QT_WA({ + char buf[1024]; + DWORD bsz = sizeof(buf); + int r = RegQueryValueEx(key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)buf, &bsz); + if (r == ERROR_SUCCESS) { + s = QString::fromUcs2((unsigned short *)buf); + } else if (r == ERROR_MORE_DATA) { + char *ptr = new char[bsz+1]; + r = RegQueryValueEx(key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)ptr, &bsz); + if (r == ERROR_SUCCESS) + s = QLatin1String(ptr); + delete [] ptr; + } + } , { + char buf[512]; + DWORD bsz = sizeof(buf); + int r = RegQueryValueExA(key, subKey.local8Bit(), 0, 0, (LPBYTE)buf, &bsz); + if (r == ERROR_SUCCESS) { + s = QLatin1String(buf); + } else if (r == ERROR_MORE_DATA) { + char *ptr = new char[bsz+1]; + r = RegQueryValueExA(key, subKey.local8Bit(), 0, 0, (LPBYTE)ptr, &bsz); + if (r == ERROR_SUCCESS) + s = QLatin1String(ptr); + delete [] ptr; + } + }); + return s; +} + +QPixmap fromHICON(HICON hIcon) +{ + ICONINFO icoInfo; + if (GetIconInfo(hIcon, &icoInfo) && icoInfo.hbmColor) { + return QPixmap::fromWinHBITMAP(icoInfo.hbmColor); + } + return QPixmap(); +} + +QWindowsIconProvider::QWindowsIconProvider(QObject *parent, const char *name) + : Q3FileIconProvider(parent, name) +{ + pixw = GetSystemMetrics(SM_CXSMICON); + pixh = GetSystemMetrics(SM_CYSMICON); + + HKEY k; + HICON si; + int r; + QString s; + UINT res = 0; + + // ---------- get default folder pixmap + const wchar_t iconFolder[] = L"folder\\DefaultIcon"; // workaround for Borland + QT_WA({ + r = RegOpenKeyEx(HKEY_CLASSES_ROOT, + iconFolder, + 0, KEY_READ, &k); + } , { + r = RegOpenKeyExA(HKEY_CLASSES_ROOT, + "folder\\DefaultIcon", + 0, KEY_READ, &k); + }); + resolveLibs(); + if (r == ERROR_SUCCESS) { + s = getWindowsRegString(k, QString()); + RegCloseKey(k); + + QStringList lst = QStringList::split(QLatin1String(","), s); + + if (lst.count() >= 2) { // don't just assume that lst has two entries +#ifndef Q_OS_WINCE + QT_WA({ + res = ptrExtractIconEx((TCHAR*)lst[0].simplifyWhiteSpace().ucs2(), + lst[1].simplifyWhiteSpace().toInt(), + 0, &si, 1); + } , { + res = ExtractIconExA(lst[0].simplifyWhiteSpace().local8Bit(), + lst[1].simplifyWhiteSpace().toInt(), + 0, &si, 1); + }); +#else + res = (UINT)ExtractIconEx((TCHAR*)lst[0].simplifyWhiteSpace().ucs2(), + lst[1].simplifyWhiteSpace().toInt(), + 0, &si, 1); +#endif + } + + if (res) { + defaultFolder = fromHICON(si); + defaultFolder.setMask(defaultFolder.createHeuristicMask()); + *closedFolderIcon = defaultFolder; + DestroyIcon(si); + } else { + defaultFolder = *closedFolderIcon; + } + } else { + RegCloseKey(k); + } + + //------------------------------- get default file pixmap +#ifndef Q_OS_WINCE + QT_WA({ + res = ptrExtractIconEx(L"shell32.dll", + 0, 0, &si, 1); + } , { + res = ExtractIconExA("shell32.dll", + 0, 0, &si, 1); + }); +#else + res = (UINT)ExtractIconEx(L"shell32.dll", + 0, 0, &si, 1); +#endif + + if (res) { + defaultFile = fromHICON(si); + defaultFile.setMask(defaultFile.createHeuristicMask()); + *fileIcon = defaultFile; + DestroyIcon(si); + } else { + defaultFile = *fileIcon; + } + + //------------------------------- get default exe pixmap +#ifndef Q_OS_WINCE + QT_WA({ + res = ptrExtractIconEx(L"shell32.dll", + 2, 0, &si, 1); + } , { + res = ExtractIconExA("shell32.dll", + 2, 0, &si, 1); + }); +#else + res = (UINT)ExtractIconEx(L"ceshell.dll", + 10, 0, &si, 1); +#endif + + if (res) { + defaultExe = fromHICON(si); + defaultExe.setMask(defaultExe.createHeuristicMask()); + DestroyIcon(si); + } else { + defaultExe = *fileIcon; + } +} + +QWindowsIconProvider::~QWindowsIconProvider() +{ + if (this == fileIconProvider) + fileIconProvider = 0; +} + +const QPixmap * QWindowsIconProvider::pixmap(const QFileInfo &fi) +{ + if (fi.isSymLink()) { + QString real = fi.symLinkTarget(); + if (!real.isEmpty()) + return pixmap(QFileInfo(real)); + } + + QString ext = fi.extension(false).upper(); + QString key = ext; + ext.prepend(QLatin1String(".")); + QMap< QString, QPixmap >::Iterator it; + + if (fi.isDir()) { + return &defaultFolder; + } else if (ext.toLower() != QLatin1String(".exe")) { + it = cache.find(key); + if (it != cache.end()) + return &(*it); + + HKEY k, k2; + int r; + QT_WA({ + r = RegOpenKeyEx(HKEY_CLASSES_ROOT, (TCHAR*)ext.ucs2(), + 0, KEY_READ, &k); + } , { + r = RegOpenKeyExA(HKEY_CLASSES_ROOT, ext.local8Bit(), + 0, KEY_READ, &k); + }); + QString s; + if (r == ERROR_SUCCESS) { + s = getWindowsRegString(k, QString()); + } else { + cache[key] = defaultFile; + RegCloseKey(k); + return &defaultFile; + } + RegCloseKey(k); + + QT_WA({ + r = RegOpenKeyEx(HKEY_CLASSES_ROOT, (TCHAR*)QString(s + QLatin1String("\\DefaultIcon")).ucs2(), + 0, KEY_READ, &k2); + } , { + r = RegOpenKeyExA(HKEY_CLASSES_ROOT, QString(s + QLatin1String("\\DefaultIcon")).local8Bit() , + 0, KEY_READ, &k2); + }); + if (r == ERROR_SUCCESS) { + s = getWindowsRegString(k2, QString()); + } else { + cache[key] = defaultFile; + RegCloseKey(k2); + return &defaultFile; + } + RegCloseKey(k2); + + if (s.isEmpty()) + return &defaultFile; + + QStringList lst = QStringList::split(QLatin1String(","), s); + + HICON si; + UINT res = 0; + if (lst.count() >= 2) { // don't just assume that lst has two entries + QString filepath = lst[0].stripWhiteSpace(); + if (!filepath.isEmpty()) { + if (filepath.find(QLatin1String("%1")) != -1) { + filepath = filepath.arg(fi.filePath()); + if (ext.toLower() == QLatin1String(".dll")) { + pix = defaultFile; + return &pix; + } + } + if (filepath[0] == QLatin1Char('"') && filepath[(int)filepath.length()-1] == QLatin1Char('"')) + filepath = filepath.mid(1, filepath.length()-2); + + resolveLibs(); +#ifndef Q_OS_WINCE + QT_WA({ + res = ptrExtractIconEx((TCHAR*)filepath.ucs2(), lst[1].stripWhiteSpace().toInt(), + 0, &si, 1); + } , { + res = ExtractIconExA(filepath.local8Bit(), lst[1].stripWhiteSpace().toInt(), + 0, &si, 1); + }); +#else + res = (UINT)ExtractIconEx((TCHAR*)filepath.ucs2(), lst[1].stripWhiteSpace().toInt(), + 0, &si, 1); +#endif + } + } + if (res) { + pix = fromHICON(si); + pix.setMask(pix.createHeuristicMask()); + DestroyIcon(si); + } else { + pix = defaultFile; + } + + cache[key] = pix; + return &pix; + } else { + HICON si; + UINT res = 0; + if (!fi.absFilePath().isEmpty()) { +#ifndef Q_OS_WINCE + QT_WA({ + res = ptrExtractIconEx((TCHAR*)fi.absFilePath().ucs2(), -1, + 0, 0, 1); + } , { + res = ExtractIconExA(fi.absFilePath().local8Bit(), -1, + 0, 0, 1); + }); + + if (res) { + QT_WA({ + res = ptrExtractIconEx((TCHAR*)fi.absFilePath().ucs2(), res - 1, + 0, &si, 1); + } , { + res = ExtractIconExA(fi.absFilePath().local8Bit(), res - 1, + 0, &si, 1); + }); + } +#else + res = (UINT)ExtractIconEx((TCHAR*)fi.absFilePath().ucs2(), -1, + 0, 0, 1); + if (res) + res = (UINT)ExtractIconEx((TCHAR*)fi.absFilePath().ucs2(), res - 1, + 0, &si, 1); +#endif + + } + + if (res) { + pix = fromHICON(si); + pix.setMask(pix.createHeuristicMask()); + DestroyIcon(si); + } else { + pix = defaultExe; + } + + return &pix; + } + + // can't happen! + return 0; +} +#endif + + + +/*! + \reimp +*/ +bool Q3FileDialog::eventFilter(QObject * o, QEvent * e) +{ + if (e->type() == QEvent::KeyPress && ((QKeyEvent*)e)->key() == Qt::Key_F5) { + rereadDir(); + ((QKeyEvent *)e)->accept(); + return true; + } else if (e->type() == QEvent::KeyPress && ((QKeyEvent*)e)->key() == Qt::Key_F2 && + (o == files || o == files->viewport())) { + if (files->isVisible() && files->currentItem()) { + if (QUrlInfo(d->url.info(QString(QLatin1Char('.')))).isWritable() && files->currentItem()->text(0) != QLatin1String("..")) { + files->renameItem = files->currentItem(); + files->startRename(true); + } + } + ((QKeyEvent *)e)->accept(); + return true; + } else if (e->type() == QEvent::KeyPress && ((QKeyEvent*)e)->key() == Qt::Key_F2 && + (o == d->moreFiles || o == d->moreFiles->viewport())) { + if (d->moreFiles->isVisible() && d->moreFiles->currentItem() != -1) { + if (QUrlInfo(d->url.info(QString(QLatin1Char('.')))).isWritable() && + d->moreFiles->item(d->moreFiles->currentItem())->text() != QLatin1String("..")) { + d->moreFiles->renameItem = d->moreFiles->item(d->moreFiles->currentItem()); + d->moreFiles->startRename(true); + } + } + ((QKeyEvent *)e)->accept(); + return true; + } else if (e->type() == QEvent::KeyPress && d->moreFiles->renaming) { + d->moreFiles->lined->setFocus(); + QApplication::sendEvent(d->moreFiles->lined, e); + ((QKeyEvent *)e)->accept(); + return true; + } else if (e->type() == QEvent::KeyPress && files->renaming) { + files->lined->setFocus(); + QApplication::sendEvent(files->lined, e); + ((QKeyEvent *)e)->accept(); + return true; + } else if (e->type() == QEvent::KeyPress && + ((QKeyEvent *)e)->key() == Qt::Key_Backspace && + (o == files || + o == d->moreFiles || + o == files->viewport() || + o == d->moreFiles->viewport())) { + cdUpClicked(); + ((QKeyEvent *)e)->accept(); + return true; + } else if (e->type() == QEvent::KeyPress && + ((QKeyEvent *)e)->key() == Qt::Key_Delete && + (o == files || + o == files->viewport())) { + if (files->currentItem()) + deleteFile(files->currentItem()->text(0)); + ((QKeyEvent *)e)->accept(); + return true; + } else if (e->type() == QEvent::KeyPress && + ((QKeyEvent *)e)->key() == Qt::Key_Delete && + (o == d->moreFiles || + o == d->moreFiles->viewport())) { + int c = d->moreFiles->currentItem(); + if (c >= 0) + deleteFile(d->moreFiles->item(c)->text()); + ((QKeyEvent *)e)->accept(); + return true; + } else if (o == files && e->type() == QEvent::FocusOut && files->currentItem()) { + } else if (o == files && e->type() == QEvent::KeyPress) { + QTimer::singleShot(0, this, SLOT(fixupNameEdit())); + } else if (o == nameEdit && e->type() == QEvent::KeyPress && d->mode != AnyFile) { + if ((nameEdit->cursorPosition() == (int)nameEdit->text().length() || nameEdit->hasSelectedText()) && + isprint(((QKeyEvent *)e)->ascii())) { +#if defined(Q_WS_WIN) + QString nt(nameEdit->text().toLower()); +#else + QString nt(nameEdit->text()); +#endif + nt.truncate(nameEdit->cursorPosition()); + nt += QLatin1Char((char)(((QKeyEvent *)e)->ascii())); + Q3ListViewItem * i = files->firstChild(); +#if defined(Q_WS_WIN) + while(i && i->text(0).left(nt.length()).toLower() != nt) +#else + while(i && i->text(0).left(nt.length()) != nt) +#endif + i = i->nextSibling(); + if (i) { + nt = i->text(0); + int cp = nameEdit->cursorPosition()+1; + nameEdit->validateAndSet(nt, cp, cp, nt.length()); + return true; + } + } + } else if (o == nameEdit && e->type() == QEvent::FocusIn) { + fileNameEditDone(); + } else if (d->moreFiles->renaming && o != d->moreFiles->lined && e->type() == QEvent::FocusIn) { + d->moreFiles->lined->setFocus(); + return true; + } else if (files->renaming && o != files->lined && e->type() == QEvent::FocusIn) { + files->lined->setFocus(); + return true; + } else if ((o == d->moreFiles || o == d->moreFiles->viewport()) && + e->type() == QEvent::FocusIn) { + if ((o == d->moreFiles->viewport() && !d->moreFiles->viewport()->hasFocus()) + || (o == d->moreFiles && !d->moreFiles->hasFocus())) + ((QWidget*)o)->setFocus(); + return false; + } + + return QDialog::eventFilter(o, e); +} + +/*! + Sets the filters used in the file dialog to \a filters. Each group + of filters must be separated by \c{;;} (\e two semicolons). + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 14 + +*/ + +void Q3FileDialog::setFilters(const QString &filters) +{ + QStringList lst = makeFiltersList(filters); + setFilters(lst); +} + +/*! + \overload + + \a types must be a null-terminated list of strings. + +*/ + +void Q3FileDialog::setFilters(const char ** types) +{ + if (!types || !*types) + return; + + d->types->clear(); + while(types && *types) { + d->types->insertItem(QString::fromLatin1(*types)); + types++; + } + d->types->setCurrentItem(0); + setFilter(d->types->text(0)); +} + + +/*! + \overload + + \a types is a list of filter strings. +*/ + +void Q3FileDialog::setFilters(const QStringList & types) +{ + if (types.count() < 1) + return; + + d->types->clear(); + for (QStringList::ConstIterator it = types.begin(); it != types.end(); ++it) + d->types->insertItem(*it); + d->types->setCurrentItem(0); + setFilter(d->types->text(0)); +} + +/*! + Adds the filter \a filter to the list of filters and makes it the + current filter. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 15 + + In the above example, a file dialog is created, and the file filter "Images + (*.png *.jpg *.xpm)" is added and is set as the current filter. The original + filter, "All Files (*)", is still available. + + \sa setFilter(), setFilters() +*/ + +void Q3FileDialog::addFilter(const QString &filter) +{ + if (filter.isEmpty()) + return; + QString f = filter; + QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp)); + int index = r.indexIn(f); + if (index >= 0) + f = r.cap(2); + for (int i = 0; i < d->types->count(); ++i) { + QString f2(d->types->text(i)); + int index = r.indexIn(f2); + if (index >= 0) + f2 = r.cap(1); + if (f2 == f) { + d->types->setCurrentItem(i); + setFilter(f2); + return; + } + } + + d->types->insertItem(filter); + d->types->setCurrentItem(d->types->count() - 1); + setFilter(d->types->text(d->types->count() - 1)); +} + +/*! + Since modeButtons is a top-level widget, it may be destroyed by the + kernel at application exit. Notice if this happens to + avoid double deletion. +*/ + +void Q3FileDialog::modeButtonsDestroyed() +{ + if (d) + d->modeButtons = 0; +} + + +/*! + This is a convenience static function that will return one or more + existing files selected by the user. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 16 + + This function creates a modal file dialog called \a name, with + parent \a parent. If \a parent is not 0, the dialog will be shown + centered over the parent. + + 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. + + 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 Q3FileDialog, unless the style of the application + is set to something other than the native style. (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 /usr/tmp is a symlink to /var/tmp, + the file dialog will change to /var/tmp after entering /usr/tmp. + If \a resolveSymlinks is false, the file dialog will treat + symlinks as regular directories. + + Note that if you want to iterate over the list of files, you should + iterate over a copy, e.g. + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 17 + + \sa getOpenFileName(), getSaveFileName(), getExistingDirectory() +*/ + +QStringList Q3FileDialog::getOpenFileNames(const QString & filter, + const QString& dir, + QWidget *parent, + const char* name, + const QString& caption, + QString *selectedFilter, + bool resolveSymlinks) +{ + bool save_qt_resolve_symlinks = qt_resolve_symlinks; + qt_resolve_symlinks = resolveSymlinks; + + QStringList filters; + if (!filter.isEmpty()) + filters = makeFiltersList(filter); + + makeVariables(); + + if (workingDirectory->isNull()) + *workingDirectory = toRootIfNotExists( QDir::currentDirPath() ); + + if (!dir.isEmpty()) { + // #### works only correct for local files + Q3UrlOperator u(Q3FileDialogPrivate::encodeFileName(dir)); + if (u.isLocalFile() && QFileInfo(u.path()).isDir()) { + *workingDirectory = dir; + } else { + *workingDirectory = u.toString(); + } + } + +#if defined(Q_WS_WIN) + if (qt_use_native_dialogs && qobject_cast(qApp->style())) + return winGetOpenFileNames(filter, workingDirectory, parent, name, caption, selectedFilter); +#elif defined(Q_WS_MAC) + if (qt_use_native_dialogs && qobject_cast(qApp->style())) { + QStringList sl = macGetOpenFileNames(filter, dir.isEmpty() ? 0 : workingDirectory, + parent, name, caption, selectedFilter); + for (int i = 0; i < sl.count(); ++i) + sl.replace(i, sl.at(i).normalized(QString::NormalizationForm_C)); + return sl; + } +#endif + + Q3FileDialog *dlg = new Q3FileDialog(*workingDirectory, QString(), parent, name ? name : "qt_filedlg_gofns", true); + + if (!caption.isNull()) + dlg->setWindowTitle(caption); + else + dlg->setWindowTitle(Q3FileDialog::tr("Open")); + + dlg->setFilters(filters); + if (selectedFilter) + dlg->setFilter(*selectedFilter); + dlg->setMode(Q3FileDialog::ExistingFiles); + QString result; + QStringList lst; + if (dlg->exec() == QDialog::Accepted) { + lst = dlg->selectedFiles(); + *workingDirectory = dlg->d->url; + if (selectedFilter) + *selectedFilter = dlg->selectedFilter(); + } + delete dlg; + + qt_resolve_symlinks = save_qt_resolve_symlinks; + + return lst; +} + +/*! Updates the line edit to match the speed-key usage in Q3ListView. */ + +void Q3FileDialog::fixupNameEdit() +{ + if (files->currentItem()) { + if (((Q3FileDialogPrivate::File*)files->currentItem())->info.isFile()) + nameEdit->setText(files->currentItem()->text(0)); + } +} + +/*! + Returns the URL of the current working directory in the file dialog. + + \sa setUrl() +*/ + +Q3Url Q3FileDialog::url() const +{ + return d->url; +} + +static bool isRoot(const Q3Url &u) +{ +#if defined(Q_OS_UNIX) + if (u.path() == QString(QLatin1Char('/'))) + return true; +#elif defined(Q_OS_WIN32) + QString p = u.path(); + if (p.length() == 3 && + p.right(2) == QLatin1String(":/")) + return true; + if (p[0] == QLatin1Char('/') && p[1] == QLatin1Char('/')) { + int slashes = p.count(QLatin1Char('/')); + if (slashes <= 3) + return true; + if (slashes == 4 && p[(int)p.length() - 1] == QLatin1Char('/')) + return true; + } +#else +#if defined(Q_CC_GNU) +#warning "case not covered.." +#endif +#endif + + if (!u.isLocalFile() && u.path() == QString(QLatin1Char('/'))) + return true; + + return false; +} + +#if defined(Q_WS_WIN) +extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; +#endif + +void Q3FileDialog::urlStart(Q3NetworkOperation *op) +{ + if (!op) + return; + +#if defined(Q_WS_WIN) + old_qt_ntfs_permission_lookup = qt_ntfs_permission_lookup; + qt_ntfs_permission_lookup = 0; +#endif + if (op->operation() == Q3NetworkProtocol::OpListChildren) { +#ifndef QT_NO_CURSOR + if (!d->cursorOverride) { + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + d->cursorOverride = true; + } +#endif + if (isRoot(d->url)) + d->cdToParent->setEnabled(false); + else + d->cdToParent->setEnabled(true); + d->mimeTypeTimer->stop(); + d->sortedList.clear(); + d->pendingItems.clear(); + d->moreFiles->clearSelection(); + files->clearSelection(); + d->moreFiles->clear(); + files->clear(); + files->setSorting(-1); + + QString s = d->url.toString(false, false); + bool found = false; + for (int i = 0; i < d->paths->count(); ++i) { +#if defined(Q_WS_WIN) + if (d->paths->text(i).toLower() == s.toLower()) { +#else + if (d->paths->text(i) == s) { +#endif + found = true; + d->paths->setCurrentItem(i); + break; + } + } + if (!found) { + d->paths->insertItem(*openFolderIcon, s, -1); + d->paths->setCurrentItem(d->paths->count() - 1); + } + d->last = 0; + d->hadDotDot = false; + + if (d->goBack && (d->history.isEmpty() || d->history.last() != d->url.toString())) { + d->history.append(d->url.toString()); + if (d->history.count() > 1) + d->goBack->setEnabled(true); + } + } +} + +void Q3FileDialog::urlFinished(Q3NetworkOperation *op) +{ + if (!op) + return; + +#ifndef QT_NO_CURSOR + if (op->operation() == Q3NetworkProtocol::OpListChildren && + d->cursorOverride) { + QApplication::restoreOverrideCursor(); + d->cursorOverride = false; + } +#endif + + if (op->state() == Q3NetworkProtocol::StFailed) { + if (d->paths->hasFocus()) + d->ignoreNextKeyPress = true; + + if (d->progressDia) { + d->ignoreStop = true; + d->progressDia->close(); + delete d->progressDia; + d->progressDia = 0; + } + + int ecode = op->errorCode(); + QMessageBox::critical(this, tr("Error"), op->protocolDetail()); + + if (ecode == Q3NetworkProtocol::ErrListChildren || ecode == Q3NetworkProtocol::ErrParse || + ecode == Q3NetworkProtocol::ErrUnknownProtocol || ecode == Q3NetworkProtocol::ErrLoginIncorrect || + ecode == Q3NetworkProtocol::ErrValid || ecode == Q3NetworkProtocol::ErrHostNotFound || + ecode == Q3NetworkProtocol::ErrFileNotExisting) { + d->url = d->oldUrl; + rereadDir(); + } else { + // another error happened, no need to go back to last dir + } + } else if (op->operation() == Q3NetworkProtocol::OpListChildren && + op == d->currListChildren) { + if (!d->hadDotDot && !isRoot(d->url)) { + bool ok = true; +#if defined(Q_WS_WIN) + if (d->url.path().left(2) == QLatin1String("//")) + ok = false; +#endif + if (ok) { + QUrlInfo ui(d->url.info(QLatin1String(".."))); + ui.setName(QLatin1String("..")); + ui.setDir(true); + ui.setFile(false); + ui.setSymLink(false); + ui.setSize(0); + Q3ValueList lst; + lst << ui; + insertEntry(lst, 0); + } + } + resortDir(); + } else if (op->operation() == Q3NetworkProtocol::OpGet) { + } else if (op->operation() == Q3NetworkProtocol::OpPut) { + rereadDir(); + if (d->progressDia) { + d->ignoreStop = true; + d->progressDia->close(); + } + delete d->progressDia; + d->progressDia = 0; + } + +#if defined(Q_WS_WIN) + qt_ntfs_permission_lookup = old_qt_ntfs_permission_lookup; +#endif +} + +void Q3FileDialog::dataTransferProgress(int bytesDone, int bytesTotal, Q3NetworkOperation *op) +{ + if (!op) + return; + + QString label; + Q3Url u(op->arg(0)); + if (u.isLocalFile()) { + label = u.path(); + } else { + label = QLatin1String("%1 (on %2)"); + label = label.arg(u.path()).arg(u.host()); + } + + if (!d->progressDia) { + if (bytesDone < bytesTotal) { + d->ignoreStop = false; + d->progressDia = new QFDProgressDialog(this, label, bytesTotal); + connect(d->progressDia, SIGNAL(cancelled()), + this, SLOT(stopCopy())); + d->progressDia->show(); + } else + return; + } + + if (d->progressDia) { + if (op->operation() == Q3NetworkProtocol::OpGet) { + if (d->progressDia) { + d->progressDia->setReadProgress(bytesDone); + } + } else if (op->operation() == Q3NetworkProtocol::OpPut) { + if (d->progressDia) { + d->progressDia->setWriteLabel(label); + d->progressDia->setWriteProgress(bytesDone); + } + } else { + return; + } + } +} + +void Q3FileDialog::insertEntry(const Q3ValueList &lst, Q3NetworkOperation *op) +{ + if (op && op->operation() == Q3NetworkProtocol::OpListChildren && + op != d->currListChildren) + return; + Q3ValueList::ConstIterator it = lst.begin(); + for (; it != lst.end(); ++it) { + const QUrlInfo &inf = *it; + if (d->mode == DirectoryOnly && !inf.isDir()) + continue; + if (inf.name() == QLatin1String("..")) { + d->hadDotDot = true; + if (isRoot(d->url)) + continue; +#if defined(Q_WS_WIN) + if (d->url.path().left(2) == QLatin1String("//")) + continue; +#endif + } else if (inf.name() == QString(QLatin1Char('.'))) + continue; + +#if defined(Q_WS_WIN) + // Workaround a Windows bug, '..' is apparantly hidden in directories + // that are one level away from root + if (!bShowHiddenFiles && inf.name() != QLatin1String("..")) { + if (d->url.isLocalFile()) { + QString file = d->url.path(); + if (!file.endsWith(QLatin1String("/"))) + file.append(QLatin1String("/")); + file += inf.name(); + QT_WA({ + if (GetFileAttributesW((TCHAR*)file.ucs2()) & FILE_ATTRIBUTE_HIDDEN) + continue; + } , { + if (GetFileAttributesA(file.local8Bit()) & FILE_ATTRIBUTE_HIDDEN) + continue; + }); + } else { + if (inf.name() != QLatin1String("..") && inf.name()[0] == QLatin1Char('.')) + continue; + } + } +#else + if (!bShowHiddenFiles && inf.name() != QLatin1String("..")) { + if (inf.name()[0] == QLatin1Char('.')) + continue; + } +#endif + if (!d->url.isLocalFile()) { + Q3FileDialogPrivate::File * i = 0; + Q3FileDialogPrivate::MCItem *i2 = 0; + i = new Q3FileDialogPrivate::File(d, &inf, files); + i2 = new Q3FileDialogPrivate::MCItem(d->moreFiles, i); + + if ((d->mode == ExistingFiles && inf.isDir()) + || (isDirectoryMode(d->mode) && inf.isFile())) { + i->setSelectable(false); + i2->setSelectable(false); + } + + i->i = i2; + } + + d->sortedList.append(new QUrlInfo(inf)); + } +} + +void Q3FileDialog::removeEntry(Q3NetworkOperation *op) +{ + if (!op) + return; + + QUrlInfo *i = 0; + Q3ListViewItemIterator it(files); + bool ok1 = false, ok2 = false; + for (i = d->sortedList.first(); it.current(); ++it, i = d->sortedList.next()) { + QString encName = Q3FileDialogPrivate::encodeFileName( + ((Q3FileDialogPrivate::File*)it.current())->info.name()); + if (encName == op->arg(0)) { + d->pendingItems.removeRef((Q3FileDialogPrivate::File*)it.current()); + delete ((Q3FileDialogPrivate::File*)it.current())->i; + delete it.current(); + ok1 = true; + } + if (i && i->name() == op->arg(0)) { + d->sortedList.removeRef(i); + i = d->sortedList.prev(); + ok2 = true; + } + if (ok1 && ok2) + break; + } +} + +void Q3FileDialog::itemChanged(Q3NetworkOperation *op) +{ + if (!op) + return; + + QUrlInfo *i = 0; + Q3ListViewItemIterator it1(files); + bool ok1 = false, ok2 = false; + // first check whether the new file replaces an existing file. + for (i = d->sortedList.first(); it1.current(); ++it1, i = d->sortedList.next()) { + if (((Q3FileDialogPrivate::File*)it1.current())->info.name() == op->arg(1)) { + delete ((Q3FileDialogPrivate::File*)it1.current())->i; + delete it1.current(); + ok1 = true; + } + if (i && i->name() == op->arg(1)) { + d->sortedList.removeRef(i); + i = d->sortedList.prev(); + ok2 = true; + } + if (ok1 && ok2) + break; + } + + i = 0; + Q3ListViewItemIterator it(files); + ok1 = false; + ok2 = false; + for (i = d->sortedList.first(); it.current(); ++it, i = d->sortedList.next()) { + if (((Q3FileDialogPrivate::File*)it.current())->info.name() == op->arg(0)) { + ((Q3FileDialogPrivate::File*)it.current())->info.setName(op->arg(1)); + ok1 = true; + } + if (i && i->name() == op->arg(0)) { + i->setName(op->arg(1)); + ok2 = true; + } + if (ok1 && ok2) + break; + } + + resortDir(); +} + +/*! + \property Q3FileDialog::infoPreview + + \brief whether the file dialog can provide preview information about + the currently selected file + + The default is false. +*/ +bool Q3FileDialog::isInfoPreviewEnabled() const +{ + return d->infoPreview; +} + +void Q3FileDialog::setInfoPreviewEnabled(bool info) +{ + if (info == d->infoPreview) + return; + d->geometryDirty = true; + d->infoPreview = info; + updateGeometries(); +} + + +/*! + \property Q3FileDialog::contentsPreview + + \brief whether the file dialog can provide a contents preview of the + currently selected file + + The default is false. + + \sa setContentsPreview() setInfoPreviewEnabled() +*/ +// ### improve the above documentation: how is the preview done, how can I add +// support for customized preview, etc. + +bool Q3FileDialog::isContentsPreviewEnabled() const +{ + return d->contentsPreview; +} + +void Q3FileDialog::setContentsPreviewEnabled(bool contents) +{ + if (contents == d->contentsPreview) + return; + d->geometryDirty = true; + d->contentsPreview = contents; + updateGeometries(); +} + + +/*! + Sets the widget to be used for displaying information about the file + to the widget \a w and a preview of that information to the + Q3FilePreview \a preview. + + Normally you would create a preview widget that derives from both QWidget and + Q3FilePreview, so you should pass the same widget twice. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 18 + + \sa setContentsPreview(), setInfoPreviewEnabled(), setPreviewMode() + +*/ + +void Q3FileDialog::setInfoPreview(QWidget *w, Q3FilePreview *preview) +{ + if (!w || !preview) + return; + + if (d->infoPreviewWidget) { + d->preview->removeWidget(d->infoPreviewWidget); + if ((void*)d->infoPreviewer == (void*)d->infoPreviewWidget) + d->infoPreviewer = 0; + delete d->infoPreviewWidget; + } + if (d->infoPreviewer) + delete d->infoPreviewer; + d->infoPreviewWidget = w; + d->infoPreviewer = preview; + w->reparent(d->preview, 0, QPoint(0, 0)); +} + +/*! + Sets the widget to be used for displaying the contents of the file + to the widget \a w and a preview of those contents to the + Q3FilePreview \a preview. + + Normally you would create a preview widget that derives from both QWidget and + Q3FilePreview, so you should pass the same widget twice. + + \snippet doc/src/snippets/code/src_qt3support_dialogs_q3filedialog.cpp 19 + + \sa setContentsPreviewEnabled(), setInfoPreview(), setPreviewMode() +*/ + +void Q3FileDialog::setContentsPreview(QWidget *w, Q3FilePreview *preview) +{ + if (!w || !preview) + return; + + if (d->contentsPreviewWidget) { + d->preview->removeWidget(d->contentsPreviewWidget); + if ((void*)d->contentsPreviewWidget == (void*)d->contentsPreviewer) + d->contentsPreviewer = 0; + delete d->contentsPreviewWidget; + } + if (d->contentsPreviewer) + delete d->contentsPreviewer; + d->contentsPreviewWidget = w; + d->contentsPreviewer = preview; + w->reparent(d->preview, 0, QPoint(0, 0)); +} + +/*! + Re-sorts the displayed directory. + + \sa rereadDir() +*/ + +void Q3FileDialog::resortDir() +{ + d->mimeTypeTimer->stop(); + d->pendingItems.clear(); + + Q3FileDialogPrivate::File *item = 0; + Q3FileDialogPrivate::MCItem *item2 = 0; + + d->sortedList.sort(); + + if (files->childCount() > 0 || d->moreFiles->count() > 0) { + d->moreFiles->clear(); + files->clear(); + d->last = 0; + files->setSorting(-1); + } + + QUrlInfo *i = sortAscending ? d->sortedList.first() : d->sortedList.last(); + for (; i; i = sortAscending ? d->sortedList.next() : d->sortedList.prev()) { + item = new Q3FileDialogPrivate::File(d, i, files); + item2 = new Q3FileDialogPrivate::MCItem(d->moreFiles, item, item2); + item->i = item2; + d->pendingItems.append(item); + if ((d->mode == ExistingFiles && item->info.isDir()) || + (isDirectoryMode(d->mode) && item->info.isFile())) { + item->setSelectable(false); + item2->setSelectable(false); + } + } + + // ##### As the Q3FileIconProvider only support QFileInfo and no + // QUrlInfo it can be only used for local files at the moment. In + // 3.0 we have to change the API of Q3FileIconProvider to work on + // QUrlInfo so that also remote filesystems can be show mime-type + // specific icons. + if (d->url.isLocalFile()) + d->mimeTypeTimer->start(0); +} + +/*! + Stops the current copy operation. +*/ + +void Q3FileDialog::stopCopy() +{ + if (d->ignoreStop) + return; + + d->url.blockSignals(true); + d->url.stop(); + if (d->progressDia) { + d->ignoreStop = true; + QTimer::singleShot(100, this, SLOT(removeProgressDia())); + } + d->url.blockSignals(false); +} + +/*! + \internal +*/ + +void Q3FileDialog::removeProgressDia() +{ + if (d->progressDia) + delete d->progressDia; + d->progressDia = 0; +} + +/*! + \internal +*/ + +void Q3FileDialog::doMimeTypeLookup() +{ + if (!iconProvider()) { + d->pendingItems.clear(); + d->mimeTypeTimer->stop(); + return; + } + + d->mimeTypeTimer->stop(); + if (d->pendingItems.count() == 0) { + return; + } + + QRect r; + Q3FileDialogPrivate::File *item = d->pendingItems.first(); + if (item) { + QFileInfo fi; + if (d->url.isLocalFile()) { + fi.setFile(Q3Url(d->url.path(), Q3FileDialogPrivate::encodeFileName(item->info.name())).path(false)); + } else + fi.setFile(item->info.name()); // ##### + const QPixmap *p = iconProvider()->pixmap(fi); + if (p && p != item->pixmap(0) && + (!item->pixmap(0) || p->serialNumber() != item->pixmap(0)->serialNumber()) && + p != fifteenTransparentPixels) { + item->hasMimePixmap = true; + + // evil hack to avoid much too much repaints! + QPointer that(this); // this may be deleted by an event handler + qApp->processEvents(); + if (that.isNull()) + return; + files->setUpdatesEnabled(false); + files->viewport()->setUpdatesEnabled(false); + if (item != d->pendingItems.first()) + return; + item->setPixmap(0, *p); + qApp->processEvents(); + if (that.isNull()) + return; + files->setUpdatesEnabled(true); + files->viewport()->setUpdatesEnabled(true); + + if (files->isVisible()) { + QRect ir(files->itemRect(item)); + if (ir != QRect(0, 0, -1, -1)) { + r = r.united(ir); + } + } else { + QRect ir(d->moreFiles->itemRect(item->i)); + if (ir != QRect(0, 0, -1, -1)) { + r = r.united(ir); + } + } + } + if (d->pendingItems.count()) + d->pendingItems.removeFirst(); + } + + if (d->moreFiles->isVisible()) { + d->moreFiles->viewport()->repaint(r); + } else { + files->viewport()->repaint(r); + } + + if (d->pendingItems.count()) + d->mimeTypeTimer->start(0); + else if (d->moreFiles->isVisible()) + d->moreFiles->triggerUpdate(true); +} + +/*! + If \a b is true then all the files in the current directory are selected; + otherwise, they are deselected. +*/ + +void Q3FileDialog::selectAll(bool b) +{ + if (d->mode != ExistingFiles) + return; + d->moreFiles->selectAll(b); + files->selectAll(b); +} + +void Q3FileDialog::goBack() +{ + if (!d->goBack || !d->goBack->isEnabled() || d->history.isEmpty()) + return; + d->history.removeLast(); + if (d->history.size() < 2) + d->goBack->setEnabled(false); + setUrl(d->history.last()); +} + +// a class with wonderfully inflexible flexibility. why doesn't it +// just subclass QWidget in the first place? 'you have to derive your +// preview widget from QWidget and from this class' indeed. + +/*! + \class Q3FilePreview + \brief The Q3FilePreview class provides file previewing in Q3FileDialog. + + \compat + + This class is an abstract base class which is used to implement + widgets that can display a preview of a file in a Q3FileDialog. + + You must derive the preview widget from both QWidget and from this + class. Then you must reimplement this class's previewUrl() function, + which is called by the file dialog if the preview of a file + (specified as a URL) should be shown. + + See also Q3FileDialog::setPreviewMode(), Q3FileDialog::setContentsPreview(), + Q3FileDialog::setInfoPreview(), Q3FileDialog::setInfoPreviewEnabled(), + Q3FileDialog::setContentsPreviewEnabled(). +*/ + +/*! + Constructs the Q3FilePreview. +*/ + +Q3FilePreview::Q3FilePreview() +{ +} + +/*! + \fn Q3FilePreview::~Q3FilePreview() + + Destroys the file preview object. +*/ + +/*! + \fn void Q3FilePreview::previewUrl(const Q3Url &url) + + This function is called by Q3FileDialog if a preview + for the \a url should be shown. Reimplement this + function to provide file previewing. +*/ + + +QT_END_NAMESPACE + +#include "moc_q3filedialog.cpp" +#include "q3filedialog.moc" + +#endif diff --git a/src/qt3support/dialogs/q3filedialog.h b/src/qt3support/dialogs/q3filedialog.h new file mode 100644 index 0000000000..c0a64cc61c --- /dev/null +++ b/src/qt3support/dialogs/q3filedialog.h @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 Q3FILEDIALOG_H +#define Q3FILEDIALOG_H + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class QAbstractButton; +class QPushButton; +class QLabel; +class QWidget; +class QFileDialog; +class QTimer; +class Q3NetworkOperation; +class QLineEdit; +class Q3ListViewItem; +class Q3ListBoxItem; +class Q3FileDialogPrivate; +class Q3FileDialogQFileListView; +class QUrlInfo; + +#ifndef QT_NO_FILEDIALOG + +class Q_COMPAT_EXPORT Q3FileIconProvider : public QObject +{ + Q_OBJECT +public: + Q3FileIconProvider(QObject * parent = 0, const char* name = 0); + virtual const QPixmap * pixmap(const QFileInfo &); + +private: + Q_DISABLE_COPY(Q3FileIconProvider) +}; + +class Q_COMPAT_EXPORT Q3FilePreview +{ +public: + Q3FilePreview(); + virtual ~Q3FilePreview() {} + virtual void previewUrl(const Q3Url &url) = 0; + +}; + +class Q_COMPAT_EXPORT Q3FileDialog : public QDialog +{ + Q_OBJECT + Q_ENUMS(Mode ViewMode PreviewMode) + // ##### Why are this read-only properties ? + Q_PROPERTY(QString selectedFile READ selectedFile) + Q_PROPERTY(QString selectedFilter READ selectedFilter) + Q_PROPERTY(QStringList selectedFiles READ selectedFiles) + // #### Should not we be able to set the path ? + Q_PROPERTY(QString dirPath READ dirPath) + Q_PROPERTY(bool showHiddenFiles READ showHiddenFiles WRITE setShowHiddenFiles) + Q_PROPERTY(Mode mode READ mode WRITE setMode) + Q_PROPERTY(ViewMode viewMode READ viewMode WRITE setViewMode) + Q_PROPERTY(PreviewMode previewMode READ previewMode WRITE setPreviewMode) + Q_PROPERTY(bool infoPreview READ isInfoPreviewEnabled WRITE setInfoPreviewEnabled) + Q_PROPERTY(bool contentsPreview READ isContentsPreviewEnabled WRITE setContentsPreviewEnabled) + +public: + Q3FileDialog(const QString& dirName, const QString& filter = QString(), + QWidget* parent=0, const char* name=0, bool modal = false); + Q3FileDialog(QWidget* parent=0, const char* name=0, bool modal = false); + ~Q3FileDialog(); + + // recommended static functions + + static QString getOpenFileName(const QString &initially = QString(), + const QString &filter = QString(), + QWidget *parent = 0, const char* name = 0, + const QString &caption = QString(), + QString *selectedFilter = 0, + bool resolveSymlinks = true); + static QString getSaveFileName(const QString &initially = QString(), + const QString &filter = QString(), + QWidget *parent = 0, const char* name = 0, + const QString &caption = QString(), + QString *selectedFilter = 0, + bool resolveSymlinks = true); + static QString getExistingDirectory(const QString &dir = QString(), + QWidget *parent = 0, + const char* name = 0, + const QString &caption = QString(), + bool dirOnly = true, + bool resolveSymlinks = true); + static QStringList getOpenFileNames(const QString &filter= QString(), + const QString &dir = QString(), + QWidget *parent = 0, + const char* name = 0, + const QString &caption = QString(), + QString *selectedFilter = 0, + bool resolveSymlinks = true); + + // other static functions + + static void setIconProvider(Q3FileIconProvider *); + static Q3FileIconProvider * iconProvider(); + + // non-static function for special needs + + QString selectedFile() const; + QString selectedFilter() const; + virtual void setSelectedFilter(const QString&); + virtual void setSelectedFilter(int); + + void setSelection(const QString &); + + void selectAll(bool b); + + QStringList selectedFiles() const; + + QString dirPath() const; + + void setDir(const QDir &); + const QDir *dir() const; + + void setShowHiddenFiles(bool s); + bool showHiddenFiles() const; + + void rereadDir(); + void resortDir(); + + enum Mode { AnyFile, ExistingFile, Directory, ExistingFiles, DirectoryOnly }; + void setMode(Mode); + Mode mode() const; + + enum ViewMode { Detail, List }; + enum PreviewMode { NoPreview, Contents, Info }; + void setViewMode(ViewMode m); + ViewMode viewMode() const; + void setPreviewMode(PreviewMode m); + PreviewMode previewMode() const; + + bool eventFilter(QObject *, QEvent *); + + bool isInfoPreviewEnabled() const; + bool isContentsPreviewEnabled() const; + void setInfoPreviewEnabled(bool); + void setContentsPreviewEnabled(bool); + + void setInfoPreview(QWidget *w, Q3FilePreview *preview); + void setContentsPreview(QWidget *w, Q3FilePreview *preview); + + Q3Url url() const; + + void addFilter(const QString &filter); + +public Q_SLOTS: + void done(int); + void setDir(const QString&); + void setUrl(const Q3UrlOperator &url); + void setFilter(const QString&); + void setFilters(const QString&); + void setFilters(const char **); + void setFilters(const QStringList&); + +protected: + void resizeEvent(QResizeEvent *); + void keyPressEvent(QKeyEvent *); + + void addWidgets(QLabel *, QWidget *, QPushButton *); + void addToolButton(QAbstractButton *b, bool separator = false); + void addLeftWidget(QWidget *w); + void addRightWidget(QWidget *w); + +Q_SIGNALS: + void fileHighlighted(const QString&); + void fileSelected(const QString&); + void filesSelected(const QStringList&); + void dirEntered(const QString&); + void filterSelected(const QString&); + +private Q_SLOTS: + void detailViewSelectionChanged(); + void listBoxSelectionChanged(); + void changeMode(int); + void fileNameEditReturnPressed(); + void stopCopy(); + void removeProgressDia(); + + void fileSelected(int); + void fileHighlighted(int); + void dirSelected(int); + void pathSelected(int); + + void updateFileNameEdit(Q3ListViewItem *); + void selectDirectoryOrFile(Q3ListViewItem *); + void popupContextMenu(Q3ListViewItem *, const QPoint &, int); + void popupContextMenu(Q3ListBoxItem *, const QPoint &); + void updateFileNameEdit(Q3ListBoxItem *); + void selectDirectoryOrFile(Q3ListBoxItem *); + void fileNameEditDone(); + + void okClicked(); + void filterClicked(); // not used + void cancelClicked(); + + void cdUpClicked(); + void newFolderClicked(); + + void fixupNameEdit(); + + void doMimeTypeLookup(); + + void updateGeometries(); + void modeButtonsDestroyed(); + void urlStart(Q3NetworkOperation *op); + void urlFinished(Q3NetworkOperation *op); + void dataTransferProgress(int bytesDone, int bytesTotal, Q3NetworkOperation *); + void insertEntry(const Q3ValueList &fi, Q3NetworkOperation *op); + void removeEntry(Q3NetworkOperation *); + void createdDirectory(const QUrlInfo &info, Q3NetworkOperation *); + void itemChanged(Q3NetworkOperation *); + void goBack(); + +private: + Q_DISABLE_COPY(Q3FileDialog) + + enum PopupAction { + PA_Open = 0, + PA_Delete, + PA_Rename, + PA_SortName, + PA_SortSize, + PA_SortType, + PA_SortDate, + PA_SortUnsorted, + PA_Cancel, + PA_Reload, + PA_Hidden + }; + + void init(); + bool trySetSelection(bool isDir, const Q3UrlOperator &, bool); + void deleteFile(const QString &filename); + void popupContextMenu(const QString &filename, bool withSort, + PopupAction &action, const QPoint &p); + void updatePreviews(const Q3Url &u); + + QString fileName; + + friend class Q3FileDialogQFileListView; + friend class QFileListBox; + + Q3FileDialogPrivate *d; + Q3FileDialogQFileListView *files; + + QLineEdit *nameEdit; // also filter + QPushButton *okB; + QPushButton *cancelB; + +#if defined(Q_WS_WIN) + static QString winGetOpenFileName(const QString &initialSelection, + const QString &filter, + QString* workingDirectory, + QWidget *parent = 0, + const char* name = 0, + const QString& caption = QString(), + QString* selectedFilter = 0); + static QString winGetSaveFileName(const QString &initialSelection, + const QString &filter, + QString* workingDirectory, + QWidget *parent = 0, + const char* name = 0, + const QString& caption = QString(), + QString* selectedFilter = 0); + static QStringList winGetOpenFileNames(const QString &filter, + QString* workingDirectory, + QWidget *parent = 0, + const char* name = 0, + const QString& caption = QString(), + QString* selectedFilter = 0); + static QString winGetExistingDirectory(const QString &initialDirectory, + QWidget* parent = 0, + const char* name = 0, + const QString& caption = QString()); + static QString resolveLinkFile(const QString& linkfile); + int old_qt_ntfs_permission_lookup; +#endif +#if defined(Q_WS_MAC) + static QString macGetSaveFileName(const QString &, const QString &, + QString *, QWidget *, const char*, + const QString&, QString *); + static QStringList macGetOpenFileNames(const QString &, QString*, + QWidget *, const char *, + const QString&, QString *, + bool = true, bool = false); +#endif +}; + +#endif // QT_NO_FILEDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3FILEDIALOG_H diff --git a/src/qt3support/dialogs/q3filedialog_mac.cpp b/src/qt3support/dialogs/q3filedialog_mac.cpp new file mode 100644 index 0000000000..f1c749ecb8 --- /dev/null +++ b/src/qt3support/dialogs/q3filedialog_mac.cpp @@ -0,0 +1,569 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 "q3filedialog.h" + +#ifndef QT_NO_FILEDIALOG + +/***************************************************************************** + Q3FileDialog debug facilities + *****************************************************************************/ +//#define DEBUG_FILEDIALOG_FILTERS + +#include "qapplication.h" +#include +#include +#include "qregexp.h" +#include "qbuffer.h" +#include "qstringlist.h" +#include "qtextcodec.h" +#include "qdesktopwidget.h" +#include + +QT_BEGIN_NAMESPACE + +/***************************************************************************** + Externals + *****************************************************************************/ +extern WindowPtr qt_mac_window_for(const QWidget*); //qwidget_mac.cpp +extern const char qt3_file_dialog_filter_reg_exp[]; //qfiledialog.cpp + +/***************************************************************************** + Q3FileDialog utility functions + *****************************************************************************/ +static UInt8 *str_buffer = NULL; +static void cleanup_str_buffer() +{ + if(str_buffer) { + free(str_buffer); + str_buffer = NULL; + } +} + +// Returns the wildcard part of a filter. +struct qt_mac_filter_name { + QString description, regxp, filter; +}; +static qt_mac_filter_name *extractFilter(const QString& rawFilter) +{ + qt_mac_filter_name *ret = new qt_mac_filter_name; + ret->filter = rawFilter; + QString result = rawFilter; + QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp)); + int index = r.indexIn(result); + if(index >= 0) { + ret->description = r.cap(1).trimmed(); + result = r.cap(2); + } + if(ret->description.isEmpty()) + ret->description = result; + ret->regxp = result.replace(QLatin1Char(' '), QLatin1Char(';')); + return ret; +} + +// Makes a list of filters from ;;-separated text. +static QList makeFiltersList(const QString &filter) +{ +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("Q3FileDialog:%d - Got filter (%s)", __LINE__, filter.latin1()); +#endif + QString f(filter); + if(f.isEmpty()) + f = Q3FileDialog::tr("All Files (*)"); + if(f.isEmpty()) + return QList(); + QString sep(QLatin1String(";;")); + int i = f.indexOf(sep, 0); + if(i == -1) { + sep = QLatin1String("\n"); + if(f.indexOf(sep, 0) != -1) + i = f.indexOf(sep, 0); + } + + QList ret; + QStringList filts = f.split(sep); + for (QStringList::Iterator it = filts.begin(); it != filts.end(); ++it) { + qt_mac_filter_name *filter = extractFilter((*it)); +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("Q3FileDialog:%d Split out filter (%d) '%s' '%s'", __LINE__, ret.count(), + filter->regxp.latin1(), filter->description.latin1()); +#endif + ret.append(filter); + } + return ret; +} + +struct qt_mac_nav_filter_type { + int index; + QList *filts; +}; + +static Boolean qt_mac_nav_filter(AEDesc *theItem, void *info, + void *myd, NavFilterModes) +{ + qt_mac_nav_filter_type *t = (qt_mac_nav_filter_type *)myd; + if(!t || !t->filts || t->index >= t->filts->count()) + return true; + + NavFileOrFolderInfo *theInfo = (NavFileOrFolderInfo *)info; + QString file; + qt_mac_filter_name *fn = t->filts->at(t->index); + if(!fn) + return true; + if(theItem->descriptorType == typeFSRef) { + FSRef ref; + AEGetDescData(theItem, &ref, sizeof(ref)); + if(!str_buffer) { + qAddPostRoutine(cleanup_str_buffer); + str_buffer = (UInt8 *)malloc(1024); + } + FSRefMakePath(&ref, str_buffer, 1024); + file = QString::fromUtf8((const char *)str_buffer); + int slsh = file.lastIndexOf(QLatin1Char('/')); + if(slsh != -1) + file = file.right(file.length() - slsh - 1); + } + QStringList reg = fn->regxp.split(QLatin1String(";")); + for(QStringList::Iterator it = reg.begin(); it != reg.end(); ++it) { + QRegExp rg((*it), false, true); +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("Q3FileDialog:%d, asked to filter.. %s (%s)", __LINE__, + file.latin1(), (*it).latin1()); +#endif + if(rg.exactMatch(file)) + return true; + } + return (theInfo->isFolder && !file.endsWith(QLatin1String(".app"))); +} + +//filter UPP stuff +static NavObjectFilterUPP mac_navFilterUPP = NULL; +static void cleanup_navFilterUPP() +{ + DisposeNavObjectFilterUPP(mac_navFilterUPP); + mac_navFilterUPP = NULL; +} +static const NavObjectFilterUPP make_navFilterUPP() +{ + if(mac_navFilterUPP) + return mac_navFilterUPP; + qAddPostRoutine(cleanup_navFilterUPP); + return mac_navFilterUPP = NewNavObjectFilterUPP(qt_mac_nav_filter); +} +//event UPP stuff +static NavEventUPP mac_navProcUPP = NULL; +static void cleanup_navProcUPP() +{ + DisposeNavEventUPP(mac_navProcUPP); + mac_navProcUPP = NULL; +} +static bool g_nav_blocking=true; +static void qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg, + NavCBRecPtr p, NavCallBackUserData myd) +{ + switch(msg) { + case kNavCBPopupMenuSelect: { + qt_mac_nav_filter_type *t = (qt_mac_nav_filter_type *)myd; + NavMenuItemSpec *s = (NavMenuItemSpec*)p->eventData.eventDataParms.param; + t->index = s->menuType; +#ifdef DEBUG_FILEDIALOG_FILTERS + qDebug("Q3FileDialog:%d - Selected a filter: %ld", __LINE__, s->menuType); +#endif + break; } + case kNavCBStart: + g_nav_blocking=true; + break; + case kNavCBUserAction: + g_nav_blocking=false; + break; + } +} +static const NavEventUPP make_navProcUPP() +{ + if(mac_navProcUPP) + return mac_navProcUPP; + qAddPostRoutine(cleanup_navProcUPP); + return mac_navProcUPP = NewNavEventUPP(qt_mac_filedialog_event_proc); +} + + +extern OSErr qt_mac_create_fsref(const QString &, FSRef *); //qglobal.cpp + +QStringList Q3FileDialog::macGetOpenFileNames(const QString &filter, QString *pwd, + QWidget *parent, const char* /*name*/, + const QString& caption, QString *selectedFilter, + bool multi, bool directory) +{ + OSErr err; + QStringList retstrl; + + NavDialogCreationOptions options; + NavGetDefaultDialogCreationOptions(&options); + options.modality = kWindowModalityAppModal; + options.optionFlags |= kNavDontConfirmReplacement | kNavSupportPackages; + if (!multi) + options.optionFlags &= ~kNavAllowMultipleFiles; + if(!caption.isEmpty()) + options.windowTitle = CFStringCreateWithCharacters(NULL, (UniChar *)caption.unicode(), + caption.length()); + + static const int w = 450, h = 350; + options.location.h = options.location.v = -1; + if(parent && parent->isVisible()) { + Qt::WindowType wt = parent->window()->windowType(); + if (wt != Qt::Desktop && wt != Qt::Sheet && wt != Qt::Drawer) { + options.modality = kWindowModalityWindowModal; + options.parentWindow = qt_mac_window_for(parent); + } else { + parent = parent->window(); + QString s = parent->windowTitle(); + options.clientName = CFStringCreateWithCharacters(NULL, (UniChar *)s.unicode(), s.length()); + options.location.h = (parent->x() + (parent->width() / 2)) - (w / 2); + options.location.v = (parent->y() + (parent->height() / 2)) - (h / 2); + + QRect r = QApplication::desktop()->screenGeometry( + QApplication::desktop()->screenNumber(parent)); + if(options.location.h + w > r.right()) + options.location.h -= (options.location.h + w) - r.right() + 10; + if(options.location.v + h > r.bottom()) + options.location.v -= (options.location.v + h) - r.bottom() + 10; + } + } else if(QWidget *p = qApp->mainWidget()) { + static int last_screen = -1; + int scr = QApplication::desktop()->screenNumber(p); + if(last_screen != scr) { + QRect r = QApplication::desktop()->screenGeometry(scr); + options.location.h = (r.x() + (r.width() / 2)) - (w / 2); + options.location.v = (r.y() + (r.height() / 2)) - (h / 2); + } + } + + QList filts = makeFiltersList(filter); + qt_mac_nav_filter_type t; + t.index = 0; + t.filts = &filts; + if(filts.count() > 1) { + int i = 0; + CFStringRef *arr = (CFStringRef *)malloc(sizeof(CFStringRef) * filts.count()); + for (QList::Iterator it = filts.begin(); it != filts.end(); ++it) { + QString rg = (*it)->description; + arr[i++] = CFStringCreateWithCharacters(NULL, (UniChar *)rg.unicode(), rg.length()); + } + options.popupExtension = CFArrayCreate(NULL, (const void **)arr, filts.count(), NULL); + } + + NavDialogRef dlg; + if(directory) { + if(NavCreateChooseFolderDialog(&options, make_navProcUPP(), NULL, NULL, &dlg)) { + qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); + return retstrl; + } + } else { + if(NavCreateGetFileDialog(&options, NULL, make_navProcUPP(), NULL, + make_navFilterUPP(), (void *) (filts.isEmpty() ? NULL : &t), + &dlg)) { + qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); + return retstrl; + } + } + if(pwd && !pwd->isEmpty()) { + FSRef fsref; + if(qt_mac_create_fsref(*pwd, &fsref) == noErr) { + AEDesc desc; + if(AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc) == noErr) + NavCustomControl(dlg, kNavCtlSetLocation, (void*)&desc); + } + } + + NavDialogRun(dlg); + if (selectedFilter) { + NavMenuItemSpec navSpec; + bzero(&navSpec, sizeof(NavMenuItemSpec)); + qt_mac_filter_name *sel_filt_name = makeFiltersList(*selectedFilter).at(0); + for (int i = 0; i < filts.count(); ++i) { + const qt_mac_filter_name *filter = filts.at(i); + if (sel_filt_name->description == filter->description + && sel_filt_name->regxp == filter->regxp + && sel_filt_name->filter == filter->filter) { + navSpec.menuType = i; + break; + } + } + NavCustomControl(dlg, kNavCtlSelectCustomType, &navSpec); + } + if(options.modality == kWindowModalityWindowModal) { //simulate modality + QWidget modal_widg(parent, __FILE__ "__modal_dlg", + Qt::WType_TopLevel | Qt::WStyle_Customize | Qt::WStyle_DialogBorder); + modal_widg.createWinId(); + QApplicationPrivate::enterModal(&modal_widg); + while(g_nav_blocking) + qApp->processEvents(QEventLoop::WaitForMoreEvents); + QApplicationPrivate::leaveModal(&modal_widg); + } + + if(!(NavDialogGetUserAction(dlg) & + (kNavUserActionOpen | kNavUserActionChoose | kNavUserActionNewFolder))) { + NavDialogDispose(dlg); + return retstrl; + } + NavReplyRecord ret; + NavDialogGetReply(dlg, &ret); + NavDialogDispose(dlg); + + long count; + err = AECountItems(&(ret.selection), &count); + if(!ret.validRecord || err != noErr || !count) { + NavDisposeReply(&ret); + return retstrl; + } + + for(long index = 1; index <= count; index++) { + FSRef ref; + err = AEGetNthPtr(&(ret.selection), index, typeFSRef, 0, 0, &ref, sizeof(ref), 0); + if(err != noErr) + break; + + if(!str_buffer) { + qAddPostRoutine(cleanup_str_buffer); + str_buffer = (UInt8 *)malloc(1024); + } + FSRefMakePath(&ref, str_buffer, 1024); + retstrl.append(QString::fromUtf8((const char *)str_buffer)); + } + NavDisposeReply(&ret); + if(selectedFilter) + *selectedFilter = filts.at(t.index)->filter; + while (!filts.isEmpty()) + delete filts.takeFirst(); + return retstrl; +} + +// Copious copy and paste from qfiledialog.cpp. Fix in 4.0. +static QString encodeFileName(const QString &fName) +{ + QString newStr; + QByteArray cName = fName.utf8(); + const QByteArray sChars("<>#@\"&%$:,;?={}|^~[]\'`\\*"); + + int len = cName.length(); + if (!len) + return QString(); + for (int i = 0; i < len ;++i) { + uchar inCh = (uchar)cName[i]; + if (inCh >= 128 || sChars.contains(inCh)) + { + newStr += QLatin1Char('%'); + ushort c = inCh / 16; + c += c > 9 ? 'A' - 10 : '0'; + newStr += QLatin1Char((char)c); + c = inCh % 16; + c += c > 9 ? 'A' - 10 : '0'; + newStr += QLatin1Char((char)c); + } else { + newStr += QLatin1Char((char)inCh); + } + } + return newStr; +} + +QString Q3FileDialog::macGetSaveFileName(const QString &start, const QString &filter, + QString *, QWidget *parent, const char* /*name*/, + const QString& caption, QString *selectedFilter) +{ + OSErr err; + QString retstr; + NavDialogCreationOptions options; + NavGetDefaultDialogCreationOptions(&options); + static const int w = 450, h = 350; + options.optionFlags |= kNavDontConfirmReplacement; + options.modality = kWindowModalityAppModal; + options.location.h = options.location.v = -1; + QString workingDir; + QString initialSelection; + if (!start.isEmpty()) { + Q3UrlOperator u(encodeFileName(start)); + if (u.isLocalFile() && QFileInfo(u.path()).isDir()) { + workingDir = start; + } else { + if (u.isLocalFile()) { + QFileInfo fi(u.dirPath()); + if (fi.exists()) { + workingDir = u.dirPath(); + initialSelection = u.fileName(); + } + } else { + workingDir = u.toString(); + } + } + if (!initialSelection.isEmpty()) + options.saveFileName = CFStringCreateWithCharacters(0, + (UniChar *)initialSelection.unicode(), + initialSelection.length()); + } + if(!caption.isEmpty()) + options.windowTitle = CFStringCreateWithCharacters(NULL, (UniChar *)caption.unicode(), + caption.length()); + if(parent && parent->isVisible()) { + Qt::WindowType wt = parent->window()->windowType(); + if (wt != Qt::Desktop && wt != Qt::Sheet && wt != Qt::Drawer) { + options.modality = kWindowModalityWindowModal; + options.parentWindow = qt_mac_window_for(parent); + } else { + parent = parent->window(); + QString s = parent->windowTitle(); + options.clientName = CFStringCreateWithCharacters(NULL, (UniChar *)s.unicode(), s.length()); + options.location.h = (parent->x() + (parent->width() / 2)) - (w / 2); + options.location.v = (parent->y() + (parent->height() / 2)) - (h / 2); + + QRect r = QApplication::desktop()->screenGeometry( + QApplication::desktop()->screenNumber(parent)); + if(options.location.h + w > r.right()) + options.location.h -= (options.location.h + w) - r.right() + 10; + if(options.location.v + h > r.bottom()) + options.location.v -= (options.location.v + h) - r.bottom() + 10; + } + } else if(QWidget *p = qApp->mainWidget()) { + static int last_screen = -1; + int scr = QApplication::desktop()->screenNumber(p); + if(last_screen != scr) { + QRect r = QApplication::desktop()->screenGeometry(scr); + options.location.h = (r.x() + (r.width() / 2)) - (w / 2); + options.location.v = (r.y() + (r.height() / 2)) - (h / 2); + } + } + + QList filts = makeFiltersList(filter); + qt_mac_nav_filter_type t; + t.index = 0; + t.filts = &filts; + if(filts.count() > 1) { + int i = 0; + CFStringRef *arr = (CFStringRef *)malloc(sizeof(CFStringRef) * filts.count()); + for (QList::Iterator it = filts.begin(); it != filts.end(); ++it) { + QString rg = (*it)->description; + arr[i++] = CFStringCreateWithCharacters(NULL, (UniChar *)rg.unicode(), rg.length()); + } + options.popupExtension = CFArrayCreate(NULL, (const void **)arr, filts.count(), NULL); + } + + NavDialogRef dlg; + if(NavCreatePutFileDialog(&options, 'cute', kNavGenericSignature, make_navProcUPP(), + (void *) (filts.isEmpty() ? NULL : &t), &dlg)) { + qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); + return retstr; + } + if (!workingDir.isEmpty()) { + FSRef fsref; + if (qt_mac_create_fsref(workingDir, &fsref) == noErr) { + AEDesc desc; + if (AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc) == noErr) + NavCustomControl(dlg, kNavCtlSetLocation, (void*)&desc); + } + } + NavDialogRun(dlg); + if (selectedFilter) { + NavMenuItemSpec navSpec; + bzero(&navSpec, sizeof(NavMenuItemSpec)); + qt_mac_filter_name *sel_filt_name = makeFiltersList(*selectedFilter).at(0); + for (int i = 0; i < filts.count(); ++i) { + const qt_mac_filter_name *filter = filts.at(i); + if (sel_filt_name->description == filter->description + && sel_filt_name->regxp == filter->regxp + && sel_filt_name->filter == filter->filter) { + navSpec.menuType = i; + break; + } + } + NavCustomControl(dlg, kNavCtlSelectCustomType, &navSpec); + } + if(options.modality == kWindowModalityWindowModal) { //simulate modality + QWidget modal_widg(parent, __FILE__ "__modal_dlg", + Qt::WType_TopLevel | Qt::WStyle_Customize | Qt::WStyle_DialogBorder); + modal_widg.createWinId(); + QApplicationPrivate::enterModal(&modal_widg); + while(g_nav_blocking) + qApp->processEvents(QEventLoop::WaitForMoreEvents); + QApplicationPrivate::leaveModal(&modal_widg); + } + + if(NavDialogGetUserAction(dlg) != kNavUserActionSaveAs) { + NavDialogDispose(dlg); + return retstr; + } + NavReplyRecord ret; + NavDialogGetReply(dlg, &ret); + NavDialogDispose(dlg); + + long count; + err = AECountItems(&(ret.selection), &count); + if(!ret.validRecord || err != noErr || !count) { + NavDisposeReply(&ret); + return retstr; + } + + AEKeyword keyword; + DescType type; + Size size; + FSRef ref; + err = AEGetNthPtr(&(ret.selection), 1, typeFSRef, &keyword, + &type, &ref, sizeof(ref), &size); + if(err == noErr) { + if(!str_buffer) { + qAddPostRoutine(cleanup_str_buffer); + str_buffer = (UInt8 *)malloc(1024); + } + FSRefMakePath(&ref, str_buffer, 1024); + retstr = QString::fromUtf8((const char *)str_buffer); + //now filename + CFStringGetCString(ret.saveFileName, (char *)str_buffer, 1024, kCFStringEncodingUTF8); + retstr += QLatin1String("/") + QString::fromUtf8((const char *)str_buffer); + } + NavDisposeReply(&ret); + if(selectedFilter) + *selectedFilter = filts.at(t.index)->filter; + while (!filts.isEmpty()) + delete filts.takeFirst(); + return retstr; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/dialogs/q3filedialog_win.cpp b/src/qt3support/dialogs/q3filedialog_win.cpp new file mode 100644 index 0000000000..1be797a2d2 --- /dev/null +++ b/src/qt3support/dialogs/q3filedialog_win.cpp @@ -0,0 +1,749 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 "q3filedialog.h" + +#ifndef QT_NO_FILEDIALOG + +#include "qapplication.h" +#include "private/qapplication_p.h" +#include "qt_windows.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 + +#ifdef Q_OS_WINCE +#include +#endif + +QT_BEGIN_NAMESPACE + +// 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 getEsistingDirectory(), etc. +typedef LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(BROWSEINFO*); +static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0; +typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(LPITEMIDLIST,LPWSTR); +static PtrSHGetPathFromIDList ptrSHGetPathFromIDList = 0; + +static void resolveLibs() +{ +#ifndef Q_OS_WINCE + 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 (qt_winUnicode()) { + QLibrary lib(QLatin1String("shell32")); + ptrSHBrowseForFolder = (PtrSHBrowseForFolder) lib.resolve("SHBrowseForFolderW"); + ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList) lib.resolve("SHGetPathFromIDListW"); + } + } +#endif +} +#ifdef Q_OS_WINCE +#define PtrSHBrowseForFolder SHBrowseForFolder ; +#define PtrSHGetPathFromIDList SHGetPathFromIDList; +#endif + + +extern const char qt3_file_dialog_filter_reg_exp[]; // defined in qfiledialog.cpp + +const int maxNameLen = 1023; +const int maxMultiLen = 65535; + +// Returns the wildcard part of a filter. +static QString extractFilter(const QString& rawFilter) +{ + QString result = rawFilter; + QRegExp r(QString::fromLatin1(qt3_file_dialog_filter_reg_exp)); + int index = r.indexIn(result); + if (index >= 0) + result = r.cap(2); + return result.replace(QLatin1Char(' '), QLatin1Char(';')); +} + +// Makes a list of filters from ;;-separated text. +static QStringList makeFiltersList(const QString &filter) +{ + QString f(filter); + + if (f.isEmpty()) + f = Q3FileDialog::tr("All Files (*.*)"); + + if (f.isEmpty()) + return QStringList(); + + int i = f.find(QLatin1String(";;"), 0); + QString sep(QLatin1String(";;")); + if (i == -1) { + if (f.find(QLatin1String("\n"), 0) != -1) { + sep = QLatin1String("\n"); + i = f.find(sep, 0); + } + } + + return QStringList::split(sep, f ); +} + +// Makes a NUL-oriented Windows filter from a Qt filter. +static QString winFilter(const QString& filter) +{ + QStringList filterLst = makeFiltersList(filter); + QStringList::Iterator it = filterLst.begin(); + QString winfilters; + for (; it != filterLst.end(); ++it) { + winfilters += *it; + winfilters += QChar::null; + winfilters += extractFilter(*it); + winfilters += QChar::null; + } + winfilters += QChar::null; + return winfilters; +} + +static QString selFilter(const QString& filter, DWORD idx) +{ + QStringList filterLst = makeFiltersList(filter); + return filterLst[(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 makeOFN (below) too +static +OPENFILENAMEA* makeOFNA(QWidget* parent, + const QString& initialSelection, + const QString& initialDirectory, + const QString& title, + const QString& filters, + Q3FileDialog::Mode mode) +{ + if (parent) + parent = parent->window(); + else + parent = qApp->activeWindow(); + + aTitle = title.local8Bit(); + aInitDir = QDir::toNativeSeparators(initialDirectory).local8Bit(); + if (initialSelection.isEmpty()) + aInitSel = ""; + else + aInitSel = QDir::toNativeSeparators(initialSelection).local8Bit(); + int maxLen = mode == Q3FileDialog::ExistingFiles ? maxMultiLen : maxNameLen; + aInitSel.resize(maxLen + 1); // make room for return value + aFilter = filters.local8Bit(); + + 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 + 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); + + if (mode == Q3FileDialog::ExistingFile || + mode == Q3FileDialog::ExistingFiles) + ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); + if (mode == Q3FileDialog::ExistingFiles) + ofn->Flags |= (OFN_ALLOWMULTISELECT | OFN_EXPLORER); + + return ofn; +} + +static void cleanUpOFNA(OPENFILENAMEA** ofn) +{ + delete *ofn; + *ofn = 0; +} +#endif + +static QString tFilters, tTitle, tInitDir; + +#ifdef UNICODE +// If you change this, then make sure you change makeOFNA (above) too +static +OPENFILENAME* makeOFN(QWidget* parent, + const QString& initialSelection, + const QString& initialDirectory, + const QString& title, + const QString& filters, + Q3FileDialog::Mode mode) +{ + if (parent) + parent = parent->window(); + else + parent = qApp->activeWindow(); + + tInitDir = QDir::toNativeSeparators(initialDirectory); + tFilters = filters; + tTitle = title; + QString initSel = QDir::toNativeSeparators(initialSelection); + + int maxLen = mode == Q3FileDialog::ExistingFiles ? maxMultiLen : maxNameLen; + TCHAR *tInitSel = new TCHAR[maxLen+1]; + if (initSel.length() > 0 && initSel.length() <= maxLen) + memcpy(tInitSel, initSel.ucs2(), (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 + ofn->hwndOwner = parent ? parent->winId() : 0; + ofn->lpstrFilter = (TCHAR *)tFilters.ucs2(); + ofn->lpstrFile = tInitSel; + ofn->nMaxFile = maxLen; + ofn->lpstrInitialDir = (TCHAR *)tInitDir.ucs2(); + ofn->lpstrTitle = (TCHAR *)tTitle.ucs2(); + ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY); + + if (mode == Q3FileDialog::ExistingFile || + mode == Q3FileDialog::ExistingFiles) + ofn->Flags |= (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); + if (mode == Q3FileDialog::ExistingFiles) + ofn->Flags |= (OFN_ALLOWMULTISELECT | OFN_EXPLORER); + + return ofn; +} + + +static void cleanUpOFN(OPENFILENAME** ofn) +{ + delete (*ofn)->lpstrFile; + delete *ofn; + *ofn = 0; +} + +#endif // UNICODE + +QString Q3FileDialog::winGetOpenFileName(const QString &initialSelection, + const QString &filter, + QString* initialDirectory, + QWidget *parent, const char* /*name*/, + const QString& caption, + QString* selectedFilter) +{ + QString result; + + QString isel = initialSelection; + + if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:")) + initialDirectory->remove(0, 5); + QFileInfo fi(*initialDirectory); + + if (initialDirectory && !fi.isDir()) { + *initialDirectory = fi.dirPath(true); + if (isel.isEmpty()) + isel = fi.fileName(); + } + + if (!fi.exists()) + *initialDirectory = QDir::homeDirPath(); + + QString title = caption; + if (title.isNull()) + title = tr("Open"); + + DWORD selFilIdx = 0; + + int idx = 0; + if (selectedFilter && !selectedFilter->isEmpty()) { + QStringList filterLst = makeFiltersList(filter); + idx = filterLst.findIndex(*selectedFilter); + } + + if (parent) { + QEvent e(QEvent::WindowBlocked); + QApplication::sendEvent(parent, &e); + QApplicationPrivate::enterModal(parent); + } + QT_WA({ + // Use Unicode strings and API + OPENFILENAME* ofn = makeOFN(parent, isel, + *initialDirectory, title, + winFilter(filter), ExistingFile); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetOpenFileName(ofn)) { + result = QString::fromUcs2((ushort*)ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + cleanUpOFN(&ofn); + } , { + // Use ANSI strings and API + OPENFILENAMEA* ofn = makeOFNA(parent, isel, + *initialDirectory, title, + winFilter(filter), ExistingFile); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetOpenFileNameA(ofn)) { + result = QString::fromLocal8Bit(ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + cleanUpOFNA(&ofn); + }); + if (parent) { + QApplicationPrivate::leaveModal(parent); + QEvent e(QEvent::WindowUnblocked); + QApplication::sendEvent(parent, &e); + } + + if (result.isEmpty()) { + return result; + } + else { + QFileInfo fi(result); + *initialDirectory = fi.dirPath(); + if (selectedFilter) + *selectedFilter = selFilter(filter, selFilIdx); + return fi.absFilePath(); + } +} + + +QString Q3FileDialog::winGetSaveFileName(const QString &initialSelection, + const QString &filter, + QString* initialDirectory, + QWidget *parent, const char* /*name*/, + const QString& caption, + QString* selectedFilter) +{ + QString result; + + QString isel = initialSelection; + if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:")) + initialDirectory->remove(0, 5); + QFileInfo fi(*initialDirectory); + + if (initialDirectory && !fi.isDir()) { + *initialDirectory = fi.dirPath(true); + if (isel.isEmpty()) + isel = fi.fileName(); + } + + if (!fi.exists()) + *initialDirectory = QDir::homeDirPath(); + + QString title = caption; + if (title.isNull()) + title = tr("Save As"); + + DWORD selFilIdx = 0; + + int idx = 0; + if (selectedFilter && !selectedFilter->isEmpty()) { + QStringList filterLst = makeFiltersList(filter); + idx = filterLst.findIndex(*selectedFilter); + } + + if (parent) { + QEvent e(QEvent::WindowBlocked); + QApplication::sendEvent(parent, &e); + QApplicationPrivate::enterModal(parent); + } + QT_WA({ + // Use Unicode strings and API + OPENFILENAME* ofn = makeOFN(parent, isel, + *initialDirectory, title, + winFilter(filter), AnyFile); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetSaveFileName(ofn)) { + result = QString::fromUcs2((ushort*)ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + cleanUpOFN(&ofn); + } , { + // Use ANSI strings and API + OPENFILENAMEA* ofn = makeOFNA(parent, isel, + *initialDirectory, title, + winFilter(filter), AnyFile); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetSaveFileNameA(ofn)) { + result = QString::fromLocal8Bit(ofn->lpstrFile); + selFilIdx = ofn->nFilterIndex; + } + cleanUpOFNA(&ofn); + }); + if (parent) { + QApplicationPrivate::leaveModal(parent); + QEvent e(QEvent::WindowUnblocked); + QApplication::sendEvent(parent, &e); + } + + if (result.isEmpty()) { + return result; + } + else { + QFileInfo fi(result); + *initialDirectory = fi.dirPath(); + if (selectedFilter) + *selectedFilter = selFilter(filter, selFilIdx); + return fi.absFilePath(); + } +} + + + +QStringList Q3FileDialog::winGetOpenFileNames(const QString &filter, + QString* initialDirectory, + QWidget *parent, + const char* /*name*/, + const QString& caption, + 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.dirPath(true); + isel = fi.fileName(); + } + + if (!fi.exists()) + *initialDirectory = QDir::homeDirPath(); + + QString title = caption; + if (title.isNull()) + title = tr("Open "); + + DWORD selFilIdx = 0; + + int idx = 0; + if (selectedFilter && !selectedFilter->isEmpty()) { + QStringList filterLst = makeFiltersList(filter); + idx = filterLst.findIndex(*selectedFilter); + } + + if (parent) { + QEvent e(QEvent::WindowBlocked); + QApplication::sendEvent(parent, &e); + QApplicationPrivate::enterModal(parent); + } + QT_WA({ + OPENFILENAME* ofn = makeOFN(parent, isel, + *initialDirectory, title, + winFilter(filter), ExistingFiles); + if (idx) + ofn->nFilterIndex = idx + 1; + if (GetOpenFileName(ofn)) { + QString fileOrDir = QString::fromUcs2((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.absFilePath(); + if (!res.isEmpty()) + result.append(res); + } + else { + // Several files selected; first string is path + dir.setPath(fileOrDir); + QString f; + while(!(f = QString::fromUcs2((ushort*)ofn->lpstrFile+offset)).isEmpty()) { + fi.setFile(dir, f); + QString res = fi.absFilePath(); + if (!res.isEmpty()) + result.append(res); + offset += f.length() + 1; + } + } + } + cleanUpOFN(&ofn); + } , { + OPENFILENAMEA* ofn = makeOFNA(parent, isel, + *initialDirectory, title, + winFilter(filter), ExistingFiles); + 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.absFilePath(); + 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.absFilePath(); + if (!res.isEmpty()) + result.append(res); + offset += f.length() + 1; + } + } + cleanUpOFNA(&ofn); + } + }); + if (parent) { + QApplicationPrivate::leaveModal(parent); + QEvent e(QEvent::WindowUnblocked); + QApplication::sendEvent(parent, &e); + } + + if (!result.isEmpty()) { + *initialDirectory = fi.dirPath(); // only save the path if there is a result + if (selectedFilter) + *selectedFilter = selFilter(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) +{ +#ifndef Q_OS_WINCE + 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, Q_ULONG(initDir->ucs2())); + } , { + SendMessageA(hwnd, BFFM_SETSELECTION, TRUE, Q_ULONG(initDir->ucs2())); + }); + } + } else if (uMsg == BFFM_SELCHANGED) { + QT_WA({ + resolveLibs(); + TCHAR path[MAX_PATH]; + ptrSHGetPathFromIDList(LPITEMIDLIST(lParam), path); + QString tmpStr = QString::fromUcs2((ushort*)path); + if (!tmpStr.isEmpty()) + SendMessage(hwnd, BFFM_ENABLEOK, 1, 1); + else + SendMessage(hwnd, BFFM_ENABLEOK, 0, 0); + SendMessage(hwnd, BFFM_SETSTATUSTEXT, 1, Q_ULONG(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, Q_ULONG(path)); + }); + } +#endif + return 0; +} + +#ifndef BIF_NEWDIALOGSTYLE +#define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize +#endif + + +QString Q3FileDialog::winGetExistingDirectory(const QString& initialDirectory, + QWidget *parent, + const char* /*name*/, + const QString& caption) +{ +#ifndef Q_OS_WINCE + QString currentDir = QDir::currentDirPath(); + QString result; + if (parent) + parent = parent->window(); + else + parent = qApp->activeWindow(); + QString title = caption; + if (title.isNull()) + title = tr("Select a Directory"); + + if (parent) { + QEvent e(QEvent::WindowBlocked); + QApplication::sendEvent(parent, &e); + QApplicationPrivate::enterModal(parent); + } + QT_WA({ + resolveLibs(); + QString initDir = QDir::toNativeSeparators(initialDirectory); + TCHAR path[MAX_PATH]; + TCHAR initPath[MAX_PATH]; + initPath[0] = 0; + path[0] = 0; + tTitle = title; + BROWSEINFO bi; + bi.hwndOwner = (parent ? parent->winId() : 0); + bi.pidlRoot = NULL; + bi.lpszTitle = (TCHAR*)tTitle.ucs2(); + bi.pszDisplayName = initPath; + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE; + bi.lpfn = winGetExistDirCallbackProc; + bi.lParam = Q_ULONG(&initDir); + LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder(&bi); + if (pItemIDList) { + ptrSHGetPathFromIDList(pItemIDList, path); + IMalloc *pMalloc; + if (SHGetMalloc(&pMalloc) != NOERROR) + result.clear(); + else { + pMalloc->Free(pItemIDList); + pMalloc->Release(); + result = QString::fromUcs2((ushort*)path); + } + } else + result.clear(); + tTitle.clear(); + } , { + QString initDir = QDir::toNativeSeparators(initialDirectory); + char path[MAX_PATH]; + char initPath[MAX_PATH]; + QByteArray ctitle = title.toLocal8Bit(); + initPath[0]=0; + path[0]=0; + BROWSEINFOA bi; + 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 = Q_ULONG(&initDir); + LPITEMIDLIST pItemIDList = SHBrowseForFolderA(&bi); + if (pItemIDList) { + SHGetPathFromIDListA(pItemIDList, path); + IMalloc *pMalloc; + if (SHGetMalloc(&pMalloc) != NOERROR) + result.clear(); + else { + pMalloc->Free(pItemIDList); + pMalloc->Release(); + result = QString::fromLocal8Bit(path); + } + } else + result.clear(); + }); + if (parent) { + QApplicationPrivate::leaveModal(parent); + QEvent e(QEvent::WindowUnblocked); + QApplication::sendEvent(parent, &e); + } + + // Due to a bug on Windows Me, we need to reset the current + // directory + if ((qWinVersion() == Qt::WV_98 || qWinVersion() == Qt::WV_Me) && QDir::currentDirPath() != currentDir) + QDir::setCurrent(currentDir); + + if (!result.isEmpty()) + result.replace(QLatin1String("\\"), QLatin1String("/")); + return result; +#else + return QString(); +#endif +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/dialogs/q3progressdialog.cpp b/src/qt3support/dialogs/q3progressdialog.cpp new file mode 100644 index 0000000000..3c9ccfd115 --- /dev/null +++ b/src/qt3support/dialogs/q3progressdialog.cpp @@ -0,0 +1,850 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 "q3progressdialog.h" + +#ifndef QT_NO_PROGRESSDIALOG + +#include "q3progressbar.h" +#include "qapplication.h" +#include "qcursor.h" +#include "qdatetime.h" +#include "qlabel.h" +#include "qpainter.h" +#include "qpushbutton.h" +#include "qshortcut.h" +#include "qstyle.h" +#include "qtimer.h" +#include + +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; + +// Various layout values +static const int margin_lr = 10; +static const int margin_tb = 10; +static const int spacing = 4; + + +class Q3ProgressDialogData +{ +public: + Q3ProgressDialogData(Q3ProgressDialog* that, QWidget* parent, + const QString& labelText, + int totalSteps) : + creator(parent), + label(new QLabel(labelText,that)), + cancel(0), + bar(new Q3ProgressBar(totalSteps, that)), + shown_once(false), + cancellation_flag(false), + showTime(defaultShowTime) + { + int align = that->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, that); + label->setAlignment(Qt::Alignment(align)); + } + + QWidget *creator; + QLabel *label; + QPushButton *cancel; + Q3ProgressBar *bar; + bool shown_once; + bool cancellation_flag; + QTime starttime; +#ifndef QT_NO_CURSOR + QCursor parentCursor; +#endif + int showTime; + bool autoClose; + bool autoReset; + bool forceHide; +}; + + +/*! + \class Q3ProgressDialog + \brief The Q3ProgressDialog class provides feedback on the progress of a slow operation. + + \compat + + 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. Q3ProgressDialog 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 setTotalSteps() (or the constructor) to set the number of + "steps" in the operation and call setProgress() as the operation + progresses. The step value 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 0, and the progress dialog shows + that the operation has finished when you call setProgress() with + totalSteps() as its argument. + + The dialog automatically resets and hides itself at the end of the + operation. Use setAutoReset() and setAutoClose() to change this + behavior. + + There are two ways of using Q3ProgressDialog: modal and modeless. + + Using a modal Q3ProgressDialog is simpler for the programmer, but you + must call QApplication::processEvents() or + QEventLoop::processEvents(ExcludeUserInput) to keep the event loop + running to ensure that the application doesn't freeze. Do the + operation in a loop, call \l setProgress() at intervals, and check + for cancellation with wasCanceled(). For example: +\snippet doc/src/snippets/code/src_qt3support_dialogs_q3progressdialog.cpp 0 + + 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 Q3ProgressBar 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 + setProgress() at intervals. For example: +\snippet doc/src/snippets/code/src_qt3support_dialogs_q3progressdialog.cpp 1 + + + 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. + + \inlineimage qprogdlg-m.png Screenshot in Motif style + \inlineimage qprogdlg-w.png Screenshot in Windows style + + \sa QDialog, Q3ProgressBar, {fowler}{GUI Design Handbook: Progress Indicator} +*/ + + +/*! + Returns the QLabel currently being displayed above the progress bar. + This QLabel is owned by the Q3ProgressDialog. + + \sa setLabel() +*/ +QLabel *Q3ProgressDialog::label() const +{ + return d->label; +} + +/*! + Returns the Q3ProgressBar currently being used to display progress. + This Q3ProgressBar is owned by the Q3ProgressDialog. + + \sa setBar() +*/ +Q3ProgressBar *Q3ProgressDialog::bar() const +{ + return d->bar; +} + + +/*! + Constructs a progress dialog. + + Default settings: + \list + \i The label text is empty. + \i The cancel button text is (translated) "Cancel". + \i The total number of steps is 100. + \endlist + + The \a creator argument is the widget to use as the dialog's parent. + The \a name, \a modal, and the widget flags, \a f, are + passed to the QDialog::QDialog() constructor. If \a modal is false (the + default), you must have an event loop proceeding for any redrawing + of the dialog to occur. If \a modal is true, the dialog ensures that + events are processed when needed. + + \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(), + setTotalSteps() +*/ + +Q3ProgressDialog::Q3ProgressDialog(QWidget *creator, const char *name, + bool modal, Qt::WindowFlags f) + : QDialog(creator, f) +{ + setObjectName(QLatin1String(name)); + setModal(modal); + init(creator, QString::fromLatin1(""), tr("Cancel"), 100); +} + +/*! + Constructs a progress dialog. + + The \a labelText is text used to remind the user what is progressing. + + The \a cancelButtonText is the text to display on the cancel button, + or 0 if no cancel button is to be shown. + + The \a totalSteps is the total 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 would be 50. Before + examining the first file, call setProgress(0). As each file is + processed call setProgress(1), setProgress(2), etc., finally + calling setProgress(50) after examining the last file. + + The \a creator argument is the widget to use as the dialog's parent. + The \a name, \a modal, and widget flags, \a f, are passed to the + QDialog::QDialog() constructor. If \a modal is false (the default), + you will must have an event loop proceeding for any redrawing of + the dialog to occur. If \a modal is true, the dialog ensures that + events are processed when needed. + + + \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(), + setTotalSteps() +*/ + +Q3ProgressDialog::Q3ProgressDialog(const QString &labelText, + const QString &cancelButtonText, + int totalSteps, + QWidget *creator, const char *name, + bool modal, Qt::WindowFlags f) + : QDialog(creator, f) +{ + setObjectName(QLatin1String(name)); + setModal(modal); + init(creator, labelText, cancelButtonText, totalSteps); +} + +/*! + Constructs a progress dialog. + + Default settings: + \list + \i The label text is empty. + \i The cancel button text is (translated) "Cancel". + \i The total number of steps is 100. + \endlist + + The \a creator argument is the widget to use as the dialog's parent. + The widget flags, \a f, are passed to the QDialog::QDialog() constructor. + + \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(), + setTotalSteps() +*/ +Q3ProgressDialog::Q3ProgressDialog(QWidget *creator, Qt::WindowFlags f) + : QDialog(creator, f) +{ + init(creator, QString::fromLatin1(""), tr("Cancel"), 100); +} + +/*! + Constructs a progress dialog. + + The \a labelText is text used to remind the user what is progressing. + + The \a cancelButtonText is the text to display on the cancel button, + or 0 if no cancel button is to be shown. + + The \a totalSteps is the total 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 would be 50. Before + examining the first file, call setProgress(0). As each file is + processed call setProgress(1), setProgress(2), etc., finally + calling setProgress(50) after examining the last file. + + The \a creator argument is the widget to use as the dialog's parent. + The widget flags, \a f, are passed to the + QDialog::QDialog() constructor. + + \sa setLabelText(), setLabel(), setCancelButtonText(), setCancelButton(), + setTotalSteps() +*/ +Q3ProgressDialog::Q3ProgressDialog(const QString &labelText, + const QString &cancelButtonText, + int totalSteps, QWidget *creator, Qt::WindowFlags f) + : QDialog(creator, f) +{ + init(creator, labelText, cancelButtonText, totalSteps); +} + +/*! + Destroys the progress dialog. +*/ + +Q3ProgressDialog::~Q3ProgressDialog() +{ +#ifndef QT_NO_CURSOR + if (d->creator) + d->creator->setCursor(d->parentCursor); +#endif + delete d; +} + +void Q3ProgressDialog::init(QWidget *creator, + const QString& lbl, const QString& canc, + int totstps) +{ + d = new Q3ProgressDialogData(this, creator, lbl, totstps); + d->autoClose = true; + d->autoReset = true; + d->forceHide = false; + setCancelButtonText(canc); + connect(this, SIGNAL(canceled()), this, SIGNAL(cancelled())); + connect(this, SIGNAL(canceled()), this, SLOT(cancel())); + forceTimer = new QTimer(this); + connect(forceTimer, SIGNAL(timeout()), this, SLOT(forceShow())); + layout(); +} + +/*! + \fn void Q3ProgressDialog::canceled() + + This signal is emitted when the cancel button is clicked. + It is connected to the cancel() slot by default. + + \sa wasCanceled() +*/ + +/*! + \fn void Q3ProgressDialog::cancelled() + + Use canceled() instead. +*/ + +/*! + 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 Q3ProgressDialog::setLabel(QLabel *label) +{ + 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 Q3ProgressDialog::labelText + \brief the label's text + + The default text is an empty string. +*/ + +QString Q3ProgressDialog::labelText() const +{ + if (label()) + return label()->text(); + return QString(); +} + +void Q3ProgressDialog::setLabelText(const QString &text) +{ + if (label()) { + 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. + + \sa setCancelButtonText() +*/ + +void Q3ProgressDialog::setCancelButton(QPushButton *cancelButton) +{ + 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())); + new QShortcut(Qt::Key_Escape, this, SIGNAL(canceled())); + } + 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. + \sa setCancelButton() +*/ + +void Q3ProgressDialog::setCancelButtonText(const QString &cancelButtonText) +{ + 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 Q3ProgressDialog::setBar(Q3ProgressBar *bar) +{ +#ifndef QT_NO_DEBUG + if (progress() > 0) + qWarning("Q3ProgressDialog::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 Q3ProgressDialog::wasCanceled + \brief whether the dialog was canceled + + \sa setProgress() +*/ + +bool Q3ProgressDialog::wasCanceled() const +{ + return d->cancellation_flag; +} + +/*! + \property Q3ProgressDialog::wasCancelled + \brief whether the dialog was canceled + \since 4.2 + + Use \l wasCanceled instead. +*/ + +/*! + Use wasCanceled() instead. +*/ +bool Q3ProgressDialog::wasCancelled() const +{ + return d->cancellation_flag; +} + +/*! + \property Q3ProgressDialog::totalSteps + \brief the total number of steps + + The default is 0. +*/ + +int Q3ProgressDialog::totalSteps() const +{ + if (d && d->bar) + return bar()->totalSteps(); + return 0; +} + +void Q3ProgressDialog::setTotalSteps(int totalSteps) +{ + bar()->setTotalSteps(totalSteps); +} + + +/*! + Resets the progress dialog. + The progress dialog becomes hidden if autoClose() is true. + + \sa setAutoClose(), setAutoReset() +*/ + +void Q3ProgressDialog::reset() +{ +#ifndef QT_NO_CURSOR + if (progress() >= 0) { + if (d->creator) + d->creator->setCursor(d->parentCursor); + } +#endif + if (d->autoClose || d->forceHide) + hide(); + bar()->reset(); + d->cancellation_flag = false; + d->shown_once = false; + forceTimer->stop(); +} + +/*! + Resets the progress dialog. wasCanceled() becomes true until + the progress dialog is reset. + The progress dialog becomes hidden. +*/ + +void Q3ProgressDialog::cancel() +{ + d->forceHide = true; + reset(); + d->forceHide = false; + d->cancellation_flag = true; +} + +/*! + \property Q3ProgressDialog::progress + \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 + Q3ProgressDialog::totalSteps(); you can call setProgress() any number of times + in-between. + + \warning If the progress dialog is modal + (see Q3ProgressDialog::Q3ProgressDialog()), + 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 Q3ProgressDialog inside a paintEvent()! + + \sa totalSteps +*/ + +int Q3ProgressDialog::progress() const +{ + return bar()->progress(); +} + +void Q3ProgressDialog::setProgress(int progress) +{ + if (progress == bar()->progress() + || (bar()->progress() == -1 && progress == bar()->totalSteps())) + return; + + bar()->setProgress(progress); + + if (d->shown_once) { + if (isModal()) + qApp->processEvents(); + } else { + if (progress == 0) { +#ifndef QT_NO_CURSOR + if (d->creator) { + d->parentCursor = d->creator->cursor(); + d->creator->setCursor(Qt::WaitCursor); + } +#endif + d->starttime.start(); + 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; + if ((totalSteps() - progress) >= INT_MAX / elapsed) + estimate = (totalSteps() - progress) / progress * elapsed; + else + estimate = elapsed * (totalSteps() - progress) / progress; + 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 == bar()->totalSteps() && d->autoReset) + reset(); +} + +/*! + \overload + + Sets the current amount of progress to \a progress and the total number of + steps to \a totalSteps. + + \sa setTotalSteps() +*/ + +void Q3ProgressDialog::setProgress(int progress, int totalSteps) +{ + setTotalSteps(totalSteps); + setProgress(progress); +} + +/*! + 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 Q3ProgressDialog::sizeHint() const +{ + QSize sh = label()->sizeHint(); + QSize bh = bar()->sizeHint(); + int h = margin_tb*2 + bh.height() + sh.height() + spacing; + if (d->cancel) + h += d->cancel->sizeHint().height() + spacing; + return QSize(qMax(200, sh.width() + 2*margin_lr), h); +} + +/*!\reimp +*/ +void Q3ProgressDialog::resizeEvent(QResizeEvent *) +{ + layout(); +} + +/*! + \reimp +*/ +void Q3ProgressDialog::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) + layout(); + QDialog::changeEvent(ev); +} + +void Q3ProgressDialog::layout() +{ + int sp = spacing; + int mtb = margin_tb; + int mlr = qMin(width()/10, margin_lr); + const bool centered = + bool(style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, this)); + + QSize cs = d->cancel ? d->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 = d->cancel ? cs.height() + sp : 0; + lh = qMax(0, height() - mtb - bh.height() - sp - cspc); + + if (lh < height()/4) { + // Getting cramped + sp /= 2; + mtb /= 2; + if (d->cancel) { + cs.setHeight(qMax(4,cs.height()-sp-2)); + } + bh.setHeight(qMax(4,bh.height()-sp-1)); + } else { + break; + } + } + + if (d->cancel) { + d->cancel->setGeometry( + centered ? width()/2 - cs.width()/2 : width() - mlr - cs.width(), + height() - mtb - cs.height() + sp, + cs.width(), cs.height()); + } + + label()->setGeometry(mlr, 0, width()-mlr*2, lh); + bar()->setGeometry(mlr, lh+sp, width()-mlr*2, bh.height()); +} + +/*! + \property Q3ProgressDialog::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 Q3ProgressDialog::setMinimumDuration(int ms) +{ + d->showTime = ms; + if (bar()->progress() == 0) { + forceTimer->stop(); + forceTimer->start(ms); + } +} + +int Q3ProgressDialog::minimumDuration() const +{ + return d->showTime; +} + + +/*! + \reimp +*/ + +void Q3ProgressDialog::closeEvent(QCloseEvent *e) +{ + emit canceled(); + QDialog::closeEvent(e); +} + +/*! + \property Q3ProgressDialog::autoReset + \brief whether the progress dialog calls reset() as soon as progress() equals totalSteps() + + The default is true. + + \sa setAutoClose() +*/ + +void Q3ProgressDialog::setAutoReset(bool b) +{ + d->autoReset = b; +} + +bool Q3ProgressDialog::autoReset() const +{ + return d->autoReset; +} + +/*! + \property Q3ProgressDialog::autoClose + \brief whether the dialog gets hidden by reset() + + The default is true. + + \sa setAutoReset() +*/ + +void Q3ProgressDialog::setAutoClose(bool b) +{ + d->autoClose = b; +} + +bool Q3ProgressDialog::autoClose() const +{ + return d->autoClose; +} + +/*! + \reimp +*/ + +void Q3ProgressDialog::showEvent(QShowEvent *e) +{ + QDialog::showEvent(e); + int w = qMax(isVisible() ? width() : 0, sizeHint().width()); + int h = qMax(isVisible() ? height() : 0, sizeHint().height()); + resize(w, h); + forceTimer->stop(); +} + +/*! + Shows the dialog if it is still hidden after the algorithm has been started + and minimumDuration milliseconds have passed. + + \sa setMinimumDuration() +*/ + +void Q3ProgressDialog::forceShow() +{ + if (d->shown_once || d->cancellation_flag) + return; + + show(); + d->shown_once = true; +} + + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/dialogs/q3progressdialog.h b/src/qt3support/dialogs/q3progressdialog.h new file mode 100644 index 0000000000..4a4bc80882 --- /dev/null +++ b/src/qt3support/dialogs/q3progressdialog.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 Q3PROGRESSDIALOG_H +#define Q3PROGRESSDIALOG_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_PROGRESSDIALOG + +class Q3ProgressDialogData; +class QLabel; +class QPushButton; +class QTimer; +class Q3ProgressBar; + +class Q_COMPAT_EXPORT Q3ProgressDialog : public QDialog +{ + Q_OBJECT + Q_PROPERTY(bool wasCanceled READ wasCanceled) + Q_PROPERTY(bool wasCancelled READ wasCancelled DESIGNABLE false STORED false) + Q_PROPERTY(int totalSteps READ totalSteps WRITE setTotalSteps) + Q_PROPERTY(int progress READ progress WRITE setProgress) + 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: + Q3ProgressDialog(QWidget* parent, const char* name, bool modal=false, + Qt::WindowFlags f=0); + Q3ProgressDialog(const QString& labelText, + const QString &cancelButtonText, int totalSteps, + QWidget* parent=0, const char* name=0, + bool modal=false, Qt::WindowFlags f=0); + Q3ProgressDialog(QWidget* parent = 0, Qt::WindowFlags f = 0); + Q3ProgressDialog(const QString& labelText, const QString &cancelButtonText, + int totalSteps, QWidget* parent=0, Qt::WindowFlags f=0); + ~Q3ProgressDialog(); + + void setLabel(QLabel *); + void setCancelButton(QPushButton *); + void setBar(Q3ProgressBar *); + + bool wasCanceled() const; + bool wasCancelled() const; + + int totalSteps() const; + int progress() const; + + QSize sizeHint() const; + + QString labelText() const; + + void setAutoReset(bool b); + bool autoReset() const; + void setAutoClose(bool b); + bool autoClose() const; + +public Q_SLOTS: + void cancel(); + void reset(); + void setTotalSteps(int totalSteps); + void setProgress(int progress); + void setProgress(int progress, int totalSteps); + void setLabelText(const QString &); + void setCancelButtonText(const QString &); + + void setMinimumDuration(int ms); +public: + int minimumDuration() const; + +Q_SIGNALS: + void canceled(); + void cancelled(); + +protected: + void resizeEvent(QResizeEvent *); + void closeEvent(QCloseEvent *); + void changeEvent(QEvent *); + void showEvent(QShowEvent *e); + +protected Q_SLOTS: + void forceShow(); + +private: + void init(QWidget *creator, const QString& lbl, const QString &canc, + int totstps); + void layout(); + QLabel *label() const; + Q3ProgressBar *bar() const; + Q3ProgressDialogData *d; + QTimer *forceTimer; + +private: + Q_DISABLE_COPY(Q3ProgressDialog) +}; + +#endif // QT_NO_PROGRESSDIALOG + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3PROGRESSDIALOG_H diff --git a/src/qt3support/dialogs/q3tabdialog.cpp b/src/qt3support/dialogs/q3tabdialog.cpp new file mode 100644 index 0000000000..6242dcec80 --- /dev/null +++ b/src/qt3support/dialogs/q3tabdialog.cpp @@ -0,0 +1,1076 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 "q3tabdialog.h" + +#include "qtabbar.h" +#include "qtabwidget.h" +#include "qpushbutton.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qapplication.h" +#include "q3widgetstack.h" +#include "qlayout.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt; + +/*! + \class Q3TabDialog + \compat + \brief The Q3TabDialog class provides a stack of tabbed widgets. + + A tabbed dialog is one in which several "tab pages" are available. + By clicking on a tab page's tab or by pressing the indicated + Alt+\e{letter} key combination, the user can select which tab page + they want to use. + + Q3TabDialog provides a tab bar consisting of single row of tabs at + the top; each tab has an associated widget which is that tab's + tab page. In addition, Q3TabDialog provides an OK button and the + following optional buttons: Apply, Cancel, Defaults and Help. + + The normal way to use Q3TabDialog is to do the following in the + constructor: + \list 1 + \i Create a Q3TabDialog. + \i Create a QWidget for each of the pages in the tab dialog, insert + children into it, set up geometry management for it, and use + addTab() (or insertTab()) to set up a tab and keyboard accelerator + for it. + \i Set up the buttons for the tab dialog using setOkButton(), + setApplyButton(), setDefaultsButton(), setCancelButton() and + setHelpButton(). + \i Connect to the signals and slots. + \endlist + + If you don't call addTab() the page you have created will not be + visible. Don't confuse the object name you supply to the + QWidget constructor and the tab label you supply to addTab(); + addTab() takes user-visible name that appears on the widget's tab + and may identify an accelerator, whereas the widget name is used + primarily for debugging. + + Almost all applications have to connect the applyButtonPressed() + signal to something. applyButtonPressed() is emitted when either OK + or Apply is clicked, and your slot must copy the dialog's state into + the application. + + There are also several other signals which may be useful: + \list + \i cancelButtonPressed() is emitted when the user clicks Cancel. + \i defaultButtonPressed() is emitted when the user clicks Defaults; + the slot it is connected to should reset the state of the dialog to + the application defaults. + \i helpButtonPressed() is emitted when the user clicks Help. + \i aboutToShow() is emitted at the start of show(); if there is any + chance that the state of the application may change between the + creation of the tab dialog and the time show() is called, you must + connect this signal to a slot that resets the state of the dialog. + \i currentChanged() is emitted when the user selects a page. + \endlist + + Each tab is either enabled or disabled at any given time (see + setTabEnabled()). If a tab is enabled the tab text is drawn in + black and the user can select that tab. If it is disabled the tab + is drawn in a different way and the user cannot select that tab. + Note that even if a tab is disabled, the page can still be visible; + for example, if all of the tabs happen to be disabled. + + You can change a tab's label and iconset using changeTab(). A tab + page can be removed with removePage() and shown with showPage(). The + current page is given by currentPage(). + + Q3TabDialog does not support tabs on the sides or bottom, nor can + you set or retrieve the visible page. If you need more functionality + than Q3TabDialog provides, consider creating a QDialog and using a + QTabBar with QTabWidgets. + + Most of the functionality in Q3TabDialog is provided by a QTabWidget. +*/ + +/*! + \fn void Q3TabDialog::selected(const QString &name) + + This signal is emitted whenever a tab is selected (raised), + including during the first show(). \a name is the name of the + selected tab. + + \sa raise() +*/ + +/*! \fn void Q3TabDialog::currentChanged(QWidget *widget) + + This signal is emitted whenever the current page changes. \a widget + is the new current page. + + \sa currentPage(), showPage(), tabLabel() +*/ + +class Q3TabDialogPrivate +{ +public: + Q3TabDialogPrivate(); + + QTabWidget* tw; + + QPushButton * ok; + QPushButton * cb; + QPushButton * db; + QPushButton * hb; + QPushButton * ab; + + QBoxLayout * tll; +}; + +Q3TabDialogPrivate::Q3TabDialogPrivate() + : tw(0), + ok(0), cb(0), db(0), hb(0), ab(0), + tll(0) +{ } + +/*! + Constructs a Q3TabDialog with only an OK button. + The \a parent, \a name, \a modal and widget flag, \a f, arguments + are passed on to the QDialog constructor. +*/ + +Q3TabDialog::Q3TabDialog(QWidget *parent, const char *name, bool modal, Qt::WindowFlags f) + : QDialog(parent, name, modal, f) +{ + d = new Q3TabDialogPrivate; + Q_CHECK_PTR(d); + + d->tw = new QTabWidget(this, "tab widget"); + connect (d->tw, SIGNAL (selected(QString)), this, SIGNAL(selected(QString))); + connect (d->tw, SIGNAL (currentChanged(QWidget*)), this, SIGNAL(currentChanged(QWidget*))); + + d->ok = new QPushButton(this, "ok"); + Q_CHECK_PTR(d->ok); + d->ok->setText(tr("OK")); + d->ok->setDefault(true); + connect(d->ok, SIGNAL(clicked()), + this, SIGNAL(applyButtonPressed())); + connect(d->ok, SIGNAL(clicked()), + this, SLOT(accept())); +} + + +/*! + Destroys the tab dialog. +*/ + +Q3TabDialog::~Q3TabDialog() +{ + delete d; +} + + +/*! + Sets the font for the tabs to \a font. + + If the widget is visible, the display is updated with the new font + immediately. There may be some geometry changes, depending on the + size of the old and new fonts. +*/ + +void Q3TabDialog::setFont(const QFont & font) +{ + QDialog::setFont(font); + setSizes(); +} + + +/*! + \fn void Q3TabDialog::applyButtonPressed(); + + This signal is emitted when either the Apply or OK button is clicked. + + It should be connected to a slot (or several slots) that change the + application's state according to the state of the dialog. + + \sa cancelButtonPressed() defaultButtonPressed() setApplyButton() +*/ + + +/*! + Returns true if the tab dialog has a Defaults button; otherwise + returns false. + + \sa setDefaultButton() defaultButtonPressed() hasApplyButton() + hasCancelButton() +*/ + +bool Q3TabDialog::hasDefaultButton() const +{ + return d->db != 0; +} + + +/*! + Returns true if the tab dialog has a Help button; otherwise returns + false. + + \sa setHelpButton() helpButtonPressed() hasApplyButton() + hasCancelButton() +*/ + +bool Q3TabDialog::hasHelpButton() const +{ + return d->hb != 0; +} + + +/*! + \fn void Q3TabDialog::cancelButtonPressed(); + + This signal is emitted when the Cancel button is clicked. It is + automatically connected to QDialog::reject(), which will hide the + dialog. + + The Cancel button should not change the application's state at all, + so you should generally not need to connect it to any slot. + + \sa applyButtonPressed() defaultButtonPressed() setCancelButton() +*/ + + +/*! + Returns true if the tab dialog has a Cancel button; otherwise + returns false. + + \sa setCancelButton() cancelButtonPressed() hasApplyButton() + hasDefaultButton() +*/ + +bool Q3TabDialog::hasCancelButton() const +{ + return d->cb != 0; +} + + +/*! + \fn void Q3TabDialog::defaultButtonPressed(); + + This signal is emitted when the Defaults button is pressed. It + should reset the dialog (but not the application) to the "factory + defaults". + + The application's state should not be changed until the user clicks + Apply or OK. + + \sa applyButtonPressed() cancelButtonPressed() setDefaultButton() +*/ + + +/*! + \fn void Q3TabDialog::helpButtonPressed(); + + This signal is emitted when the Help button is pressed. It + could be used to present information about how to use the dialog. + + \sa applyButtonPressed() cancelButtonPressed() setHelpButton() +*/ + + +/*! + Returns true if the tab dialog has an Apply button; otherwise + returns false. + + \sa setApplyButton() applyButtonPressed() hasCancelButton() + hasDefaultButton() +*/ + +bool Q3TabDialog::hasApplyButton() const +{ + return d->ab != 0; +} + + +/*! + Returns true if the tab dialog has an OK button; otherwise returns + false. + + \sa setOkButton() hasApplyButton() hasCancelButton() + hasDefaultButton() +*/ + +bool Q3TabDialog::hasOkButton() const +{ + return d->ok != 0; +} + + +/*! + \fn void Q3TabDialog::aboutToShow() + + This signal is emitted by show() when it is time to set the state of + the dialog's contents. The dialog should reflect the current state + of the application when it appears; if there is any possibility that + the state of the application may change between the time you call + Q3TabDialog() and show(), you should set the + dialog's state in a slot and connect this signal to it. + + This applies mainly to Q3TabDialog objects that are kept around + hidden, rather than being created, shown, and deleted afterwards. + + \sa applyButtonPressed(), QWidget::show(), cancelButtonPressed() +*/ + + +/*! + \internal + + Implemented to delay show()'ing of every page. +*/ +void Q3TabDialog::show() +{ + // Reimplemented in order to delay show()'ing of every page + // except the initially visible one, and in order to emit the + // aboutToShow() signal. + if (window() == this) + d->tw->setFocus(); + emit aboutToShow(); + setSizes(); + setUpLayout(); + QDialog::show(); +} + + + +/*! + Adds another tab and page to the tab view. + + The new page is \a child; the tab's label is \a label. + Note the difference between the widget name (which you supply to + widget constructors and to setTabEnabled(), for example) and the tab + label. The name is internal to the program and invariant, whereas + the label is shown on-screen and may vary according to language and + other factors. + + If the tab's \a label contains an ampersand, the letter following + the ampersand is used as an accelerator for the tab, e.g. if the + label is "Bro&wse" then Alt+W becomes an accelerator which will + move the focus to this tab. + + If you call addTab() after show() the screen will flicker and the + user may be confused. + + \sa insertTab() +*/ + +void Q3TabDialog::addTab(QWidget * child, const QString &label) +{ + d->tw->addTab(child, label); +} + + + +/*! \overload + + This version of the function shows the \a iconset as well as the \a + label on the tab of \a child. +*/ +void Q3TabDialog::addTab(QWidget *child, const QIcon& iconset, const QString &label) +{ + d->tw->addTab(child, iconset, label); +} + + +/*! + Inserts another tab and page to the tab view. + + The new page is \a child; the tab's label is \a label. + Note the difference between the widget name (which you supply to + widget constructors and to setTabEnabled(), for example) and the tab + label. The name is internal to the program and invariant, whereas + the label is shown on-screen and may vary according to language and + other factors. + + If the tab's \a label contains an ampersand, the letter following + the ampersand is used as an accelerator for the tab, e.g. if the + label is "Bro&wse" then Alt+W becomes an accelerator which will + move the focus to this tab. + + If \a index is not specified, the tab is simply added. Otherwise + it is inserted at the specified position. + + If you call insertTab() after show(), the screen will flicker and the + user may be confused. + + \sa addTab() +*/ + +void Q3TabDialog::insertTab(QWidget * child, const QString &label, int index) +{ + d->tw->insertTab(child, label, index); +} + + +/*! \overload + + This version of the function shows the \a iconset as well as the \a + label on the tab of \a child. + */ +void Q3TabDialog::insertTab(QWidget *child, const QIcon& iconset, const QString &label, int index) +{ + d->tw->insertTab(child, iconset, label, index); +} + +/*! + Replaces the QTabBar heading the dialog by the given tab bar, \a tb. + Note that this must be called \e before any tabs have been added, + or the behavior is undefined. + \sa tabBar() +*/ +void Q3TabDialog::setTabBar(QTabBar* tb) +{ + if (tb == 0){ + qWarning("Q3TabDialog::setTabBar() called with null QTabBar pointer"); + return; + } + + d->tw->setTabBar(tb); + setUpLayout(); +} + +/*! + Returns the currently set QTabBar. + \sa setTabBar() +*/ +QTabBar* Q3TabDialog::tabBar() const +{ + return d->tw->tabBar(); +} + +/*! Ensures that widget \a w is shown. This is mainly useful for accelerators. + + \warning If used carelessly, this function can easily surprise or + confuse the user. + + \sa QTabBar::setCurrentTab() +*/ + +void Q3TabDialog::showPage(QWidget * w) +{ + d->tw->showPage(w); +} + + +/*! \obsolete + Returns true if the page with object name \a name is enabled and + false if it is disabled. + + If \a name is 0 or not the name of any of the pages, isTabEnabled() + returns false. + + \sa setTabEnabled(), QWidget::isEnabled() +*/ + +bool Q3TabDialog::isTabEnabled(const char* name) const +{ + if (!name) + return false; + QObjectList l = this->queryList("QWidget", name, false, true); + if (!l.isEmpty()) { + for (int i = 0; i < l.size(); ++i) { + QObject *o = l.at(i); + if (!o->isWidgetType()) + continue; + QWidget *w = static_cast(o); + return d->tw->isTabEnabled(w); + } + } + return false; +} + + +/*!\obsolete + + Finds the page with object name \a name, enables/disables it + according to the value of \a enable and redraws the page's tab + appropriately. + + Q3TabDialog uses QWidget::setEnabled() internally, rather than keeping a + separate flag. + + Note that even a disabled tab/page may be visible. If the page is + already visible Q3TabDialog will not hide it; if all the pages + are disabled Q3TabDialog will show one of them. + + The object name is used (rather than the tab label) because the tab + text may not be invariant in multi-language applications. + + \sa isTabEnabled(), QWidget::setEnabled() +*/ + +void Q3TabDialog::setTabEnabled(const char* name, bool enable) +{ + if (!name) + return; + QObjectList l = this->queryList("QWidget", name, false, true); + if (!l.isEmpty()) { + for (int i = 0; i < l.size(); ++i) { + QObject *o = l.at(i); + if(o->isWidgetType()) + d->tw->setTabEnabled(static_cast(o), enable); + } + } +} + + +/* ### SHOULD THIS BE HERE? + Adds an Apply button to the dialog. The button's text is set to \e + text (and defaults to "Apply"). + + The Apply button should apply the current settings in the dialog box + to the application, while keeping the dialog visible. + + When Apply is clicked, the applyButtonPressed() signal is emitted. + + If \a text is an empty string, no button is shown. + + \sa setCancelButton() setDefaultButton() applyButtonPressed() +*/ + + +/*! + Returns true if the page \a w is enabled; otherwise returns false. + + \sa setTabEnabled(), QWidget::isEnabled() +*/ + +bool Q3TabDialog::isTabEnabled(QWidget* w) const +{ + return d->tw->isTabEnabled(w); +} + +/*! + If \a enable is true the page \a w is enabled; otherwise \a w is + disabled. The page's tab is redrawn appropriately. + + QTabWidget uses QWidget::setEnabled() internally, rather than keeping a + separate flag. + + Note that even a disabled tab and tab page may be visible. If the + page is already visible QTabWidget will not hide it; if all the + pages are disabled QTabWidget will show one of them. + + \sa isTabEnabled(), QWidget::setEnabled() +*/ + +void Q3TabDialog::setTabEnabled(QWidget* w, bool enable) +{ + d->tw->setTabEnabled(w, enable); +} + + +/*! + Adds an Apply button to the dialog. The button's text is set to \a + text. + + The Apply button should apply the current settings in the dialog box + to the application while keeping the dialog visible. + + When Apply is clicked, the applyButtonPressed() signal is emitted. + + If \a text is an empty string, no button is shown. + + \sa setCancelButton() setDefaultButton() applyButtonPressed() +*/ +void Q3TabDialog::setApplyButton(const QString &text) +{ + if (text.isEmpty() && d->ab) { + delete d->ab; + d->ab = 0; + setSizes(); + } else { + if (!d->ab) { + d->ab = new QPushButton(this, "apply settings"); + connect(d->ab, SIGNAL(clicked()), + this, SIGNAL(applyButtonPressed())); + setUpLayout(); + } + d->ab->setText(text); + setSizes(); + //d->ab->show(); + } +} + +/*! + \overload + + Adds an Apply button to the dialog. The button's text is set to + a localizable "Apply". + */ +void Q3TabDialog::setApplyButton() +{ + setApplyButton(tr("Apply")); +} + + +/*! + Adds a Help button to the dialog. The button's text is set to \a + text. + + When Help is clicked, the helpButtonPressed() signal is emitted. + + If \a text is an empty string, no button is shown. + + \sa setApplyButton() setCancelButton() helpButtonPressed() +*/ + +void Q3TabDialog::setHelpButton(const QString &text) +{ + if (text.isEmpty()) { + delete d->hb; + d->hb = 0; + setSizes(); + } else { + if (!d->hb) { + d->hb = new QPushButton(this, "give help"); + connect(d->hb, SIGNAL(clicked()), + this, SIGNAL(helpButtonPressed())); + setUpLayout(); + } + d->hb->setText(text); + setSizes(); + //d->hb->show(); + } +} + + +/*! + \overload + + Adds a Help button to the dialog. The button's text is set to + a localizable "Help". + */ +void Q3TabDialog::setHelpButton() +{ + setHelpButton(tr("Help")); +} + +/*! + Adds a Defaults button to the dialog. The button's text is set to \a + text. + + The Defaults button should set the dialog (but not the application) + back to the application defaults. + + When Defaults is clicked, the defaultButtonPressed() signal is emitted. + + If \a text is an empty string, no button is shown. + + \sa setApplyButton() setCancelButton() defaultButtonPressed() +*/ + +void Q3TabDialog::setDefaultButton(const QString &text) +{ + if (text.isEmpty()) { + delete d->db; + d->db = 0; + setSizes(); + } else { + if (!d->db) { + d->db = new QPushButton(this, "back to default"); + connect(d->db, SIGNAL(clicked()), + this, SIGNAL(defaultButtonPressed())); + setUpLayout(); + } + d->db->setText(text); + setSizes(); + //d->db->show(); + } +} + + +/*! + \overload + + Adds a Defaults button to the dialog. The button's text is set to + a localizable "Defaults". + */ +void Q3TabDialog::setDefaultButton() +{ + setDefaultButton(tr("Defaults")); +} + +/*! + Adds a Cancel button to the dialog. The button's text is set to \a + text. + + The cancel button should always return the application to the state + it was in before the tab view popped up, or if the user has clicked + Apply, back to the state immediately after the last Apply. + + When Cancel is clicked, the cancelButtonPressed() signal is emitted. + The dialog is closed at the same time. + + If \a text is an empty string, no button is shown. + + \sa setApplyButton() setDefaultButton() cancelButtonPressed() +*/ + +void Q3TabDialog::setCancelButton(const QString &text) +{ + if (text.isEmpty()) { + delete d->cb; + d->cb = 0; + setSizes(); + } else { + if (!d->cb) { + d->cb = new QPushButton(this, "cancel dialog"); + connect(d->cb, SIGNAL(clicked()), + this, SIGNAL(cancelButtonPressed())); + connect(d->cb, SIGNAL(clicked()), + this, SLOT(reject())); + setUpLayout(); + } + d->cb->setText(text); + setSizes(); + //d->cb->show(); + } +} + + +/*! + \overload + + Adds a Cancel button to the dialog. The button's text is set to + a localizable "Cancel". + */ + +void Q3TabDialog::setCancelButton() +{ + setCancelButton(tr("Cancel")); +} + +/*! Sets up the layout manager for the tab dialog. + + \sa setSizes() setApplyButton() setCancelButton() setDefaultButton() +*/ + +void Q3TabDialog::setUpLayout() +{ + // the next four are probably the same, really? + const int topMargin = 6; + const int leftMargin = 6; + const int rightMargin = 6; + const int bottomMargin = 6; + const int betweenButtonsMargin = 7; + const int aboveButtonsMargin = 8; + + delete d->tll; + d->tll = new QBoxLayout(this, QBoxLayout::Down); + + // top margin + d->tll->addSpacing(topMargin); + + QBoxLayout * tmp = new QHBoxLayout(); + d->tll->addLayout(tmp, 1); + tmp->addSpacing(leftMargin); + tmp->addWidget(d->tw, 1); + tmp->addSpacing(rightMargin + 2); + + d->tll->addSpacing(aboveButtonsMargin + 2); + QBoxLayout * buttonRow = new QBoxLayout(QBoxLayout::RightToLeft); + d->tll->addLayout(buttonRow, 0); + d->tll->addSpacing(bottomMargin); + + buttonRow->addSpacing(rightMargin); + if (d->cb) { + buttonRow->addWidget(d->cb, 0); + buttonRow->addSpacing(betweenButtonsMargin); + d->cb->raise(); + } + + if (d->ab) { + buttonRow->addWidget(d->ab, 0); + buttonRow->addSpacing(betweenButtonsMargin); + d->ab->raise(); + } + + if (d->db) { + buttonRow->addWidget(d->db, 0); + buttonRow->addSpacing(betweenButtonsMargin); + d->db->raise(); + } + + if (d->hb) { + buttonRow->addWidget(d->hb, 0); + buttonRow->addSpacing(betweenButtonsMargin); + d->hb->raise(); + } + + if (d->ok) { + buttonRow->addWidget(d->ok, 0); + buttonRow->addSpacing(betweenButtonsMargin); + d->ok->raise(); + } + + // add one custom widget here + buttonRow->addStretch(1); + // add another custom widget here + + d->tll->activate(); +} + + +/*! Sets up the minimum and maximum sizes for each child widget. + + \sa setUpLayout() setFont() +*/ + +void Q3TabDialog::setSizes() +{ + // compute largest button size + QSize s(0, 0); + int bw = s.width(); + int bh = s.height(); + + if (d->ok) { + s = d->ok->sizeHint(); + if (s.width() > bw) + bw = s.width(); + if (s.height() > bh) + bh = s.height(); + } + + if (d->ab) { + s = d->ab->sizeHint(); + if (s.width() > bw) + bw = s.width(); + if (s.height() > bh) + bh = s.height(); + } + + if (d->db) { + s = d->db->sizeHint(); + if (s.width() > bw) + bw = s.width(); + if (s.height() > bh) + bh = s.height(); + } + + if (d->hb) { + s = d->hb->sizeHint(); + if (s.width() > bw) + bw = s.width(); + if (s.height() > bh) + bh = s.height(); + } + + if (d->cb) { + s = d->cb->sizeHint(); + if (s.width() > bw) + bw = s.width(); + if (s.height() > bh) + bh = s.height(); + } + + // and set all the buttons to that size + if (d->ok) + d->ok->setFixedSize(bw, bh); + if (d->ab) + d->ab->setFixedSize(bw, bh); + if (d->db) + d->db->setFixedSize(bw, bh); + if (d->hb) + d->hb->setFixedSize(bw, bh); + if (d->cb) + d->cb->setFixedSize(bw, bh); + + // fiddle the tab chain so the buttons are in their natural order + QWidget * w = d->ok; + + if (d->hb) { + if (w) + setTabOrder(w, d->hb); + w = d->hb; + } + if (d->db) { + if (w) + setTabOrder(w, d->db); + w = d->db; + } + if (d->ab) { + if (w) + setTabOrder(w, d->ab); + w = d->ab; + } + if (d->cb) { + if (w) + setTabOrder(w, d->cb); + w = d->cb; + } + setTabOrder(w, d->tw); +} + +/*!\reimp +*/ +void Q3TabDialog::resizeEvent(QResizeEvent * e) +{ + QDialog::resizeEvent(e); +} + + +/*!\reimp +*/ +void Q3TabDialog::paintEvent(QPaintEvent *) +{ +} + + +/*! + Adds an OK button to the dialog and sets the button's text to \a text. + + When the OK button is clicked, the applyButtonPressed() signal is emitted, + and the current settings in the dialog box should be applied to + the application. The dialog then closes. + + If \a text is an empty string, no button is shown. + + \sa setCancelButton() setDefaultButton() applyButtonPressed() +*/ + +void Q3TabDialog::setOkButton(const QString &text) +{ + if (text.isEmpty()) { + delete d->ok; + d->ok = 0; + setSizes(); + } else { + if (!d->ok) { + d->ok = new QPushButton(this, "ok"); + connect(d->ok, SIGNAL(clicked()), + this, SIGNAL(applyButtonPressed())); + setUpLayout(); + } + d->ok->setText(text); + setSizes(); + //d->ok->show(); + } +} +/*! + \overload + + Adds an OK button to the dialog. The button's text is set to + a localizable "OK". + */ + +void Q3TabDialog::setOkButton() +{ + setOkButton(tr("OK")); +} + + +/* + \overload + Old version of setOkButton(), provided for backward compatibility. +*/ +void Q3TabDialog::setOKButton(const QString &text) +{ + // Ugly workaround for original "OK" default argument + QString newText(text); + if (text.isNull()) + newText = QString::fromLatin1("OK"); + setOkButton(newText); +} + + +/*! Returns the text in the tab for page \a w. +*/ + +QString Q3TabDialog::tabLabel(QWidget * w) +{ + return d->tw->tabLabel(w); +} + + +/*! \reimp +*/ +void Q3TabDialog::styleChange(QStyle& s) +{ + QDialog::styleChange(s); + setSizes(); +} + + +/*! Returns a pointer to the page currently being displayed by the +tab dialog. The tab dialog does its best to make sure that this value +is never 0 (but if you try hard enough, it can be). +*/ + +QWidget * Q3TabDialog::currentPage() const +{ + return d->tw->currentPage(); +} + +/*! + \overload + Defines a new \a label for the tab of page \a w + */ +void Q3TabDialog::changeTab(QWidget *w, const QString &label) +{ + d->tw->changeTab(w, label); +} + +/*! + Changes tab page \a w's iconset to \a iconset and label to \a label. + + */ +void Q3TabDialog::changeTab(QWidget *w, const QIcon& iconset, const QString &label) +{ + d->tw->changeTab(w, iconset, label); +} + +/*! Removes page \a w from this stack of widgets. Does not + delete \a w. + \sa showPage(), QTabWidget::removePage() +*/ +void Q3TabDialog::removePage(QWidget * w) +{ + d->tw->removePage(w); +} + +QT_END_NAMESPACE diff --git a/src/qt3support/dialogs/q3tabdialog.h b/src/qt3support/dialogs/q3tabdialog.h new file mode 100644 index 0000000000..3645df15ab --- /dev/null +++ b/src/qt3support/dialogs/q3tabdialog.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 Q3TABDIALOG_H +#define Q3TABDIALOG_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class QTabBar; +class QTab; +class Q3TabDialogPrivate; + +class Q_COMPAT_EXPORT Q3TabDialog : public QDialog +{ + Q_OBJECT +public: + Q3TabDialog(QWidget* parent=0, const char* name=0, bool modal=false, Qt::WindowFlags f=0); + ~Q3TabDialog(); + + void show(); + void setFont(const QFont & font); + + void addTab(QWidget *, const QString &); + void addTab(QWidget *child, const QIcon& iconset, const QString &label); + + void insertTab(QWidget *, const QString &, int index = -1); + void insertTab(QWidget *child, const QIcon& iconset, const QString &label, int index = -1); + + void changeTab(QWidget *, const QString &); + void changeTab(QWidget *child, const QIcon& iconset, const QString &label); + + bool isTabEnabled( QWidget *) const; + void setTabEnabled(QWidget *, bool); + bool isTabEnabled(const char*) const; // compatibility + void setTabEnabled(const char*, bool); // compatibility + + void showPage(QWidget *); + void removePage(QWidget *); + QString tabLabel(QWidget *); + + QWidget * currentPage() const; + + void setDefaultButton(const QString &text); + void setDefaultButton(); + bool hasDefaultButton() const; + + void setHelpButton(const QString &text); + void setHelpButton(); + bool hasHelpButton() const; + + void setCancelButton(const QString &text); + void setCancelButton(); + bool hasCancelButton() const; + + void setApplyButton(const QString &text); + void setApplyButton(); + bool hasApplyButton() const; + +#ifndef qdoc + void setOKButton(const QString &text = QString()); +#endif + void setOkButton(const QString &text); + void setOkButton(); + bool hasOkButton() const; + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void styleChange(QStyle&); + void setTabBar(QTabBar*); + QTabBar* tabBar() const; + +Q_SIGNALS: + void aboutToShow(); + + void applyButtonPressed(); + void cancelButtonPressed(); + void defaultButtonPressed(); + void helpButtonPressed(); + + void currentChanged(QWidget *); + void selected(const QString&); // obsolete + +private: + void setSizes(); + void setUpLayout(); + + Q3TabDialogPrivate *d; + + Q_DISABLE_COPY(Q3TabDialog) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3TABDIALOG_H diff --git a/src/qt3support/dialogs/q3wizard.cpp b/src/qt3support/dialogs/q3wizard.cpp new file mode 100644 index 0000000000..7a4af4702c --- /dev/null +++ b/src/qt3support/dialogs/q3wizard.cpp @@ -0,0 +1,906 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 "q3wizard.h" + +#include "qlayout.h" +#include "qpushbutton.h" +#include "qcursor.h" +#include "qlabel.h" +#include "qapplication.h" +#include "qlist.h" +#include "qpainter.h" +#include "q3accel.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt; + +/*! + \class Q3Wizard + \compat + \brief The Q3Wizard class provides a framework for wizard dialogs. + + A wizard is a special type of input dialog that consists of a + sequence of dialog pages. A wizard's purpose is to walk the user + through a process step by step. Wizards are useful for complex or + infrequently occurring tasks that people may find difficult to + learn or do. + + Q3Wizard provides page titles and displays Next, Back, Finish, + Cancel, and Help push buttons, as appropriate to the current + position in the page sequence. These buttons can be + enabled/disabled using setBackEnabled(), setNextEnabled(), + setFinishEnabled() and setHelpEnabled(). + + Create and populate dialog pages that inherit from QWidget and add + them to the wizard using addPage(). Use insertPage() to add a + dialog page at a certain position in the page sequence. Use + removePage() to remove a page from the page sequence. + + Use currentPage() to retrieve a pointer to the currently displayed + page. page() returns a pointer to the page at a certain position + in the page sequence. + + Use pageCount() to retrieve the total number of pages in the page + sequence. indexOf() will return the index of a page in the page + sequence. + + Q3Wizard provides functionality to mark pages as appropriate (or + not) in the current context with setAppropriate(). The idea is + that a page may be irrelevant and should be skipped depending on + the data entered by the user on a preceding page. + + It is generally considered good design to provide a greater number + of simple pages with fewer choices rather than a smaller number of + complex pages. +*/ + + +class Q3WizardPrivate +{ +public: + + virtual ~Q3WizardPrivate() + { + foreach(Page *page, pages) + delete page; + } + + struct Page { + Page( QWidget * widget, const QString & title ): + w( widget ), t( title ), + backEnabled( true ), nextEnabled( true ), finishEnabled( false ), + helpEnabled( true ), + appropriate( true ) + {} + QWidget * w; + QString t; + bool backEnabled; + bool nextEnabled; + bool finishEnabled; + bool helpEnabled; + bool appropriate; + }; + + QVBoxLayout * v; + Page * current; + QList pages; + QLabel * title; + QPushButton * backButton; + QPushButton * nextButton; + QPushButton * finishButton; + QPushButton * cancelButton; + QPushButton * helpButton; + + QFrame * hbar1, * hbar2; + +#ifndef QT_NO_ACCEL + Q3Accel * accel; + int backAccel; + int nextAccel; +#endif + + Page * page( const QWidget * w ) + { + if ( !w ) + return 0; + int i = pages.count(); + while( --i >= 0 && pages.at( i ) && pages.at( i )->w != w ) { } + return i >= 0 ? pages.at( i ) : 0; + } + +}; + + +/*! + Constructs an empty wizard dialog. The \a parent, \a name, \a + modal and \a f arguments are passed to the QDialog constructor. +*/ + +Q3Wizard::Q3Wizard(QWidget *parent, const char *name, bool modal, Qt::WindowFlags f ) + : QDialog( parent, name, modal, f ) +{ + d = new Q3WizardPrivate(); + d->current = 0; // not quite true, but... + d->title = new QLabel( this, "title label" ); + + // create in nice tab order + d->nextButton = new QPushButton( this, "next" ); + d->finishButton = new QPushButton( this, "finish" ); + d->helpButton = new QPushButton( this, "help" ); + d->backButton = new QPushButton( this, "back" ); + d->cancelButton = new QPushButton( this, "cancel" ); + + d->v = 0; + d->hbar1 = 0; + d->hbar2 = 0; + + d->cancelButton->setText( tr( "&Cancel" ) ); + d->backButton->setText( tr( "< &Back" ) ); + d->nextButton->setText( tr( "&Next >" ) ); + d->finishButton->setText( tr( "&Finish" ) ); + d->helpButton->setText( tr( "&Help" ) ); + + d->nextButton->setDefault( true ); + + connect( d->backButton, SIGNAL(clicked()), + this, SLOT(back()) ); + connect( d->nextButton, SIGNAL(clicked()), + this, SLOT(next()) ); + connect( d->finishButton, SIGNAL(clicked()), + this, SLOT(accept()) ); + connect( d->cancelButton, SIGNAL(clicked()), + this, SLOT(reject()) ); + connect( d->helpButton, SIGNAL(clicked()), + this, SLOT(help()) ); + +#ifndef QT_NO_ACCEL + d->accel = new Q3Accel( this, "arrow-key accel" ); + d->backAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Left ); + d->accel->connectItem( d->backAccel, this, SLOT(back()) ); + d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right ); + d->accel->connectItem( d->nextAccel, this, SLOT(next()) ); +#endif +} + + +/*! + Destroys the object and frees any allocated resources, including + all pages and controllers. +*/ + +Q3Wizard::~Q3Wizard() +{ + delete d; +} + + +/*! + \internal +*/ + +void Q3Wizard::setVisible(bool show) +{ + if ( show && !d->current ) { + // No page yet + if ( pageCount() > 0 ) + showPage( d->pages.at( 0 )->w ); + else + showPage( 0 ); + } + + QDialog::setVisible(show); +} + + +/*! + \internal +*/ + +void Q3Wizard::setFont( const QFont & font ) +{ + QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) ); + QDialog::setFont( font ); +} + +/*! + Adds \a page to the end of the page sequence, with the title, \a + title. +*/ + +void Q3Wizard::addPage( QWidget * page, const QString & title ) +{ + if ( !page ) + return; + if ( d->page( page ) ) { +#if defined(QT_CHECK_STATE) + qWarning( "Q3Wizard::addPage(): already added %s/%s to %s/%s", + page->className(), page->name(), + className(), name() ); +#endif + return; + } + int i = d->pages.count(); + + if( i > 0 ) + d->pages.at( i - 1 )->nextEnabled = true; + + Q3WizardPrivate::Page * p = new Q3WizardPrivate::Page( page, title ); + p->backEnabled = ( i > 0 ); + d->pages.append( p ); +} + +/*! + Inserts \a page at position \a index into the page sequence, with + title \a title. If \a index is -1, the page will be appended to + the end of the wizard's page sequence. +*/ + +void Q3Wizard::insertPage( QWidget * page, const QString & title, int index ) +{ + if ( !page ) + return; + if ( d->page( page ) ) { +#if defined(QT_CHECK_STATE) + qWarning( "Q3Wizard::insertPage(): already added %s/%s to %s/%s", + page->className(), page->name(), + className(), name() ); +#endif + return; + } + + if ( index < 0 || index > (int)d->pages.count() ) + index = d->pages.count(); + + if( index > 0 && ( index == (int)d->pages.count() ) ) + d->pages.at( index - 1 )->nextEnabled = true; + + Q3WizardPrivate::Page * p = new Q3WizardPrivate::Page( page, title ); + p->backEnabled = ( index > 0 ); + p->nextEnabled = ( index < (int)d->pages.count() ); + + d->pages.insert( index, p ); +} + +/*! + \fn void Q3Wizard::selected(const QString &title) + + This signal is emitted when the current page changes. The + \a title parameter contains the title of the selected page. +*/ + + +/*! + Makes \a page the current page and emits the selected() signal. + + This virtual function is called whenever a different page is to + be shown, including the first time the Q3Wizard is shown. + By reimplementing it (and calling Q3Wizard::showPage()), + you can prepare each page prior to it being shown. +*/ + +void Q3Wizard::showPage( QWidget * page ) +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( p ) { + int i; + for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ ) {} + bool notFirst( false ); + + if( i ) { + i--; + while( ( i >= 0 ) && !notFirst ) { + notFirst |= appropriate( d->pages.at( i )->w ); + i--; + } + } + setBackEnabled( notFirst ); + setNextEnabled( true ); + + page->show(); + foreach(Q3WizardPrivate::Page *ppage, d->pages) { + if (ppage->w != page) + ppage->w->hide(); + } + + d->current = p; + } + + layOut(); + updateButtons(); + emit selected( p ? p->t : QString() ); +} + + +/*! + Returns the number of pages in the wizard. +*/ + +int Q3Wizard::pageCount() const +{ + return d->pages.count(); +} + +/*! + Returns the position of page \a page. If the page is not part of + the wizard -1 is returned. +*/ + +int Q3Wizard::indexOf( QWidget* page ) const +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( !p ) return -1; + + return d->pages.indexOf( p ); +} + +/*! + Called when the user clicks the Back button; this function shows + the preceding relevant page in the sequence. + + \sa appropriate() +*/ +void Q3Wizard::back() +{ + int i = 0; + + while( i < (int)d->pages.count() && d->pages.at( i ) && + d->current && d->pages.at( i )->w != d->current->w ) + i++; + + i--; + while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) ) + i--; + + if( i >= 0 ) + if( d->pages.at( i ) ) + showPage( d->pages.at( i )->w ); +} + + +/*! + Called when the user clicks the Next button, this function shows + the next relevant page in the sequence. + + \sa appropriate() +*/ +void Q3Wizard::next() +{ + int i = 0; + while( i < (int)d->pages.count() && d->pages.at( i ) && + d->current && d->pages.at( i )->w != d->current->w ) + i++; + i++; + while( i <= (int)d->pages.count()-1 && + ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) ) + i++; + // if we fell of the end of the world, step back + while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) ) + i--; + if ( d->pages.at( i ) ) + showPage( d->pages.at( i )->w ); +} + + +/*! + \fn void Q3Wizard::helpClicked() + + This signal is emitted when the user clicks on the Help button. +*/ + +/*! + Called when the user clicks the Help button, this function emits + the helpClicked() signal. +*/ + +void Q3Wizard::help() +{ + QWidget *page = d->current ? d->current->w : 0; + if ( !page ) + return; + +#if 0 + Q3WizardPage *wpage = qobject_cast(page); + if ( wpage ) + emit wpage->helpClicked(); +#endif + emit helpClicked(); +} + + +void Q3Wizard::setBackEnabled( bool enable ) +{ + d->backButton->setEnabled( enable ); +#ifndef QT_NO_ACCEL + d->accel->setItemEnabled( d->backAccel, enable ); +#endif +} + + +void Q3Wizard::setNextEnabled( bool enable ) +{ + d->nextButton->setEnabled( enable ); +#ifndef QT_NO_ACCEL + d->accel->setItemEnabled( d->nextAccel, enable ); +#endif +} + + +void Q3Wizard::setHelpEnabled( bool enable ) +{ + d->helpButton->setEnabled( enable ); +} + + +/*! + \fn void Q3Wizard::setFinish(QWidget *widget, bool finish) + \obsolete + + Use setFinishEnabled() instead. +*/ + +/*! + If \a enable is true, page \a page has a Back button; otherwise \a + page has no Back button. By default all pages have this button. +*/ +void Q3Wizard::setBackEnabled( QWidget * page, bool enable ) +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( !p ) + return; + + p->backEnabled = enable; + updateButtons(); +} + + +/*! + If \a enable is true, page \a page has a Next button; otherwise + the Next button on \a page is disabled. By default all pages have + this button. +*/ + +void Q3Wizard::setNextEnabled( QWidget * page, bool enable ) +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( !p ) + return; + + p->nextEnabled = enable; + updateButtons(); +} + + +/*! + If \a enable is true, page \a page has a Finish button; otherwise + \a page has no Finish button. By default \e no page has this + button. +*/ +void Q3Wizard::setFinishEnabled( QWidget * page, bool enable ) +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( !p ) + return; + + p->finishEnabled = enable; + updateButtons(); +} + + +/*! + If \a enable is true, page \a page has a Help button; otherwise \a + page has no Help button. By default all pages have this button. +*/ +void Q3Wizard::setHelpEnabled( QWidget * page, bool enable ) +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( !p ) + return; + + p->helpEnabled = enable; + updateButtons(); +} + + +/*! + Called when the Next button is clicked; this virtual function + returns true if \a page is relevant for display in the current + context; otherwise it is ignored by Q3Wizard and returns false. The + default implementation returns the value set using + setAppropriate(). The ultimate default is true. + + \warning The last page of the wizard will be displayed if no page + is relevant in the current context. +*/ + +bool Q3Wizard::appropriate( QWidget * page ) const +{ + Q3WizardPrivate::Page * p = d->page( page ); + return p ? p->appropriate : true; +} + + +/*! + If \a appropriate is true then page \a page is considered relevant + in the current context and should be displayed in the page + sequence; otherwise \a page should not be displayed in the page + sequence. + + \sa appropriate() +*/ +void Q3Wizard::setAppropriate( QWidget * page, bool appropriate ) +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( p ) + p->appropriate = appropriate; +} + + +void Q3Wizard::updateButtons() +{ + if ( !d->current ) + return; + + int i; + for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ ) {} + bool notFirst( false ); + if( i ) { + i--; + while( ( i >= 0 ) && !notFirst ) { + notFirst |= appropriate( d->pages.at( i )->w ); + i--; + } + } + setBackEnabled( d->current->backEnabled && notFirst ); + setNextEnabled( d->current->nextEnabled ); + d->finishButton->setEnabled( d->current->finishEnabled ); + d->helpButton->setEnabled( d->current->helpEnabled ); + + if ( ( d->current->finishEnabled && !d->finishButton->isVisible() ) || + ( d->current->backEnabled && !d->backButton->isVisible() ) || + ( d->current->nextEnabled && !d->nextButton->isVisible() ) || + ( d->current->helpEnabled && !d->helpButton->isVisible() ) ) + layOut(); +} + + +/*! + Returns a pointer to the current page in the sequence. Although + the wizard does its best to make sure that this value is never 0, + it can be if you try hard enough. +*/ + +QWidget * Q3Wizard::currentPage() const +{ + return d->current ? d->current->w : 0; +} + + +/*! + Returns the title of page \a page. +*/ + +QString Q3Wizard::title( QWidget * page ) const +{ + Q3WizardPrivate::Page * p = d->page( page ); + return p ? p->t : QString(); +} + +/*! + Sets the title for page \a page to \a title. +*/ + +void Q3Wizard::setTitle( QWidget *page, const QString &title ) +{ + Q3WizardPrivate::Page * p = d->page( page ); + if ( p ) + p->t = title; + if ( page == currentPage() ) + d->title->setText( title ); +} + +/*! + \property Q3Wizard::titleFont + \brief the font used for page titles + + The default is QApplication::font(). +*/ +QFont Q3Wizard::titleFont() const +{ + return d->title->font(); +} + +void Q3Wizard::setTitleFont( const QFont & font ) +{ + d->title->setFont( font ); +} + + +/*! + Returns a pointer to the dialog's Back button + + By default, this button is connected to the back() slot, which is + virtual so you can reimplement it in a Q3Wizard subclass. Use + setBackEnabled() to enable/disable this button. +*/ +QPushButton * Q3Wizard::backButton() const +{ + return d->backButton; +} + + +/*! + Returns a pointer to the dialog's Next button + + By default, this button is connected to the next() slot, which is + virtual so you can reimplement it in a Q3Wizard subclass. Use + setNextEnabled() to enable/disable this button. +*/ +QPushButton * Q3Wizard::nextButton() const +{ + return d->nextButton; +} + + +/*! + Returns a pointer to the dialog's Finish button + + By default, this button is connected to the QDialog::accept() + slot, which is virtual so you can reimplement it in a Q3Wizard + subclass. Use setFinishEnabled() to enable/disable this button. +*/ +QPushButton * Q3Wizard::finishButton() const +{ + return d->finishButton; +} + + +/*! + Returns a pointer to the dialog's Cancel button + + By default, this button is connected to the QDialog::reject() + slot, which is virtual so you can reimplement it in a Q3Wizard + subclass. +*/ +QPushButton * Q3Wizard::cancelButton() const +{ + return d->cancelButton; +} + + +/*! + Returns a pointer to the dialog's Help button + + By default, this button is connected to the help() slot, which is + virtual so you can reimplement it in a Q3Wizard subclass. Use + setHelpEnabled() to enable/disable this button. +*/ +QPushButton * Q3Wizard::helpButton() const +{ + return d->helpButton; +} + + +/*! + This virtual function is responsible for adding the buttons below + the bottom divider. + + \a layout is the horizontal layout of the entire wizard. +*/ + +void Q3Wizard::layOutButtonRow( QHBoxLayout * layout ) +{ + bool hasHelp = false; + bool hasEarlyFinish = false; + + int i = d->pages.count() - 2; + while ( !hasEarlyFinish && i >= 0 ) { + if ( d->pages.at( i ) && d->pages.at( i )->finishEnabled ) + hasEarlyFinish = true; + i--; + } + i = 0; + while ( !hasHelp && i < (int)d->pages.count() ) { + if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled ) + hasHelp = true; + i++; + } + + QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight ); + layout->addLayout( h ); + + if ( hasHelp ) + h->addWidget( d->helpButton ); + else + d->helpButton->hide(); + + h->addStretch( 42 ); + + h->addWidget( d->backButton ); + + h->addSpacing( 6 ); + + if (d->current == d->pages.at( d->pages.count()-1 )) + hasEarlyFinish = false; + + if ( hasEarlyFinish ) { + d->nextButton->show(); + d->finishButton->show(); + h->addWidget( d->nextButton ); + h->addSpacing( 12 ); + h->addWidget( d->finishButton ); + } else if ( d->pages.count() == 0 || + (d->current && d->current->finishEnabled) || + d->current == d->pages.at( d->pages.count()-1 ) ) { + d->nextButton->hide(); + d->finishButton->show(); + h->addWidget( d->finishButton ); + } else { + d->nextButton->show(); + d->finishButton->hide(); + h->addWidget( d->nextButton ); + } + + // if last page is disabled - show finished btn. at lastpage-1 + i = d->pages.count()-1; + if ( i >= 0 && !appropriate( d->pages.at( i )->w ) && + d->current == d->pages.at( d->pages.count()-2 ) ) { + d->nextButton->hide(); + d->finishButton->show(); + h->addWidget( d->finishButton ); + } + + h->addSpacing( 12 ); + h->addWidget( d->cancelButton ); +} + + +/*! + This virtual function is responsible for laying out the title row. + + \a layout is the horizontal layout for the wizard, and \a + title is the title for this page. This function is called every + time \a title changes. +*/ + +void Q3Wizard::layOutTitleRow( QHBoxLayout * layout, const QString & title ) +{ + d->title->setText( title ); + layout->addWidget( d->title, 10 ); +} + + +/* + +*/ + +void Q3Wizard::layOut() +{ + delete d->v; + d->v = new QVBoxLayout( this, 6, 0, "top-level layout" ); + + QHBoxLayout * l; + l = new QHBoxLayout( 6 ); + d->v->addLayout( l, 0 ); + layOutTitleRow( l, d->current ? d->current->t : QString() ); + + if ( ! d->hbar1 ) { + d->hbar1 = new QFrame( this, "
", 0 ); + d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine ); + d->hbar1->setFixedHeight( 12 ); + } + + d->v->addWidget( d->hbar1 ); + + if (d->current) + d->v->addWidget(d->current->w, 10); + + if ( ! d->hbar2 ) { + d->hbar2 = new QFrame( this, "
", 0 ); + d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine ); + d->hbar2->setFixedHeight( 12 ); + } + d->v->addWidget( d->hbar2 ); + + l = new QHBoxLayout( 6 ); + d->v->addLayout( l ); + layOutButtonRow( l ); + d->v->activate(); +} + + +/*! + \reimp +*/ + +bool Q3Wizard::eventFilter( QObject * o, QEvent * e ) +{ + return QDialog::eventFilter( o, e ); +} + + +/*! + Removes \a page from the page sequence but does not delete the + page. If \a page is currently being displayed, Q3Wizard will + display the page that precedes it, or the first page if this was + the first page. +*/ + +void Q3Wizard::removePage( QWidget * page ) +{ + if ( !page ) + return; + + int i = d->pages.count(); + QWidget* cp = currentPage(); + while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { } + if ( i < 0 ) + return; + delete d->pages.takeAt(i); + page->hide(); + + if( cp == page ) { + i--; + if( i < 0 ) + i = 0; + if ( pageCount() > 0 ) + showPage( Q3Wizard::page( i ) ); + } else if (pageCount() > 0) { + showPage(cp); + } +} + + +/*! + Returns a pointer to the page at position \a index in the + sequence, or 0 if \a index is out of range. The first page has + index 0. +*/ + +QWidget* Q3Wizard::page( int index ) const +{ + if ( index >= pageCount() || index < 0 ) + return 0; + return d->pages.at( index )->w; +} + +QT_END_NAMESPACE diff --git a/src/qt3support/dialogs/q3wizard.h b/src/qt3support/dialogs/q3wizard.h new file mode 100644 index 0000000000..2d74a7ec1c --- /dev/null +++ b/src/qt3support/dialogs/q3wizard.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support 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 Q3WIZARD_H +#define Q3WIZARD_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class QHBoxLayout; +class Q3WizardPrivate; + +class Q_COMPAT_EXPORT Q3Wizard : public QDialog +{ + Q_OBJECT + Q_PROPERTY( QFont titleFont READ titleFont WRITE setTitleFont ) + +public: + Q3Wizard( QWidget* parent=0, const char* name=0, bool modal=false, Qt::WindowFlags f=0 ); + ~Q3Wizard(); + + void setVisible(bool); + + void setFont( const QFont & font ); + + virtual void addPage( QWidget *, const QString & ); + virtual void insertPage( QWidget*, const QString&, int ); + virtual void removePage( QWidget * ); + + QString title( QWidget * ) const; + void setTitle( QWidget *, const QString & ); + QFont titleFont() const; + void setTitleFont( const QFont & ); + + virtual void showPage( QWidget * ); + + QWidget * currentPage() const; + + QWidget* page( int ) const; + int pageCount() const; + int indexOf( QWidget* ) const; + + virtual bool appropriate( QWidget * ) const; + virtual void setAppropriate( QWidget *, bool ); + + QPushButton * backButton() const; + QPushButton * nextButton() const; + QPushButton * finishButton() const; + QPushButton * cancelButton() const; + QPushButton * helpButton() const; + + bool eventFilter( QObject *, QEvent * ); + +public Q_SLOTS: + virtual void setBackEnabled( QWidget *, bool ); + virtual void setNextEnabled( QWidget *, bool ); + virtual void setFinishEnabled( QWidget *, bool ); + + virtual void setHelpEnabled( QWidget *, bool ); + + // obsolete + virtual void setFinish( QWidget *, bool ) {} + +protected Q_SLOTS: + virtual void back(); + virtual void next(); + virtual void help(); + +Q_SIGNALS: + void helpClicked(); + void selected( const QString& ); + +protected: + virtual void layOutButtonRow( QHBoxLayout * ); + virtual void layOutTitleRow( QHBoxLayout *, const QString & ); + +private: + void setBackEnabled( bool ); + void setNextEnabled( bool ); + + void setHelpEnabled( bool ); + + void setNextPage( QWidget * ); + + void updateButtons(); + + void layOut(); + + Q3WizardPrivate *d; + + Q_DISABLE_COPY(Q3Wizard) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3WIZARD_H -- cgit v1.2.3