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

KDE's Doxygen guidelines are available online.