aboutsummaryrefslogblamecommitdiff
path: root/qdeduper/preferencedialog.cpp
blob: 40d2aead94e5de81e724f220488a1d98c9f70866 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
                  


                     
                    







                           



                             

                      









                                                                                                                     
                               
















                                                                                       















                                                                                                                                            















                                                    
                                       

















































                                                                              










                                                                                                                                 
















                                                                                                                                  

 
































                                                                      









                                                                   
                                                               






                                                                                         

















                                                                                          


































                                                                      

                                              
                                                                                     
                                                                                                   







                                                                         

     






























                                                                                                     






















                                                                                                                                  
 












































































                                                                                                      
#include <climits>
#include <functional>

#include <QDebug>
#include <QKeyEvent>
#include <QLabel>
#include <QTabWidget>
#include <QGridLayout>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QDialogButtonBox>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QAction>
#include <QTableView>
#include <QStandardItemModel>
#include <QKeySequenceEdit>
#include <QGroupBox>
#include <QMessageBox>

#include "preferencedialog.hpp"
#include "settings.hpp"

PreferenceDialog::PreferenceDialog(SettingsRegistry *sr, QWidget *parent) : QDialog(parent)
{
    this->sr = sr;
    tw = new QTabWidget(this);
    bb = new QDialogButtonBox(QDialogButtonBox::StandardButton::Ok | QDialogButtonBox::StandardButton::Cancel, this);
    this->setWindowTitle("Preferences");
    this->setMinimumWidth(480);
    QVBoxLayout *l = new QVBoxLayout();
    this->setLayout(l);
    l->addWidget(tw);
    l->addWidget(bb);
    QObject::connect(bb, &QDialogButtonBox::accepted, this, &PreferenceDialog::accept);
    QObject::connect(bb, &QDialogButtonBox::rejected, this, &PreferenceDialog::reject);
    setup_widgets();
}

void PreferenceDialog::open()
{
    load_widget_status();
    QDialog::open();
}

void PreferenceDialog::accept()
{
    QKeySequence bks;
    switch (verify_shortcuts(&bks))
    {
        case 0:
        break;
        case 1:
            QMessageBox::critical(this, "Ambiguous shortcut found",
                                  QString("Shortcut %1 is assigned to multiple actions.").arg(bks.toString()));
        return;
        case 2:
            QMessageBox::critical(this, "Bad shortcut assignment",
                                  QString("Shortcut %1 has more than one key and cannot be assigned to item actions.").arg(bks.toString()));
        return;
        default:
        break;
    }
    save_widget_status();
    QDialog::accept();
}

void PreferenceDialog::setup_widgets()
{
    for (auto &i : sr->tabs)
    {
        QWidget *container = new QWidget();
        QGridLayout *l = new QGridLayout(container);
        tw->addTab(container, i);
        tabs.push_back(l);
    }
    for (auto &k : sr->klist)
    {
        auto &p = sr->smap[k];
        if (!p.desc.length()) continue;
        QWidget *w = nullptr;
        QGridLayout *l = p.tab < tabs.size() ? tabs[p.tab] : nullptr;
        if (!l) continue;
        switch (p.type)
        {
            case SettingsItem::ParameterType::_int:
            {
                QSpinBox *sb = new QSpinBox();
                sb->setMinimum(p.min.value<int>());
                sb->setMaximum(p.max.value<int>());
                w = sb;
            }
            break;
            case SettingsItem::ParameterType::_bool:
            {
                QCheckBox *cb = new QCheckBox(p.desc);
                w = cb;
            }
            break;
            case SettingsItem::ParameterType::_double:
            {
                QDoubleSpinBox *sb = new QDoubleSpinBox();
                sb->setMinimum(p.min.value<double>());
                sb->setMaximum(p.max.value<double>());
                sb->setSingleStep((sb->maximum() - sb->minimum()) / 100.);
                w = sb;
            }
            break;
            case SettingsItem::ParameterType::_keyseq:
            break;
            default:
            break;
        }
        if (!w) continue;
        p.w = w;
        if (p.type == SettingsItem::ParameterType::_bool)
        {
            l->addWidget(w, l->rowCount(), 0, 1, 2);
        }
        else
        {
            QLabel *lb = new QLabel(p.desc);
            lb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
            int r = l->rowCount();
            l->addWidget(lb, r, 0);
            l->addWidget(w, r, 1);
        }
    }
}

void PreferenceDialog::set_hkactions(int tab, std::map<std::string, QKeySequence> defmap, std::map<std::string, QAction*> actmap)
{
    this->defmap = defmap;
    this->actmap = actmap;
    this->hktv = new QTableView();
    this->hkim = new QStandardItemModel();
    this->tabs[tab]->addWidget(hktv, 0, 0);
    this->hktv->setModel(hkim);
    this->hktv->setSortingEnabled(false);
    this->hktv->setSelectionMode(QAbstractItemView::SelectionMode::NoSelection);
    this->hktv->setItemDelegateForColumn(1, new ShortcutEditorDelegate);

    QGroupBox *gb = new QGroupBox("Action Modifiers");
    QGridLayout *l = new QGridLayout();
    gb->setLayout(l);
    this->tabs[tab]->addWidget(gb, 1, 0);

    const QStringList llist = {"Mark/Unmark", "Mark All Except", "Maximize", "Open with System Viewer", "Open Containing Folder"};

    for (int i = 0; i < llist.size(); ++i)
    {
        auto &lt = llist[i];
        ModifierEdit *me = new ModifierEdit();
        QLabel *lb = new QLabel(lt);
        l->addWidget(lb, i, 0);
        l->addWidget(me, i, 1);
        mes.push_back(me);
    }
}

void PreferenceDialog::load_widget_status()
{
    for (auto &k : sr->klist)
    {
        auto &p = sr->smap[k];
        QWidget *w = p.w;
        switch (p.type)
        {
            case SettingsItem::ParameterType::_int:
            {
                QSpinBox *sb = qobject_cast<QSpinBox*>(w);
                if (!sb) continue;
                sb->setValue(sr->get_option_int(p.key));
            }
            break;
            case SettingsItem::ParameterType::_bool:
            {
                QCheckBox *cb = qobject_cast<QCheckBox*>(w);
                cb->setChecked(sr->get_option_bool(p.key));
            }
            break;
            case SettingsItem::ParameterType::_double:
            {
                QDoubleSpinBox *sb = qobject_cast<QDoubleSpinBox*>(w);
                sb->setValue(sr->get_option_double(p.key));
            }
            break;
            case SettingsItem::ParameterType::_keyseq:
            break;
            default:
            break;
        }
    }
    this->hkim->clear();
    this->hkim->setHorizontalHeaderLabels({"Menu Item", "Hotkey"});
    for (auto &hkp : this->defmap)
    {
        std::string actn = hkp.first.substr(3);
        QKeySequence ks = sr->get_option_keyseq("hotkey/" + actn);
        if (this->actmap.find(actn) == this->actmap.end())
            continue;
        QAction *act = this->actmap[actn];
        QStandardItem *itma = new QStandardItem(act->text());
        QStandardItem *itmk = new QStandardItem(ks.toString());
        itma->setIcon(act->icon());
        itma->setEditable(false);
        itma->setData(QString::fromStdString(actn), Qt::ItemDataRole::UserRole);
        itmk->setData(QVariant::fromValue<QKeySequence>(ks), Qt::ItemDataRole::UserRole);
        this->hkim->appendRow({itma, itmk});
    }
    this->hktv->resizeColumnsToContents();
    for (size_t i = 0; i < 16; ++i)
    {
        std::string iakt = "item_" + std::to_string(i) + "_action_key";
        int ik = sr->get_option_int("hotkey/" + iakt);
        QKeySequence ks(ik);
        QStandardItem *itma = new QStandardItem(QString("Item %1 Action Key").arg(i + 1));
        QStandardItem *itmk = new QStandardItem(ks.toString());
        itma->setEditable(false);
        itma->setData(QString::fromStdString(iakt), Qt::ItemDataRole::UserRole);
        itmk->setData(QVariant::fromValue<QKeySequence>(ks), Qt::ItemDataRole::UserRole);
        this->hkim->appendRow({itma, itmk});
    }
    for (size_t i = 0; i < 5; ++i)
    {
        std::string iamt = "hotkey/item_action_mod_" + std::to_string(i);
        int im = sr->get_option_int(iamt);
        mes[i]->set_modifier(static_cast<Qt::Modifier>(im));
    }
}

void PreferenceDialog::save_widget_status()
{
    for (auto &k : sr->klist)
    {
        auto &p = sr->smap[k];
        QWidget *w = p.w;
        switch (p.type)
        {
            case SettingsItem::ParameterType::_int:
            {
                QSpinBox *sb = qobject_cast<QSpinBox*>(w);
                if (!sb) continue;
                sr->set_option_int(p.key, sb->value());
            }
            break;
            case SettingsItem::ParameterType::_bool:
            {
                QCheckBox *cb = qobject_cast<QCheckBox*>(w);
                sr->set_option_bool(p.key, cb->isChecked());
            }
            break;
            case SettingsItem::ParameterType::_double:
            {
                QDoubleSpinBox *sb = qobject_cast<QDoubleSpinBox*>(w);
                sr->set_option_double(p.key, sb->value());
            }
            break;
            case SettingsItem::ParameterType::_keyseq:
            break;
            default:
            break;
        }
    }
    for (int i = 0; i < hkim->rowCount(); ++i)
    {
        QString actn = hkim->item(i, 0)->data(Qt::ItemDataRole::UserRole).toString();
        QKeySequence ks = hkim->item(i, 1)->data(Qt::ItemDataRole::UserRole).value<QKeySequence>();
        if (actn.endsWith("_action_key"))
            sr->set_option_int("hotkey/" + actn.toStdString(), ks[0]);
        else sr->set_option_keyseq("hotkey/" + actn.toStdString(), ks);
    }
    for (size_t i = 0; i < 5; ++i)
    {
        std::string iamt = "hotkey/item_action_mod_" + std::to_string(i);
        sr->set_option_int(iamt, mes[i]->get_modifier());
    }
}

int PreferenceDialog::verify_shortcuts(QKeySequence *bks)
{
    QList<QKeySequence> ksl;
    for (int i = 0; i < hkim->rowCount(); ++i)
    {
        QString actn = hkim->item(i, 0)->data(Qt::ItemDataRole::UserRole).toString();
        QKeySequence ks = hkim->item(i, 1)->data(Qt::ItemDataRole::UserRole).value<QKeySequence>();
        if (actn.endsWith("_action_key"))
        {
            if (ks.count() > 1)
            {
                *bks = ks;
                return 2;
            }
            for (int j = 0;j < 5; ++j)
                if (mes[j]->get_modifier() != INT_MAX)
                    ksl.push_back(QKeySequence(mes[j]->get_modifier() | ks[0]));
        }
        else ksl.push_back(hkim->item(i, 1)->data(Qt::ItemDataRole::UserRole).value<QKeySequence>());
    }
    for (int i = 0; i < ksl.size(); ++i)
        for (int j = i + 1; j < ksl.size(); ++j)
            if (!ksl[i].isEmpty() && ksl[i] == ksl[j])
            {
                *bks = ksl[i];
                return 1;
            }
    return 0;
}

QWidget* ShortcutEditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option);
    Q_UNUSED(index);
    QKeySequenceEdit *kse = new QKeySequenceEdit(parent);
    QObject::connect(kse, &QKeySequenceEdit::editingFinished, [this, kse] {
        Q_EMIT const_cast<ShortcutEditorDelegate*>(this)->commitData(kse);
        Q_EMIT const_cast<ShortcutEditorDelegate*>(this)->closeEditor(kse);
    });
    return kse;
}

void ShortcutEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QKeySequenceEdit *kse = qobject_cast<QKeySequenceEdit*>(editor);
    kse->setKeySequence(index.data(Qt::ItemDataRole::UserRole).value<QKeySequence>());
}

void ShortcutEditorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QKeySequenceEdit *kse = qobject_cast<QKeySequenceEdit*>(editor);
    model->setData(index, QVariant::fromValue<QKeySequence>(kse->keySequence()), Qt::ItemDataRole::UserRole);
    model->setData(index, kse->keySequence().toString(), Qt::ItemDataRole::DisplayRole);
}

ModifierEdit::ModifierEdit(QWidget *par) : QPushButton(par)
{
    this->setCheckable(true);
    this->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
    this->mod = static_cast<Qt::Modifier>(0);
    QObject::connect(this, &QPushButton::toggled, [this](bool c) {
        if (c)
        {
            if (int(this->mod) == INT_MAX)
                this->set_modifier(static_cast<Qt::Modifier>(0));
            this->setText("Input modifier...");
        }
        else
            this->set_modifier(this->mod);
    });
}

Qt::Modifier ModifierEdit::get_modifier()
{
    return this->mod;
}

void ModifierEdit::set_modifier(Qt::Modifier mod)
{
    this->mod = mod;
    int imod = mod;
    switch (imod)
    {
        case 0:
            if (this->isChecked())
                this->setText("Input modifier...");
            else
                this->setText("No modifier");
        break;
        case INT_MAX:
            this->setText("Disabled");
        break;
        default:
            QString kss = QKeySequence(mod).toString();
            while (kss.length() > 0 && kss.back() == '+') kss.chop(1);
            this->setText(kss);
    }
}

void ModifierEdit::keyPressEvent(QKeyEvent *e)
{
    if (!this->isChecked()) return QPushButton::keyPressEvent(e);
    switch (e->key())
    {
        case Qt::Key::Key_Enter:
        case Qt::Key::Key_Return:
        case Qt::Key::Key_Space:
            this->set_modifier(static_cast<Qt::Modifier>(Qt::KeyboardModifiers::Int(e->modifiers())));
            this->setChecked(false);
        break;
        case Qt::Key::Key_Escape:
            this->set_modifier(static_cast<Qt::Modifier>(0));
            this->setChecked(false);
        break;
        case Qt::Key::Key_Backspace:
        case Qt::Key::Key_Delete:
            this->set_modifier(static_cast<Qt::Modifier>(INT_MAX));
            this->setChecked(false);
        break;
    }
}

bool ModifierEdit::event(QEvent *e)
{
    if (e->type() == QEvent::Type::ShortcutOverride)
    {
        e->accept();
        return true;
    }
    return QPushButton::event(e);
}