view src/folderselectdialog.cpp @ 90:c3000fa6ca56

Add back button to subfolder selection dialog
author Andre Heinecke <andre.heinecke@intevation.de>
date Fri, 19 Jun 2015 14:27:56 +0200
parents 3916cb3c9105
children 0cf81afa82c1
line wrap: on
line source
/* Copyright (C) 2015 by ETH Zürich
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */
#include "folderselectdialog.h"
#include "constants.h"

#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTableView>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QApplication>
#include <QStyle>
#include <QFileDialog>
#include <QStandardPaths>
#include <QPushButton>
#include <QHeaderView>
#include <QDebug>
#include <QSettings>
#include <QStringList>
#include <QStandardItem>
#include <QMessageBox>

static ulong ipStringToLong(const QString str) {
    QStringList octets = str.split(".");
    if (!octets.size() > 3) {
        qWarning() << "invalid call to str to long";
        return 0;
    }
    ulong s1 = octets.at(0).toLong();
    ulong s2 = octets.at(1).toLong();
    ulong s3 = octets.at(2).toLong();
    ulong s4 = octets.at(3).toLong();

    return (s1 << 24) | (s2 << 16) | (s3 << 8) | s4;
}

class IPAwareSortModel : public QSortFilterProxyModel
{
public:
    bool lessThan(const QModelIndex &left,
                  const QModelIndex &right) const
    {
        QString leftString = sourceModel()->data(left).toString();
        QString rightString = sourceModel()->data(right).toString();

        static QRegExp ipPattern("\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b");
        if (ipPattern.indexIn(leftString) != -1 && ipPattern.indexIn(rightString) != -1) {
            return ipStringToLong(leftString) > ipStringToLong(rightString);
        } else {
            return QSortFilterProxyModel::lessThan(left, right);
        }
    }
};

FolderSelectDialog::FolderSelectDialog(const QString& startFolder,
                                       const QString& folderPattern,
                                       const QString& pathLabel,
                                       QWidget * parent,
                                       Qt::WindowFlags f) :
    QDialog(parent, f),
    mCurFolder(startFolder),
    mFolderPattern(folderPattern),
    mPathLabelString(pathLabel),
    mPathLabel(NULL),
    mPathLineEdit(NULL)
{
    mSortModel = new IPAwareSortModel();
    mModel = new QStandardItemModel();
    mShowPathSelection = !pathLabel.isEmpty();
    setupGUI();
    if (mShowPathSelection) {
        mPathLabel->setText("<b>" + pathLabel + ":</b>   ");
    }
    setFolder(mCurFolder);
#ifndef Q_OS_WIN
    QIcon windowIcon = QIcon(":/icon-64.png");
    setWindowIcon(windowIcon);
#endif
}

void FolderSelectDialog::setupGUI() {
    QVBoxLayout *base = new QVBoxLayout;
    QHBoxLayout *folderChangeArea = new QHBoxLayout;

    if (mShowPathSelection) {
        mPathLabel = new QLabel;
        folderChangeArea->addWidget(mPathLabel);
        base->addLayout(folderChangeArea);

        QPushButton *folderSelect = new QPushButton();
        folderSelect->setToolTip(tr("Select folder"));
        folderSelect->setIcon(QApplication::style()->standardIcon(QStyle::SP_DirIcon));
        connect(folderSelect, &QPushButton::clicked, this,
                &FolderSelectDialog::changeFolderClicked);
        folderChangeArea->addWidget(folderSelect);

        mPathLineEdit = new QLineEdit;
        mPathLineEdit->setClearButtonEnabled(true);
        connect(mPathLineEdit, &QLineEdit::textChanged, this,
                &FolderSelectDialog::pathLineChanged);
        folderChangeArea->addWidget(mPathLineEdit);

        mGoButton = new QPushButton(tr("Go"));
        mGoButton->setEnabled(false);

        QPushButton *backBtn = new QPushButton;
        backBtn->setIcon(QApplication::style()->standardIcon(QStyle::SP_ArrowBack));
        backBtn->setToolTip(tr("Back to exam selection."));

        folderChangeArea->addWidget(mGoButton);
        connect(mGoButton, &QPushButton::clicked, this,
                &FolderSelectDialog::goClicked);
        connect(mPathLineEdit, &QLineEdit::returnPressed, this,
                &FolderSelectDialog::goClicked);
    }

    mFilterWidget = new FilterWidget(mSortModel, false);
    base->addWidget(mFilterWidget);
    connect(mSortModel, &QSortFilterProxyModel::sourceModelChanged,
            mFilterWidget, &FilterWidget::headersChanged);

    mView = new QTableView;
    mView->setModel(mSortModel);

    mView->setSelectionBehavior(QAbstractItemView::SelectRows);
    mView->setSelectionMode(QAbstractItemView::SingleSelection);
    mView->setSortingEnabled(true);
    mView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    connect(mView->selectionModel(), &QItemSelectionModel::selectionChanged,
            this, &FolderSelectDialog::selectionChanged);
    connect(mView, &QTableView::doubleClicked, this,
            &FolderSelectDialog::wantToAccept);

    base->addWidget(mView);

    QHBoxLayout * bottomButtons = new QHBoxLayout;
    base->addLayout(bottomButtons);

    mOkButton = new QPushButton(tr("Ok"));
    mOkButton->setEnabled(false);
    connect(mOkButton, &QPushButton::clicked, this,
            &FolderSelectDialog::wantToAccept);
    bottomButtons->addStretch(-1);

    QPushButton *backBtn = NULL;
    if (!mShowPathSelection) {
        backBtn = new QPushButton(tr("Back"));
        backBtn->setToolTip(tr("Back to exam selection."));
        connect(backBtn, &QPushButton::clicked, this,
                &FolderSelectDialog::backClicked);
        bottomButtons->addWidget(backBtn);
    }

    bottomButtons->addWidget(mOkButton);
    setLayout(base);
}

void FolderSelectDialog::selectionChanged (const QItemSelection& selected,
                                           const QItemSelection& deselected) {
    mOkButton->setEnabled(!selected.indexes().isEmpty());
    if (mOkButton->isEnabled()) {
        mOkButton->setFocus();
    }
}

void FolderSelectDialog::pathLineChanged() {
    const QString path = mPathLineEdit->text();
    if (path.isEmpty()) {
        mGoButton->setEnabled(false);
        return;
    }
    mGoButton->setEnabled(true);
}


void FolderSelectDialog::goClicked() {
    const QString path = mPathLineEdit->text();
    QDir dir(path);
    if (!dir.exists()) {
        QMessageBox::warning(this, tr("Error!"), tr("Failed to access directory: '%1'").arg(path));
        mGoButton->setEnabled(false);
        return;
    }
    setFolder(mPathLineEdit->text());
    QSettings settings;
    /* assuming go is only available in root folder mode */
    settings.setValue(ROOT_FOLDER_KEY, mPathLineEdit->text());
}

void FolderSelectDialog::setFolder(const QString& folder) {
    if (folder.isEmpty()) {
        return;
    }
    mCurFolder = folder;
    if (mPathLineEdit) {
        mPathLineEdit->setText(mCurFolder);
    }

    QStringList columns = mFolderPattern.split(PATTERN_SEPERATOR);
    mModel->clear();
    mModel->setHorizontalHeaderLabels(columns);
    QDir dir(folder);
    qDebug() << "Folder set to: " << folder;
    QStringList errors;
    foreach (const QString & subfolder, dir.entryList(QDir::Dirs |
                                                      QDir::Readable |
                                                      QDir::NoDotAndDotDot)) {
        qDebug() << "Looking at: " << subfolder;
        QStringList itemData = subfolder.split(PATTERN_SEPERATOR);
        if (itemData.size() != columns.size()) {
            errors << subfolder;
            continue;
        }
        QList<QStandardItem*> items;
        foreach (const QString& part, itemData) {
            QStandardItem * item = new QStandardItem(part);
            item->setData(dir.absoluteFilePath(subfolder));
            items.append(item);
        }
        mModel->appendRow(items);
    }
    mSortModel->setSourceModel(mModel);
    if (!errors.isEmpty()) {
        QString errMsg = tr("The following folders did not match the pattern: %1").arg(mFolderPattern);
        errMsg += "\n" + errors.join("\n");
        QMessageBox::warning(this, tr("Failed to parse some folders."), errMsg);
    }
    mView->resizeColumnsToContents();
}

void FolderSelectDialog::wantToAccept() {
    QItemSelectionModel *selection = mView->selectionModel();
    QModelIndexList selected = selection->selectedIndexes();
    if (selected.isEmpty()) { /* Ok should not be enabled in that case */
        return;
    }
    const QString folder = selected[0].data(Qt::UserRole + 1).toString();

    emit folderSelected(folder);
    accept();
}

void FolderSelectDialog::changeFolderClicked()
{
    const QString oldFolder = mCurFolder;
    const QString startLoc = mCurFolder.isEmpty() ?
        QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) :
        mCurFolder;

    QString outFolder = QFileDialog::getExistingDirectory(
            this, tr("Select %1").arg(mPathLabelString),
            startLoc);
    if (outFolder.isEmpty()) {
        /* User aborted */
        return;
    }
    setFolder(outFolder);
}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)