File indexing completed on 2024-05-19 05:55:48

0001 /*
0002     SPDX-FileCopyrightText: 2003, 2004 George Staikos <staikos@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kwmapeditor.h"
0008 
0009 #include <QAction>
0010 #include <KActionCollection>
0011 #include <QIcon>
0012 #include <KLocalizedString>
0013 #include <QMenu>
0014 #include <KStandardAction>
0015 #include <KWindowSystem>
0016 #include <QTextEdit>
0017 
0018 #include <QApplication>
0019 #include <QClipboard>
0020 #include <QItemDelegate>
0021 #include <QFocusEvent>
0022 #include <QKeyEvent>
0023 #include <QPointer>
0024 #include <QToolButton>
0025 
0026 class InlineEditor : public QTextEdit
0027 {
0028 public:
0029     InlineEditor(KWMapEditor *p) : QTextEdit(), _p(p)
0030     {
0031         setAttribute(Qt::WA_DeleteOnClose);
0032         setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
0033         connect(p, &QObject::destroyed, this, &QWidget::close);
0034     }
0035 
0036 protected:
0037     void focusOutEvent(QFocusEvent *e) override
0038     {
0039         if (e->reason() == Qt::PopupFocusReason) {
0040             // TODO: It seems we only get here if we're disturbed
0041             // by our own popup. this needs some clearance though.
0042             return;
0043         }
0044 
0045         close();
0046     }
0047     void keyPressEvent(QKeyEvent *e) override
0048     {
0049         if (e->key() == Qt::Key_Escape) {
0050             e->accept();
0051             close();
0052         } else {
0053             e->ignore();
0054             QTextEdit::keyPressEvent(e);
0055         }
0056     }
0057     void contextMenuEvent(QContextMenuEvent *event) override
0058     {
0059         QMenu *menu = createStandardContextMenu();
0060         popup = menu;
0061         popup->exec(event->globalPos());
0062         delete popup;
0063     }
0064     QPointer<KWMapEditor> _p;
0065     QPointer<QMenu> popup;
0066 };
0067 
0068 class KWMapEditorDelegate : public QItemDelegate
0069 {
0070 public:
0071     KWMapEditorDelegate(KWMapEditor *parent) : QItemDelegate(parent)
0072     {
0073     }
0074 
0075     QWidget *createEditor(QWidget *parentWidget, const QStyleOptionViewItem &option, const QModelIndex &index) const override
0076     {
0077         if (index.column() != 2) {
0078             return QItemDelegate::createEditor(parentWidget, option, index);
0079         }
0080 
0081         auto mapEditor = static_cast<KWMapEditor *>(parent());
0082         return new InlineEditor(mapEditor);
0083     }
0084 
0085     void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
0086     {
0087         if (dynamic_cast<InlineEditor *>(editor)) {
0088             auto mapEditor = static_cast<KWMapEditor *>(parent());
0089             const QRect geo = mapEditor->visualRect(index);
0090             editor->move(mapEditor->mapToGlobal(geo.topLeft()));
0091             editor->resize(geo.width(), geo.height() * 3);
0092         } else {
0093             QItemDelegate::updateEditorGeometry(editor, option, index);
0094         }
0095     }
0096 
0097     void setEditorData(QWidget *editor, const QModelIndex &index) const override
0098     {
0099         auto e = dynamic_cast<InlineEditor *>(editor);
0100         if (e) {
0101             auto mapEditor = static_cast<KWMapEditor *>(parent());
0102             e->setText(mapEditor->item(index.row(), index.column())->text());
0103         } else {
0104             QItemDelegate::setEditorData(editor, index);
0105         }
0106     }
0107 
0108     void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
0109     {
0110         auto e = dynamic_cast<InlineEditor *>(editor);
0111         if (e) {
0112             auto mapEditor = static_cast<KWMapEditor *>(parent());
0113             mapEditor->item(index.row(), index.column())->setText(e->toPlainText());
0114         } else {
0115             QItemDelegate::setModelData(editor, model, index);
0116         }
0117     }
0118 };
0119 
0120 KWMapEditor::KWMapEditor(QMap<QString, QString> &map, QWidget *parent)
0121     : QTableWidget(0, 3, parent), _map(map)
0122 {
0123     setItemDelegate(new KWMapEditorDelegate(this));
0124     _ac = new KActionCollection(this);
0125     _copyAct = KStandardAction::copy(this, &KWMapEditor::copy, _ac);
0126     connect(this, &KWMapEditor::itemChanged, this, &KWMapEditor::dirty);
0127     connect(this, &KWMapEditor::customContextMenuRequested, this, &KWMapEditor::contextMenu);
0128     setSelectionMode(NoSelection);
0129     setHorizontalHeaderLabels(QStringList() << QString() << i18n("Key") << i18n("Value"));
0130     setContextMenuPolicy(Qt::CustomContextMenu);
0131     reload();
0132 }
0133 
0134 void KWMapEditor::reload()
0135 {
0136     int row = 0;
0137 
0138     while ((row = rowCount()) > _map.count()) {
0139         removeRow(row - 1);
0140     }
0141 
0142     if ((row = rowCount()) < _map.count()) {
0143         setRowCount(_map.count());
0144         for (int x = row; x < rowCount(); ++x) {
0145             auto b = new QToolButton(this);
0146             b->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0147             b->setToolTip(i18n("Delete Entry"));
0148             connect(b, &QToolButton::clicked, this, &KWMapEditor::erase);
0149             setCellWidget(x, 0, b);
0150             if (columnWidth(0) != b->sizeHint().width()) {
0151                 setColumnWidth(0, b->sizeHint().width());
0152             }
0153             setItem(x, 1, new QTableWidgetItem());
0154             setItem(x, 2, new QTableWidgetItem());
0155         }
0156     }
0157 
0158     row = 0;
0159     for (QMap<QString, QString>::Iterator it = _map.begin(), end = _map.end(); it != end; ++it) {
0160         item(row, 1)->setText(it.key());
0161         item(row, 2)->setText(it.value());
0162         row++;
0163     }
0164 }
0165 
0166 KWMapEditor::~KWMapEditor() = default;
0167 
0168 void KWMapEditor::erase()
0169 {
0170     const QObject *o = sender();
0171     for (int i = 0; i < rowCount(); i++) {
0172         if (cellWidget(i, 0) == o) {
0173             removeRow(i);
0174             break;
0175         }
0176     }
0177 
0178     Q_EMIT dirty();
0179 }
0180 
0181 void KWMapEditor::saveMap()
0182 {
0183     _map.clear();
0184 
0185     for (int i = 0; i < rowCount(); i++) {
0186         _map[item(i, 1)->text()] = item(i, 2)->text();
0187     }
0188 }
0189 
0190 void KWMapEditor::addEntry()
0191 {
0192     int x = rowCount();
0193     insertRow(x);
0194     auto b = new QToolButton(this);
0195     b->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0196     b->setToolTip(i18n("Delete Entry"));
0197     connect(b, &QToolButton::clicked, this, &KWMapEditor::erase);
0198     setCellWidget(x, 0, b);
0199     setItem(x, 1, new QTableWidgetItem());
0200     setItem(x, 2, new QTableWidgetItem());
0201     scrollToItem(item(x, 1));
0202     setCurrentCell(x, 1);
0203     Q_EMIT dirty();
0204 }
0205 
0206 void KWMapEditor::emitDirty()
0207 {
0208     Q_EMIT dirty();
0209 }
0210 
0211 void KWMapEditor::contextMenu(const QPoint &pos)
0212 {
0213     QTableWidgetItem *twi = itemAt(pos);
0214     _contextRow = row(twi);
0215     auto m = new QMenu(this);
0216     m->addAction(i18n("&New Entry"), this, &KWMapEditor::addEntry);
0217     m->addAction(_copyAct);
0218     m->exec(mapToGlobal(pos));
0219     delete m;
0220 }
0221 
0222 void KWMapEditor::copy()
0223 {
0224     QTableWidgetItem *twi = item(_contextRow, 2);
0225     if (twi) {
0226         QApplication::clipboard()->setText(twi->text());
0227     }
0228 }
0229 
0230 #include "moc_kwmapeditor.cpp"