Pimcommon

templatelistwidget.cpp
1/*
2 SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "templatelistwidget.h"
8using namespace Qt::Literals::StringLiterals;
9
10#include "templateeditdialog.h"
11
12#include <KAuthorized>
13#include <KConfigGroup>
14#include <KLocalizedString>
15#include <KMessageBox>
16#include <KNSWidgets/Action>
17#include <KSharedConfig>
18#include <QIcon>
19#include <QMenu>
20
21#include <QDropEvent>
22#include <QFileDialog>
23#include <QListWidgetItem>
24#include <QMimeData>
25#include <QPointer>
26
27namespace PimCommon
28{
29class TemplateListWidgetPrivate
30{
31public:
32 TemplateListWidgetPrivate(const QString &configName, TemplateListWidget *qq)
33 : config(KSharedConfig::openConfig(configName, KConfig::NoGlobals))
34 , q(qq)
35 {
36 }
37
38 ~TemplateListWidgetPrivate()
39 {
40 save();
41 }
42
43 void createListWidgetItem(const QString &name, const QString &text, bool isDefaultTemplate)
44 {
45 auto item = new QListWidgetItem(name, q);
46 item->setData(TemplateListWidget::Text, text);
47 item->setData(TemplateListWidget::DefaultTemplate, isDefaultTemplate);
48 if (isDefaultTemplate) {
49 item->setIcon(QIcon::fromTheme(QStringLiteral("lock")));
50 }
51 q->setCurrentItem(item);
52 }
53
54 void slotAdd()
55 {
56 QString templateName;
57 QString templateScript;
58 if (q->addNewTemplate(templateName, templateScript)) {
59 createListWidgetItem(templateName, templateScript, false);
60 dirty = true;
61 }
62 }
63
64 void slotInsertNewTemplate(const QString &newTemplateScript)
65 {
66 QString templateName;
67 QString templateScript = newTemplateScript;
68 if (q->modifyTemplate(templateName, templateScript, false)) {
69 createListWidgetItem(templateName, templateScript, false);
70 dirty = true;
71 }
72 }
73
74 void slotRemove()
75 {
76 const int answer = KMessageBox::questionTwoActions(q,
77 i18n("Do you want to delete selected template?"),
78 i18nc("@title:window", "Delete template"),
81 if (answer == KMessageBox::ButtonCode::PrimaryAction) {
82 const QList<QListWidgetItem *> lstSelectedItems = q->selectedItems();
83 for (QListWidgetItem *item : lstSelectedItems) {
84 if (item->data(TemplateListWidget::DefaultTemplate).toBool() == false) {
85 delete item;
86 }
87 }
88 dirty = true;
89 }
90 }
91
92 void slotModify()
93 {
94 QListWidgetItem *item = q->currentItem();
95 if (item) {
96 const bool defaultTemplate = item->data(TemplateListWidget::DefaultTemplate).toBool();
97
98 QString templateName = item->text();
99 QString templateScript = item->data(TemplateListWidget::Text).toString();
100 if (q->modifyTemplate(templateName, templateScript, defaultTemplate)) {
101 if (!defaultTemplate) {
102 item->setText(templateName);
103 item->setData(TemplateListWidget::Text, templateScript);
104 dirty = true;
105 }
106 }
107 }
108 }
109
110 void slotInsertTemplate()
111 {
112 QListWidgetItem *item = q->currentItem();
113 if (item) {
114 const QString templateScript = item->data(TemplateListWidget::Text).toString();
115 Q_EMIT q->insertTemplate(templateScript);
116 }
117 }
118
119 void slotDuplicate()
120 {
121 QListWidgetItem *item = q->currentItem();
122 if (item) {
124 name.reserve(q->count());
125 for (int i = 0; i < q->count(); ++i) {
126 name.append(q->item(i)->text());
127 }
128 QString templateName = item->text() + " (%1)"_L1;
129 QString newName;
130 int i = 1;
131 do {
132 newName = templateName.arg(i);
133 i++;
134 } while (name.contains(newName));
135
136 const QString templateScript = item->data(TemplateListWidget::Text).toString();
137 createListWidgetItem(newName, templateScript, false);
138 dirty = true;
139 slotModify();
140 }
141 }
142
143 void slotImportTemplates()
144 {
145 const QString templateFile = QFileDialog::getOpenFileName(q, i18n("Import Template"));
146 if (!templateFile.isEmpty()) {
147 KConfig conf(templateFile, KConfig::SimpleConfig);
148 loadTemplates(&conf);
149 }
150 }
151
152 void slotExportTemplates()
153 {
154 const QString templateFile = QFileDialog::getSaveFileName(q, i18n("Export Template"));
155 if (!templateFile.isEmpty()) {
156 KConfig conf(templateFile, KConfig::SimpleConfig);
157 saveTemplates(&conf);
158 }
159 }
160
161 void slotContextMenu(const QPoint &pos)
162 {
163 const QList<QListWidgetItem *> lstSelectedItems = q->selectedItems();
164 const bool listSelectedIsEmpty = lstSelectedItems.isEmpty();
165 auto menu = new QMenu(q);
166
167 if (!listSelectedIsEmpty) {
168 menu->addAction(i18n("Insert template"), q, [this]() {
169 slotInsertTemplate();
170 });
171 menu->addSeparator();
172 }
173
174 menu->addAction(i18n("Add..."), q, [this]() {
175 slotAdd();
176 });
177 if (!listSelectedIsEmpty) {
178 const bool defaultTemplate = lstSelectedItems.first()->data(TemplateListWidget::DefaultTemplate).toBool();
179 if (lstSelectedItems.count() == 1) {
180 menu->addAction(defaultTemplate ? i18n("Show...") : i18n("Modify..."), q, [this]() {
181 slotModify();
182 });
183 menu->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Duplicate"), q, [this]() {
184 slotDuplicate();
185 });
186 }
187 if (lstSelectedItems.count() == 1 && !defaultTemplate) {
188 menu->addSeparator();
189 menu->addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Remove"), q, [this]() {
190 slotRemove();
191 });
192 }
193 }
194 menu->addSeparator();
195 if (q->count() > 0) {
196 menu->addAction(i18n("Export..."), q, [this]() {
197 slotExportTemplates();
198 });
199 }
200 menu->addAction(i18n("Import..."), q, [this]() {
201 slotImportTemplates();
202 });
203
204 if (KAuthorized::authorize(QStringLiteral("ghns"))) {
205 if (!knewstuffConfigName.isEmpty()) {
206 menu->addSeparator();
207 auto fileGHNS = new KNSWidgets::Action(i18n("Download new Templates..."), knewstuffConfigName, q);
208 menu->addAction(fileGHNS);
209 }
210 }
211 menu->exec(q->mapToGlobal(pos));
212 delete menu;
213 }
214
215 void load()
216 {
217 q->clear();
218 const QList<PimCommon::defaultTemplate> templatesLst = q->defaultTemplates();
219 for (const PimCommon::defaultTemplate &tmp : templatesLst) {
220 createListWidgetItem(tmp.name, tmp.text, true);
221 }
222 KConfigGroup group = config.data()->group(QStringLiteral("template"));
223 if (group.hasKey(QStringLiteral("templateCount"))) {
224 loadTemplates(config.data());
225 }
226 dirty = false;
227 }
228
229 void loadTemplates(KConfig *configFile)
230 {
231 KConfigGroup group = configFile->group(QStringLiteral("template"));
232 if (group.hasKey(QStringLiteral("templateCount"))) {
233 const int numberTemplate = group.readEntry("templateCount", 0);
234 for (int i = 0; i < numberTemplate; ++i) {
235 KConfigGroup templateGroup = configFile->group(QStringLiteral("templateDefine_%1").arg(i));
236 const QString name = templateGroup.readEntry("Name", QString());
237 const QString text = templateGroup.readEntry("Text", QString());
238
239 createListWidgetItem(name, text, false);
240 }
241 } else {
242 KMessageBox::error(q, i18n("\'%1\' is not a template file", configFile->name()), i18n("Load Template"));
243 }
244 }
245
246 void saveTemplates(KConfig *configFile)
247 {
248 // clear everything
249 const QStringList lst = configFile->groupList();
250 for (const QString &group : lst) {
251 configFile->deleteGroup(group);
252 }
253 int numberOfTemplate = 0;
254 for (int i = 0; i < q->count(); ++i) {
255 QListWidgetItem *templateItem = q->item(i);
256 if (templateItem->data(TemplateListWidget::DefaultTemplate).toBool() == false) {
257 KConfigGroup group = configFile->group(QStringLiteral("templateDefine_%1").arg(numberOfTemplate));
258 group.writeEntry("Name", templateItem->text());
259 group.writeEntry("Text", templateItem->data(TemplateListWidget::Text));
260 ++numberOfTemplate;
261 }
262 }
263 KConfigGroup group = configFile->group(QStringLiteral("template"));
264 group.writeEntry("templateCount", numberOfTemplate);
265 configFile->sync();
266 }
267
268 void save()
269 {
270 if (!dirty) {
271 return;
272 }
273
274 saveTemplates(config.data());
275 dirty = false;
276 }
277
278 QString knewstuffConfigName;
279 bool dirty = false;
280 KSharedConfig::Ptr config;
281 TemplateListWidget *const q;
282};
283
284TemplateListWidget::TemplateListWidget(const QString &configName, QWidget *parent)
285 : QListWidget(parent)
286 , d(new TemplateListWidgetPrivate(configName, this))
287{
288 setContextMenuPolicy(Qt::CustomContextMenu);
289 setDragDropMode(QAbstractItemView::DragDrop);
290
291 connect(this, &TemplateListWidget::customContextMenuRequested, this, [this](QPoint p) {
292 d->slotContextMenu(p);
293 });
294 connect(this, &TemplateListWidget::doubleClicked, this, [this]() {
295 d->slotModify();
296 });
297 connect(this, &TemplateListWidget::insertNewTemplate, this, [this](const QString &tmp) {
298 d->slotInsertNewTemplate(tmp);
299 });
300}
301
302TemplateListWidget::~TemplateListWidget() = default;
303
304void TemplateListWidget::loadTemplates()
305{
306 d->load();
307}
308
309QList<defaultTemplate> TemplateListWidget::defaultTemplates()
310{
311 return {};
312}
313
314QStringList TemplateListWidget::mimeTypes() const
315{
316 const QStringList lst{QStringLiteral("text/plain")};
317 return lst;
318}
319
320QMimeData *TemplateListWidget::mimeData(const QList<QListWidgetItem *> &items) const
321{
322 if (items.isEmpty()) {
323 return nullptr;
324 }
325 auto mimeData = new QMimeData();
326 QListWidgetItem *item = items.first();
327 mimeData->setText(item->data(TemplateListWidget::Text).toString());
328 return mimeData;
329}
330
331bool TemplateListWidget::addNewTemplate(QString &templateName, QString &templateScript)
332{
333 QPointer<TemplateEditDialog> dlg = new TemplateEditDialog(this);
334 bool result = false;
335 if (dlg->exec()) {
336 templateName = dlg->templateName();
337 templateScript = dlg->script();
338 result = true;
339 }
340 delete dlg;
341 return result;
342}
343
344bool TemplateListWidget::modifyTemplate(QString &templateName, QString &templateScript, bool defaultTemplate)
345{
346 QPointer<TemplateEditDialog> dlg = new TemplateEditDialog(this, defaultTemplate);
347 dlg->setTemplateName(templateName);
348 dlg->setScript(templateScript);
349 bool result = false;
350 if (dlg->exec()) {
351 if (!defaultTemplate) {
352 templateName = dlg->templateName();
353 templateScript = dlg->script();
354 }
355 result = true;
356 }
357 delete dlg;
358 return result;
359}
360
361void TemplateListWidget::dropEvent(QDropEvent *event)
362{
363 if (event->source() == this) {
364 event->ignore();
365 return;
366 }
367 if (event->mimeData()->hasText()) {
368 event->setDropAction(Qt::CopyAction);
369 Q_EMIT insertNewTemplate(event->mimeData()->text());
370 event->accept();
371 }
373}
374
375void TemplateListWidget::setKNewStuffConfigFile(const QString &configName)
376{
377 d->knewstuffConfigName = configName;
378}
379
380void TemplateListWidget::addDefaultTemplate(const QString &templateName, const QString &templateScript)
381{
382 d->createListWidgetItem(templateName, templateScript, true);
383}
384}
385
386#include "moc_templatelistwidget.cpp"
static Q_INVOKABLE bool authorize(const QString &action)
void deleteGroup(const QString &group, WriteConfigFlags flags=Normal)
KConfigGroup group(const QString &group)
bool hasKey(const char *key) const
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
bool sync() override
QString name() const
QStringList groupList() const override
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
ButtonCode questionTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Notify)
QString name(StandardAction id)
KGuiItem cancel()
KGuiItem del()
folderdialogacltab.h
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QIcon fromTheme(const QString &name)
qsizetype count() const const
T & first()
bool isEmpty() const const
void clear()
QListWidgetItem * currentItem() const const
virtual void dropEvent(QDropEvent *event) override
QListWidgetItem * item(int row) const const
QList< QListWidgetItem * > selectedItems() const const
void setCurrentItem(QListWidgetItem *item)
virtual QVariant data(int role) const const
virtual void setData(int role, const QVariant &value)
void setText(const QString &text)
QString text() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
void reserve(qsizetype size)
CustomContextMenu
CopyAction
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool toBool() const const
QString toString() const const
QPoint mapToGlobal(const QPoint &pos) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:14:35 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.