/* SPDX-FileCopyrightText: 2009 Dmitry Suzdalev SPDX-License-Identifier: GPL-2.0-or-later */ #include "editactiondialog.h" #include "klipper_debug.h" #include #include #include #include #include #include #include "ui_editactiondialog.h" #include "urlgrabber.h" namespace { static QString output2text(ClipCommand::Output output) { switch (output) { case ClipCommand::IGNORE: return QString(i18n("Ignore")); case ClipCommand::REPLACE: return QString(i18n("Replace Clipboard")); case ClipCommand::ADD: return QString(i18n("Add to Clipboard")); } return QString(); } } /** * Show dropdown of editing Output part of commands */ class ActionOutputDelegate : public QItemDelegate { public: ActionOutputDelegate(QObject *parent = nullptr) : QItemDelegate(parent) { } QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const override { QComboBox *editor = new QComboBox(parent); editor->setInsertPolicy(QComboBox::NoInsert); editor->addItem(output2text(ClipCommand::IGNORE), QVariant::fromValue(ClipCommand::IGNORE)); editor->addItem(output2text(ClipCommand::REPLACE), QVariant::fromValue(ClipCommand::REPLACE)); editor->addItem(output2text(ClipCommand::ADD), QVariant::fromValue(ClipCommand::ADD)); return editor; } void setEditorData(QWidget *editor, const QModelIndex &index) const override { QComboBox *ed = static_cast(editor); QVariant data(index.model()->data(index, Qt::EditRole)); ed->setCurrentIndex(static_cast(data.value())); } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { QComboBox *ed = static_cast(editor); model->setData(index, ed->itemData(ed->currentIndex())); } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & /*index*/) const override { editor->setGeometry(option.rect); } }; class ActionDetailModel : public QAbstractTableModel { public: ActionDetailModel(ClipAction *action, QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; const QList &commands() const { return m_commands; } void addCommand(const ClipCommand &command); void removeCommand(const QModelIndex &index); private: enum column_t { COMMAND_COL = 0, OUTPUT_COL = 1, DESCRIPTION_COL = 2 }; QList m_commands; QVariant displayData(ClipCommand *command, column_t column) const; QVariant editData(ClipCommand *command, column_t column) const; QVariant decorationData(ClipCommand *command, column_t column) const; void setIconForCommand(ClipCommand &cmd); }; ActionDetailModel::ActionDetailModel(ClipAction *action, QObject *parent) : QAbstractTableModel(parent) , m_commands(action->commands()) { } Qt::ItemFlags ActionDetailModel::flags(const QModelIndex & /*index*/) const { return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } void ActionDetailModel::setIconForCommand(ClipCommand &cmd) { // let's try to update icon of the item according to command QString command = cmd.command; if (command.contains(QLatin1Char(' '))) { // get first word command = command.section(QLatin1Char(' '), 0, 0); } if (QIcon::hasThemeIcon(command)) { cmd.icon = command; } else { cmd.icon.clear(); } } bool ActionDetailModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::EditRole) { ClipCommand cmd = m_commands.at(index.row()); switch (static_cast(index.column())) { case COMMAND_COL: cmd.command = value.toString(); setIconForCommand(cmd); break; case OUTPUT_COL: cmd.output = value.value(); break; case DESCRIPTION_COL: cmd.description = value.toString(); break; } m_commands.replace(index.row(), cmd); Q_EMIT dataChanged(index, index); return true; } return false; } int ActionDetailModel::columnCount(const QModelIndex & /*parent*/) const { return 3; } int ActionDetailModel::rowCount(const QModelIndex &) const { return m_commands.count(); } QVariant ActionDetailModel::displayData(ClipCommand *command, ActionDetailModel::column_t column) const { switch (column) { case COMMAND_COL: return command->command; case OUTPUT_COL: return output2text(command->output); case DESCRIPTION_COL: return command->description; } return QVariant(); } QVariant ActionDetailModel::decorationData(ClipCommand *command, ActionDetailModel::column_t column) const { switch (column) { case COMMAND_COL: return command->icon.isEmpty() ? QIcon::fromTheme(QStringLiteral("system-run")) : QIcon::fromTheme(command->icon); case OUTPUT_COL: case DESCRIPTION_COL: break; } return QVariant(); } QVariant ActionDetailModel::editData(ClipCommand *command, ActionDetailModel::column_t column) const { switch (column) { case COMMAND_COL: return command->command; case OUTPUT_COL: return QVariant::fromValue(command->output); case DESCRIPTION_COL: return command->description; } return QVariant(); } QVariant ActionDetailModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (static_cast(section)) { case COMMAND_COL: return i18n("Command"); case OUTPUT_COL: return i18n("Output Handling"); case DESCRIPTION_COL: return i18n("Description"); } } return QAbstractTableModel::headerData(section, orientation, role); } QVariant ActionDetailModel::data(const QModelIndex &index, int role) const { const int column = index.column(); const int row = index.row(); ClipCommand cmd = m_commands.at(row); switch (role) { case Qt::DisplayRole: return displayData(&cmd, static_cast(column)); case Qt::DecorationRole: return decorationData(&cmd, static_cast(column)); case Qt::EditRole: return editData(&cmd, static_cast(column)); } return QVariant(); } void ActionDetailModel::addCommand(const ClipCommand &command) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_commands << command; endInsertRows(); } void ActionDetailModel::removeCommand(const QModelIndex &index) { int row = index.row(); beginRemoveRows(QModelIndex(), row, row); m_commands.removeAt(row); endRemoveRows(); } EditActionDialog::EditActionDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Action Properties")); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); buttons->button(QDialogButtonBox::Ok)->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttons, &QDialogButtonBox::accepted, this, &EditActionDialog::slotAccepted); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); QWidget *dlgWidget = new QWidget(this); m_ui = new Ui::EditActionDialog; m_ui->setupUi(dlgWidget); m_ui->leRegExp->setClearButtonEnabled(true); m_ui->leDescription->setClearButtonEnabled(true); m_ui->pbAddCommand->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); m_ui->pbRemoveCommand->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); // For some reason, the default row height is 30 pixel. Set it to the minimum sectionSize instead, // which is the font height+struts. m_ui->twCommandList->verticalHeader()->setDefaultSectionSize(m_ui->twCommandList->verticalHeader()->minimumSectionSize()); m_ui->twCommandList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(dlgWidget); layout->addWidget(buttons); connect(m_ui->pbAddCommand, &QPushButton::clicked, this, &EditActionDialog::onAddCommand); connect(m_ui->pbRemoveCommand, &QPushButton::clicked, this, &EditActionDialog::onRemoveCommand); const KConfigGroup grp = KSharedConfig::openConfig()->group("EditActionDialog"); KWindowConfig::restoreWindowSize(windowHandle(), grp); QByteArray hdrState = grp.readEntry("ColumnState", QByteArray()); if (!hdrState.isEmpty()) { qCDebug(KLIPPER_LOG) << "Restoring column state"; m_ui->twCommandList->horizontalHeader()->restoreState(QByteArray::fromBase64(hdrState)); } // do this after restoreState() m_ui->twCommandList->horizontalHeader()->setHighlightSections(false); } EditActionDialog::~EditActionDialog() { delete m_ui; } void EditActionDialog::setAction(ClipAction *act, int commandIdxToSelect) { m_action = act; m_model = new ActionDetailModel(act, this); m_ui->twCommandList->setModel(m_model); m_ui->twCommandList->setItemDelegateForColumn(1, new ActionOutputDelegate); connect(m_ui->twCommandList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &EditActionDialog::onSelectionChanged); updateWidgets(commandIdxToSelect); } void EditActionDialog::updateWidgets(int commandIdxToSelect) { if (!m_action) { qCDebug(KLIPPER_LOG) << "no action to edit was set"; return; } m_ui->leRegExp->setText(m_action->actionRegexPattern()); m_ui->automatic->setChecked(m_action->automatic()); m_ui->leDescription->setText(m_action->description()); if (commandIdxToSelect != -1) { m_ui->twCommandList->setCurrentIndex(m_model->index(commandIdxToSelect, 0)); } // update Remove button onSelectionChanged(); } void EditActionDialog::saveAction() { if (!m_action) { qCDebug(KLIPPER_LOG) << "no action to edit was set"; return; } m_action->setActionRegexPattern(m_ui->leRegExp->text()); m_action->setDescription(m_ui->leDescription->text()); m_action->setAutomatic(m_ui->automatic->isChecked()); m_action->clearCommands(); foreach (const ClipCommand &cmd, m_model->commands()) { m_action->addCommand(cmd); } } void EditActionDialog::slotAccepted() { saveAction(); qCDebug(KLIPPER_LOG) << "Saving dialogue state"; KConfigGroup grp = KSharedConfig::openConfig()->group("EditActionDialog"); KWindowConfig::saveWindowSize(windowHandle(), grp); grp.writeEntry("ColumnState", m_ui->twCommandList->horizontalHeader()->saveState().toBase64()); accept(); } void EditActionDialog::onAddCommand() { m_model->addCommand(ClipCommand(i18n("new command"), i18n("Command Description"), true, QLatin1String(""))); m_ui->twCommandList->edit(m_model->index(m_model->rowCount() - 1, 0)); } void EditActionDialog::onRemoveCommand() { m_model->removeCommand(m_ui->twCommandList->selectionModel()->currentIndex()); } void EditActionDialog::onSelectionChanged() { m_ui->pbRemoveCommand->setEnabled(m_ui->twCommandList->selectionModel() && m_ui->twCommandList->selectionModel()->hasSelection()); }