/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
** this package.
**
** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@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 <shlobj.h>

#ifdef Q_OS_WINCE
#include <commdlg.h>
#endif

QT_BEGIN_NAMESPACE

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];
}

static QString tFilters, tTitle, tInitDir;

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;
    wchar_t *tInitSel = new wchar_t[maxLen+1];
    if (initSel.length() > 0 && initSel.length() <= maxLen)
        memcpy(tInitSel, initSel.utf16(), (initSel.length() + 1) * sizeof(wchar_t));
    else
        tInitSel[0] = 0;

    OPENFILENAME* ofn = new OPENFILENAME;
    memset(ofn, 0, sizeof(OPENFILENAME));

    ofn->lStructSize     = sizeof(OPENFILENAME);
    ofn->hwndOwner       = parent ? parent->winId() : 0;
    ofn->lpstrFilter     = (wchar_t*)tFilters.utf16();
    ofn->lpstrFile       = tInitSel;
    ofn->nMaxFile        = maxLen;
    ofn->lpstrInitialDir = (wchar_t*)tInitDir.utf16();
    ofn->lpstrTitle      = (wchar_t*)tTitle.utf16();
    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;
}

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);
    }

    OPENFILENAME* ofn = makeOFN(parent, isel,
                                *initialDirectory, title,
                                winFilter(filter), ExistingFile);
    if (idx)
        ofn->nFilterIndex = idx + 1;
    if (GetOpenFileName(ofn)) {
        result = QString::fromWCharArray(ofn->lpstrFile);
        selFilIdx = ofn->nFilterIndex;
    }
    cleanUpOFN(&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);
    }

    OPENFILENAME* ofn = makeOFN(parent, isel,
                                 *initialDirectory, title,
                                 winFilter(filter), AnyFile);
    if (idx)
        ofn->nFilterIndex = idx + 1;
    if (GetSaveFileName(ofn)) {
        result = QString::fromWCharArray(ofn->lpstrFile);
        selFilIdx = ofn->nFilterIndex;
    }
    cleanUpOFN(&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);
    }

    OPENFILENAME* ofn = makeOFN(parent, isel,
                                 *initialDirectory, title,
                                 winFilter(filter), ExistingFiles);
    if (idx)
        ofn->nFilterIndex = idx + 1;
    if (GetOpenFileName(ofn)) {
        QString fileOrDir = QString::fromWCharArray(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::fromWCharArray(ofn->lpstrFile + offset)).isEmpty()) {
                fi.setFile(dir, f);
                QString res = fi.absFilePath();
                if (!res.isEmpty())
                    result.append(res);
                offset += f.length() + 1;
            }
        }
    }
    cleanUpOFN(&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()) {
            SendMessage(hwnd, BFFM_SETSELECTION, TRUE, Q_ULONG(initDir->utf16()));
        }
    } else if (uMsg == BFFM_SELCHANGED) {
        wchar_t path[MAX_PATH];
        SHGetPathFromIDList(LPITEMIDLIST(lParam), path);
        QString tmpStr = QString::fromWCharArray(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));
    }
#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);
    }

    QString initDir = QDir::toNativeSeparators(initialDirectory);
    wchar_t path[MAX_PATH];
    wchar_t initPath[MAX_PATH];
    initPath[0] = 0;
    path[0] = 0;
    tTitle = title;
    BROWSEINFO bi;
    bi.hwndOwner = (parent ? parent->winId() : 0);
    bi.pidlRoot = NULL;
    bi.lpszTitle = (wchar_t*)tTitle.utf16();
    bi.pszDisplayName = initPath;
    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
    bi.lpfn = winGetExistDirCallbackProc;
    bi.lParam = Q_ULONG(&initDir);
    LPITEMIDLIST pItemIDList = SHBrowseForFolder(&bi);
    if (pItemIDList) {
        SHGetPathFromIDList(pItemIDList, path);
        IMalloc *pMalloc;
        if (SHGetMalloc(&pMalloc) != NOERROR)
            result.clear();
        else {
            pMalloc->Free(pItemIDList);
            pMalloc->Release();
            result = QString::fromWCharArray(path);
        }
    } else
        result.clear();
    tTitle.clear();

    if (parent) {
        QApplicationPrivate::leaveModal(parent);
        QEvent e(QEvent::WindowUnblocked);
        QApplication::sendEvent(parent, &e);
    }

    if (!result.isEmpty())
        result.replace(QLatin1Char('\\'), QLatin1Char('/'));
    return result;
#else
    return QString();
#endif
}

QT_END_NAMESPACE

#endif
