view src/folderselectdialog.cpp @ 106:32dd6ea6a582

Explicitly use c++11 as newer qt versions need that.
author Andre Heinecke <andre.heinecke@intevation.de>
date Thu, 08 Dec 2016 15:21:11 +0100
parents 4302ca793c5a
children 0ad468912ff3
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);
    resize(700, 600);
}

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

QString stripQuotes (const QString &in)
{
    QString out = in;
    if (in.startsWith("\"")) {
        out = out.right(out.size()-1);
    }
    if (in.endsWith("\"")) {
        out = out.left(out.size()-1);
    }
    return out;
}

QMap <QString, QString> readExtraColumns(const QString &path)
{
    QMap <QString, QString> ret;

    QFile file(path);
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug () << "Failed to open: " << path;
        return ret;
    }
    QTextStream in(&file);

    while (!in.atEnd()) {
        QString line = in.readLine();
        int equalsign = line.indexOf("=");
        if (equalsign != -1) {
            QString name = stripQuotes(line.left(equalsign).replace (QStringLiteral("export "), ""));
            QString value = stripQuotes(line.right(line.size() - equalsign - 1));
            ret.insert (name, value);
        }
    }
    return ret;
}

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

    QStringList columns = mFolderPattern.split(PATTERN_SEPERATOR);
    int patternSize = columns.size();
    QDir dir(folder);
    mModel->clear();
    qDebug() << "Folder set to: " << folder;
    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() != patternSize) {
            qDebug() << "Folder does not match pattern: " << subfolder;
            continue;
        }
        QList<QStandardItem*> items;
        foreach (const QString& part, itemData) {
            QStandardItem * item = new QStandardItem(part);
            item->setData(dir.absoluteFilePath(subfolder));
            QFileInfo fi (dir.absoluteFilePath(subfolder + "/.exam-setup-user"));
            items.append(item);
            if (fi.isReadable()) {
                const QMap<QString, QString> map = readExtraColumns (fi.filePath());

                Q_FOREACH (const QString &name, map.keys())
                {
                    qDebug() << "Adding extra column: " << name;
                    if (!columns.contains(name)) {
                        columns << name;
                    }
                    QStandardItem * subitem = new QStandardItem(map[name]);
                    items.append(subitem);
                }
            }
        }
        mModel->appendRow(items);
    }
    mModel->setHorizontalHeaderLabels(columns);
    mSortModel->setSourceModel(mModel);
    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)