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"