/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

/**
 * Copyright (C) 2023-2026 Linaro Limited (or its affiliates). All rights reserved.
 * Copyright (C) 2012-2023 Arm Limited (or its affiliates).
 * All rights reserved.
 */

#include <QtGlobal>
#include <QComboBox>
#include <QApplication>
#include <QDateTime>
#include <QDir>
#include <QUrl>
#include <QFileIconProvider>
#include <QListView>
#include <QSettings>
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>
#include <QPixmapCache>
#include "remotefiledialog_p.h"
#include "remotefilesystemmodel.h"
#ifdef Q_OS_WIN
#include <qt_windows.h>
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
#include <QtWin>
#define HICON2PIXMAP(hIcon) QtWin::fromHICON(hIcon)
#else
#define HICON2PIXMAP(hIcon) QPixmap::fromImage(QImage::fromHICON(hIcon))
#endif
#endif
//#define DEBUG_REMOTEFILEDIALOG
#ifdef DEBUG_REMOTEFILEDIALOG
#include <qdebug.h>
#endif

namespace {
    QString g_defaultWorkingDirectory;
    QString g_remoteHomeDirectory;
    QString g_remotePrefix(QLatin1String("remote:"));
    RemoteFileDialog::Hook g_hook;
    RemoteFileDialog *g_instance = 0;
    const qint32 RemoteFileDialogMagic = 0xbf;
    QReadWriteLock g_lock;

#ifdef Q_OS_WIN
    // Use our own icon provider that relies only on the extension.
	class WinIconProvider : public QFileIconProvider
	{
	public:
            QIcon icon(const QFileInfo &fileInfo) const
            {
                return getWinIcon(fileInfo);
            }
	private:
	    QIcon getWinIcon(const QFileInfo &fileInfo) const
		{
    		QIcon retIcon;
		    const QString fileExtension = QLatin1Char('.') + fileInfo.suffix().toUpper();
		    if (fileExtension.length() == 1 ||
		        fileInfo.isDir()) // no suffix, no generic icon
		        return retIcon;

    		QString key;
		    if (fileInfo.isFile() && !fileInfo.isExecutable() && !fileInfo.isSymLink())
        		key = QLatin1String("qt_") + fileExtension;

    		QPixmap pixmap;
		    if (!key.isEmpty()) {
        		QPixmapCache::find(key, &pixmap);
    		}

	    	if (!pixmap.isNull()) {
        		retIcon.addPixmap(pixmap);
	        	if (QPixmapCache::find(key + QLatin1Char('l'), &pixmap))
            		retIcon.addPixmap(pixmap);
        		return retIcon;
    		}

    		/* We don't use the variable, but by storing it statically, we
	     	* ensure CoInitialize is only called once. */
    		static HRESULT comInit = CoInitialize(NULL);
		    Q_UNUSED(comInit);

    		SHFILEINFO info;
		    unsigned long val = 0;

    		//Get the small icon
    		info.dwAttributes = 0;
    		if (fileInfo.isDir())
    		    info.dwAttributes |= FILE_ATTRIBUTE_DIRECTORY;
    		if (!fileInfo.isWritable())
    		    info.dwAttributes |= FILE_ATTRIBUTE_READONLY;
    		if (fileInfo.isHidden())
    		    info.dwAttributes |= FILE_ATTRIBUTE_HIDDEN;
    		if (!info.dwAttributes)
    		    info.dwAttributes = FILE_ATTRIBUTE_NORMAL;
#ifndef Q_OS_WINCE
    		val = SHGetFileInfo((const wchar_t *)fileExtension.utf16(), 0, &info,
                        		sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_SMALLICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);
#else
    		val = SHGetFileInfo((const wchar_t *)fileExtension.utf16(), 0, &info,
                        		sizeof(SHFILEINFO), SHGFI_SMALLICON|SHGFI_SYSICONINDEX);
#endif

    		// Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
		    if (val && info.hIcon) {
		        if (fileInfo.isDir() && !fileInfo.isRoot()) {
		            //using the unique icon index provided by windows save us from duplicate keys
		            key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
		            QPixmapCache::find(key, &pixmap);
		            if (!pixmap.isNull()) {
		                retIcon.addPixmap(pixmap);
		                if (QPixmapCache::find(key + QLatin1Char('l'), &pixmap))
		                    retIcon.addPixmap(pixmap);
		                DestroyIcon(info.hIcon);
		                return retIcon;
		            }
		        }
		        if (pixmap.isNull()) {
#ifndef Q_OS_WINCE
		            pixmap = HICON2PIXMAP(info.hIcon);
#else
		            pixmap = HICON2PIXMAP(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL));
#endif
		            if (!pixmap.isNull()) {
		                retIcon.addPixmap(pixmap);
		                if (!key.isEmpty())
		                    QPixmapCache::insert(key, pixmap);
		            }
		            else {
		              qWarning("QFileIconProviderPrivate::getWinIcon() no small icon found");
		            }
		        }
		        DestroyIcon(info.hIcon);
		    }

		    //Get the big icon
#ifndef Q_OS_WINCE
		    val = SHGetFileInfo((const wchar_t *)fileExtension.utf16(), 0, &info,
		                        sizeof(SHFILEINFO), SHGFI_ICON|SHGFI_LARGEICON|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX|SHGFI_USEFILEATTRIBUTES);
#else
		    val = SHGetFileInfo((const wchar_t *)fileExtension.utf16(), 0, &info,
		                        sizeof(SHFILEINFO), SHGFI_LARGEICON|SHGFI_SYSICONINDEX);
#endif
		    if (val && info.hIcon) {
		        if (fileInfo.isDir() && !fileInfo.isRoot()) {
		            //using the unique icon index provided by windows save us from duplicate keys
		            key = QString::fromLatin1("qt_dir_%1").arg(info.iIcon);
		        }
#ifndef Q_OS_WINCE
		        pixmap = HICON2PIXMAP(info.hIcon);
#else
		        pixmap = HICON2PIXMAP(ImageList_GetIcon((HIMAGELIST) val, info.iIcon, ILD_NORMAL));
#endif
		        if (!pixmap.isNull()) {
		            retIcon.addPixmap(pixmap);
		            if (!key.isEmpty())
		                QPixmapCache::insert(key + QLatin1Char('l'), pixmap);
		        }
		        else {
		            qWarning("QFileIconProviderPrivate::getWinIcon() no large icon found");
		        }
		        DestroyIcon(info.hIcon);
		    }
		    return retIcon;
		}
	};
#endif
}

RemoteFileDialog::RemoteFileDialog(QWidget * parent, Qt::WindowFlags flags) :
    RemoteFileDialogBase(parent, flags),
    mBusy(false)
{
    init();
    setLocation(Remote);
}

RemoteFileDialog::RemoteFileDialog(QWidget * parent, const QString & caption, const QString & directory, const QString & filter, FileSystemLocation location) :
    RemoteFileDialogBase(parent, caption, QString{}, filter),
    mBusy(false)
{
    init();
    setLocation(location);
    if (!directory.isEmpty())
        RemoteFileDialogBase::setDirectory(mungePath(directory));
}

RemoteFileDialog::~RemoteFileDialog()
{
    g_instance = 0;
    emit aboutToBeDestroyed();
}

RemoteFileDialog *RemoteFileDialog::instance()
{
    return g_instance;
}

void RemoteFileDialog::init()
{
    g_instance = this;
    setProxyModel(new RemoteProxyModel(this));
    mLookInComboBox = findChild<QComboBox *>();
    Q_ASSERT(mLookInComboBox != 0);
    connect(mLookInComboBox, qOverload<int>(&QComboBox::activated), this, &RemoteFileDialog::delayedCheckDirectory);
    connect(mLookInComboBox, &QComboBox::editTextChanged, this, &RemoteFileDialog::delayedCheckDirectory);
    connect(mLookInComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &RemoteFileDialog::delayedCheckDirectory);
#ifdef Q_OS_WIN
    setIconProvider(new WinIconProvider());
#endif
    if (g_hook)
        g_hook(this);
}

bool RemoteFileDialog::isLocalPath(const QString& path) const
{
    bool isLocal = !path.contains(QLatin1Char(':'));
#if defined (Q_OS_WIN)
    if (!isLocal)
    {
        const QFileInfoList drives = QDir(path).drives();
        const int driveCount = drives.count();
        for (int i = 0;!isLocal && (i < driveCount);++i)
        {
            isLocal = path.startsWith(drives[i].path());
        }
    }
#endif
    return isLocal;
}

void RemoteFileDialog::setLocation(FileSystemLocation location)
{
    mLocation = location;
    RemoteProxyModel *model = static_cast<RemoteProxyModel *>(proxyModel());
    model->setLocation(mLocation);
    if (mLocation == LocalRemote)
        return;

#ifdef DEBUG_REMOTEFILEDIALOG
    if( mLocation == Local)
        qDebug() << "---- RemoteFileDialog::setLocation Local ----";
    else if( mLocation == Remote)
        qDebug() << "---- RemoteFileDialog::setLocation Remote ----";

    qDebug() << "history paths:";
#endif

    QStringList paths(RemoteFileDialogBase::history());
    for (QStringList::iterator it = paths.begin(); it != paths.end();) {
        QString& path(*it);

#ifdef DEBUG_REMOTEFILEDIALOG
        qDebug() << "dir: " << path;
#endif

        switch (mLocation) {
        case Local:
            if (path.startsWith(g_remotePrefix))
                it = paths.erase(it);
            else
                ++it;
            break;
        case Remote:
            if (!path.startsWith(g_remotePrefix))
                it = paths.erase(it);
            else
                ++it;
            break;
        default:
            break;
        }
    }

#ifdef DEBUG_REMOTEFILEDIALOG
    qDebug() << "after filter:";

    for (QStringList::iterator it = paths.begin(); it != paths.end();)
    {
        QString& path(*it);
        qDebug() << "dir: " << path;
        ++it;
    }

    qDebug() << "---------------------------------------";
#endif

    RemoteFileDialogBase::setHistory(paths);
    bool foundDefaultWorkingDirectory = false;
    bool foundRemoteHomeDirectory = false;
    QList<QUrl> sidebarUrls(RemoteFileDialogBase::sidebarUrls());

#ifdef DEBUG_REMOTEFILEDIALOG
    qDebug() << "sidebar urls:";

    for (QList<QUrl>::iterator it = sidebarUrls.begin(); it != sidebarUrls.end();)
    {
        QUrl& path(*it);
        qDebug() << "dir: " << path;
        ++it;
    }

    qDebug() << "---------------------------------------";
#endif

    for (QList<QUrl>::iterator it = sidebarUrls.begin(); it != sidebarUrls.end();) {
        QUrl& url(*it);
        QString localFile(url.toLocalFile());
        switch (mLocation)
        {
            case Local:
                if (!isLocalPath(localFile))
                    it = sidebarUrls.erase(it);
                else
                    ++it;
                break;
            case Remote:
                if (isLocalPath(localFile))
                {
                    it = sidebarUrls.erase(it);
                }
                else
                {
                    if (!localFile.startsWith(g_remotePrefix))
                        url = QUrl::fromLocalFile(g_remotePrefix + localFile.section(QLatin1Char(':'), 1));
                    ++it;
                }
                break;
            default:
                break;
        }
        if (localFile == g_defaultWorkingDirectory)
            foundDefaultWorkingDirectory = true;
        if (localFile == g_remoteHomeDirectory)
            foundRemoteHomeDirectory = true;
    }
    if ((mLocation != Local || !g_defaultWorkingDirectory.startsWith(g_remotePrefix)) &&
        (mLocation != Remote || g_defaultWorkingDirectory.startsWith(g_remotePrefix)) &&
        g_remoteHomeDirectory != g_defaultWorkingDirectory &&
        !foundDefaultWorkingDirectory && !g_defaultWorkingDirectory.isEmpty())
        sidebarUrls.append(QUrl::fromLocalFile(g_defaultWorkingDirectory));
    if (mLocation == Remote &&
        !foundRemoteHomeDirectory && !g_remoteHomeDirectory.isEmpty())
        sidebarUrls.append(QUrl::fromLocalFile(g_remoteHomeDirectory));

    RemoteFileDialogBase::setSidebarUrls(sidebarUrls);

    QDir dir(RemoteFileDialogBase::directory());
    switch (mLocation) {
    case Local:
        if (!isLocalPath(dir.path())) {
            if (!g_defaultWorkingDirectory.isEmpty() &&
                !g_defaultWorkingDirectory.startsWith(g_remotePrefix))
                RemoteFileDialogBase::setDirectory(g_defaultWorkingDirectory);
            else
                RemoteFileDialogBase::setDirectory(QDir::homePath());
        }
        break;
    case Remote:
        if (isLocalPath(dir.path()))
        {
            if (!g_defaultWorkingDirectory.isEmpty() &&
                g_defaultWorkingDirectory.startsWith(g_remotePrefix))
                RemoteFileDialogBase::setDirectory(g_defaultWorkingDirectory);
            else
                RemoteFileDialogBase::setDirectory(g_remotePrefix);
        }
        else if (!dir.path().startsWith(g_remotePrefix))
        {
            dir.setPath(g_remotePrefix + dir.path().section(QLatin1Char(':'), 1));
        }
        break;
    default:
        break;
    }
}

RemoteFileDialog::FileSystemLocation RemoteFileDialog::location() const
{
    return mLocation;
}

QStringList RemoteFileDialog::selectedFiles() const
{
    return unmungePaths(RemoteFileDialogBase::selectedFiles());
}

QString RemoteFileDialog::selectedFile() const
{
    return unmungePath(RemoteFileDialogBase::selectedFiles().first());
}

QString RemoteFileDialog::getOpenFileName(QWidget *parent,
                                          const QString &caption,
                                          const QString &dir,
                                          const QString &filter,
                                          QString *selectedFilter,
                                          QFileDialog::Options options,
                                          FileSystemLocation location)
{
#ifdef DEBUG_REMOTEFILEDIALOG
    qDebug() << "RemoteFileDialog::getOpenFileName dir: " << dir;
#endif

    RemoteFileDialog dialog(parent, caption, dir, filter, location);
    dialog.setFileMode(QFileDialog::ExistingFile);
    dialog.setOptions(options);
    if (selectedFilter)
        dialog.selectNameFilter(*selectedFilter);
    if (dialog.exec() == QDialog::Accepted) {
        if (selectedFilter)
            *selectedFilter = dialog.selectedNameFilter();
        return dialog.selectedFiles().value(0);
    }
    return QString();
}

QStringList RemoteFileDialog::getOpenFileNames(QWidget *parent,
                                               const QString &caption,
                                               const QString &dir,
                                               const QString &filter,
                                               QString *selectedFilter,
                                               QFileDialog::Options options,
                                               FileSystemLocation location)
{
    RemoteFileDialog dialog(parent, caption, dir, filter, location);
    dialog.setFileMode(QFileDialog::ExistingFiles);
    dialog.setOptions(options);
    if (selectedFilter)
        dialog.selectNameFilter(*selectedFilter);
    if (dialog.exec() == QDialog::Accepted) {
        if (selectedFilter)
            *selectedFilter = dialog.selectedNameFilter();
        return dialog.selectedFiles();
    }
    return QStringList();
}

QString RemoteFileDialog::getSaveFileName(QWidget *parent,
                                          const QString &caption,
                                          const QString &dir,
                                          const QString &filter,
                                          QString *selectedFilter,
                                          QFileDialog::Options options,
                                          FileSystemLocation location)
{
    RemoteFileDialog dialog(parent, caption, dir, filter, location);
    dialog.setFileMode(QFileDialog::AnyFile);
    dialog.setOptions(options);
    dialog.setAcceptMode(QFileDialog::AcceptSave);
    if (selectedFilter)
        dialog.selectNameFilter(*selectedFilter);
    if (dialog.exec() == QDialog::Accepted) {
        if (selectedFilter)
            *selectedFilter = dialog.selectedNameFilter();
        return dialog.selectedFiles().value(0);
    }

    return QString();
}

QString RemoteFileDialog::getExistingDirectory(QWidget *parent,
                                               const QString &caption,
                                               const QString &dir,
                                               QFileDialog::Options options,
                                               FileSystemLocation location)
{
    RemoteFileDialog dialog(parent, caption, dir, QString{}, location);
    dialog.setFileMode(QFileDialog::Directory);
    dialog.setOptions(options);
    if (dialog.exec() == QDialog::Accepted) {
        return dialog.selectedFiles().value(0);
    }
    return QString();
}

QString RemoteFileDialog::mungePath(const QString& path) const
{
    if (mLocation != Remote || path.isEmpty() || path.startsWith(g_remotePrefix))
        return path;
    return g_remotePrefix + path;
}

QString RemoteFileDialog::unmungePath(const QString& path) const
{
    if (mLocation != Remote || path.isEmpty())
        return path;
    if (!path.startsWith(g_remotePrefix)) {
        // somehow the user escaped from the remote side
        qWarning("Path is not remote: %s", qPrintable(path));
        return path;
    }
    return path.mid(g_remotePrefix.length());
}


QStringList RemoteFileDialog::unmungePaths(const QStringList& paths) const
{
    QStringList unmunged;
    foreach (const QString& file, paths)
        unmunged.append(unmungePath(file));
    return unmunged;
}

bool RemoteFileDialog::isRemotePath(const QString& path) const
{
	return path.startsWith(g_remotePrefix);
}

void RemoteFileDialog::setDefaultWorkingDirectory(const QString& workingDirectory)
{
    QWriteLocker locker(&g_lock);
    g_defaultWorkingDirectory = workingDirectory;
}

QString RemoteFileDialog::defaultWorkingDirectory()
{
    QReadLocker locker(&g_lock);
    return g_defaultWorkingDirectory;
}

void RemoteFileDialog::setRemoteHomeDirectory(const QString& homeDirectory)
{
    QWriteLocker locker(&g_lock);
    g_remoteHomeDirectory = homeDirectory;
}

QString RemoteFileDialog::remoteHomeDirectory()
{
    QReadLocker locker(&g_lock);
    return g_remoteHomeDirectory;
}

void RemoteFileDialog::setRemotePrefix(const QString& remotePrefix)
{
    QWriteLocker locker(&g_lock);
    g_remotePrefix = remotePrefix;
}

QString RemoteFileDialog::remotePrefix()
{
    QReadLocker locker(&g_lock);
    return g_remotePrefix;
}

void RemoteFileDialog::delayedCheckDirectory()
{
    QMetaObject::invokeMethod(this, "checkDirectory", Qt::QueuedConnection);
}

void RemoteFileDialog::checkDirectory()
{
    checkDirectory(mLookInComboBox->currentText());
}

void RemoteFileDialog::checkDirectory(const QString& path)
{
    RemoteFileSystemModel *fsModel = static_cast<RemoteFileSystemModel *>(proxyModel()->sourceModel());
    if (!fsModel->rootPath().isEmpty())
    {
        if ((mLocation == Remote) && !path.isEmpty())
        {
            const bool hasPrefix = path.startsWith(g_remotePrefix);
            const QString fsPath = hasPrefix
                ? path.mid(g_remotePrefix.length()) : path;

            if (!fsPath.isEmpty() && !fsPath.startsWith('/')) // relative path
            {
                const QString requiredPath = g_defaultWorkingDirectory + QLatin1Char('/') + fsPath;
                RemoteFileDialogBase::setDirectory(requiredPath);
            }
            else if (!hasPrefix)
            {
                RemoteFileDialogBase::setDirectory(g_remotePrefix + path);
            }
        }
    }
}

RemoteProxyModel::RemoteProxyModel(QObject * parent) :
    QSortFilterProxyModel(parent),
    mLocation(RemoteFileDialog::Remote)
{
    setSortCaseSensitivity(Qt::CaseInsensitive); // Qt default behaviour for files
}

bool RemoteProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
    if (mLocation != RemoteFileDialog::Remote)
        return true;
    RemoteFileSystemModel* f = static_cast<RemoteFileSystemModel*>(sourceModel());
    QModelIndex i = f->index(source_row, 0, source_parent);
    QString path(f->filePath(i));
    return path.startsWith(g_remotePrefix);
}

// This method is a copy of LocalExecutableFileProxy::lessThan (filedialog.cpp), with the
// only difference being the use of a RemoteFileSystemModel rather than a QFileSystemModel.
bool RemoteProxyModel::lessThan( const QModelIndex & left, const QModelIndex & right ) const
{
    // Based on QFileSystemModel's sort implementation, note that
    // QSortFilterProxyModel maintains a lot of internal state that makes 
    // reimplementing sort() inadvisable
    const RemoteFileSystemModel* f = static_cast<const RemoteFileSystemModel*>(sourceModel());
    const bool leftIsDir = f->isDir(left);
    const bool rightIsDir = f->isDir(right);

    if(left.column() == 0) // filename
    {
#ifndef Q_OS_MACOS
        // directories come first (except on OS X)
        if (leftIsDir ^ rightIsDir)
            return leftIsDir;
#endif
        return QSortFilterProxyModel::lessThan(left, right);
    }
    else if(left.column() == 1) // size
    {
        // directories always come first (at least in Qt land)
        if (leftIsDir && !rightIsDir)
            return true;
        return f->size(left) < f->size(right);
    }
    else if(left.column() == 3) // date
    {
        return f->lastModified(left) < f->lastModified(right);
    }
    else
    {
        return QSortFilterProxyModel::lessThan(left, right);
    }
}

void RemoteProxyModel::setLocation(RemoteFileDialog::FileSystemLocation location)
{
    mLocation = location;
    invalidateFilter();
}

RemoteFileDialog::FileSystemLocation RemoteProxyModel::location() const
{
    return mLocation;
}

void RemoteFileDialog::setHook(Hook hook)
{
    QWriteLocker locker(&g_lock);
    g_hook = hook;
}

RemoteFileDialog::Hook RemoteFileDialog::hook()
{
    QReadLocker locker(&g_lock);
    return g_hook;
}

QString RemoteFileDialog::tildeExpansion(const QString &path, bool *expanded) const
{
	if (mLocation != Remote)
	    return RemoteFileDialogBase::tildeExpansion(path, expanded);
    if (path == QLatin1String("~") ||
        path.startsWith(QLatin1String("~/"))) {
        if (expanded != 0)
            *expanded = true;
        return g_remoteHomeDirectory + path.mid(1);
    } else {
        if (expanded != 0)
            *expanded = false;
        return path;
	}
}

QString RemoteFileDialog::getEnvironmentVariable(const QString &string) const
{
    if (mLocation != Remote)
        return RemoteFileDialogBase::getEnvironmentVariable(string);
    return string;
}

QStringList RemoteFileDialog::typedFiles() const
{
	if (mLocation != Remote)
	    return RemoteFileDialogBase::typedFiles();
	QStringList files(RemoteFileDialogBase::typedFiles());
	QStringList ret;
	foreach (const QString& file, files) {
	    if (file.startsWith(QLatin1Char('/')) ||
	        file.startsWith(QLatin1Char('\\')))
	        ret.append(g_remotePrefix + file);
	    else
	        ret.append(file);
	}
    return ret;
}

#ifdef Q_OS_UNIX
/*
QInotifyFileSystemWatcherEngine prints:

QInotifyFileSystemWatcherEngine::addPaths: inotify_add_watch failed: No such file or directory

when trying to watch localhost:/home, etc.

This hack eats the message.
*/

#include <errno.h>
#include <stdio.h>
#include <string.h>

extern "C" void perror(const char *s)
{
    if (strstr(s, "QInotifyFileSystemWatcherEngine"))
        return;
    if (s && *s)
        fprintf(stderr, "%s: %s", s, strerror(errno));
    else
        fprintf(stderr, "%s", strerror(errno));
}
#endif
