Krita Source Code Documentation
Loading...
Searching...
No Matches
KisTemplateCreateDia.cpp
Go to the documentation of this file.
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
4 SPDX-FileCopyrightText: 2000 Werner Trobin <trobin@kde.org>
5 SPDX-FileCopyrightText: 2004 Nicolas GOUTTE <goutte@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
11
12#include <QFile>
13#include <QLabel>
14#include <QRadioButton>
15#include <QPushButton>
16#include <QCheckBox>
17#include <QVBoxLayout>
18#include <QPixmap>
19#include <QHBoxLayout>
20#include <QTreeWidget>
21#include <QTreeWidgetItem>
22#include <QGroupBox>
23#include <QInputDialog>
24#include <QTemporaryFile>
25#include <QLineEdit>
26#include <QDir>
27
28#include <klocalizedstring.h>
29#include <kis_icon.h>
30#include <KisDocument.h>
31#include <KisTemplates.h>
32#include <KisTemplateTree.h>
33#include <KisTemplateGroup.h>
34#include <KisTemplate.h>
35#include <QMessageBox>
36#include <KoResourcePaths.h>
37#include <kis_debug.h>
38#include <kconfiggroup.h>
39#include <QUrl>
40#include <KoFileDialog.h>
41
42#include <ksharedconfig.h>
43
44// ODF thumbnail extent
45static const int thumbnailExtent = 128;
46
48public:
49 KisTemplateCreateDiaPrivate(const QString &templatesResourcePath,
50 const QString &filePath, const QPixmap &thumbnail)
51 : m_tree(templatesResourcePath, true)
52 , m_filePath(filePath)
53 , m_thumbnail(thumbnail)
54 { }
55
57 QLineEdit *m_name {nullptr};
58 QRadioButton *m_default {nullptr};
59 QRadioButton *m_custom {nullptr};
60 QPushButton *m_select {nullptr};
61 QLabel *m_preview {nullptr};
62 QString m_customFile;
64 QTreeWidget *m_groups {nullptr};
65 QPushButton *m_add {nullptr};
66 QPushButton *m_remove {nullptr};
67 QCheckBox *m_defaultTemplate {nullptr};
68 QString m_filePath;
69 QPixmap m_thumbnail;
70 bool m_changed {false};
71};
72
73
74/****************************************************************************
75 *
76 * Class: KisTemplateCreateDia
77 *
78 ****************************************************************************/
79
80KisTemplateCreateDia::KisTemplateCreateDia(const QString &templatesResourcePath,
81 const QString &filePath, const QPixmap &thumbnail, QWidget *parent)
82 : KoDialog(parent)
83 , d(new KisTemplateCreateDiaPrivate(templatesResourcePath, filePath, thumbnail))
84{
87 setCaption( i18n( "Create Template" ) );
88 setModal( true );
89 setObjectName( "template create dia" );
90
91 QWidget *mainwidget = mainWidget();
92 QHBoxLayout *mbox=new QHBoxLayout( mainwidget );
93 QVBoxLayout* leftbox = new QVBoxLayout();
94 mbox->addLayout( leftbox );
95
96 QLabel *label=new QLabel(i18nc("Template name", "Name:"), mainwidget);
97 QHBoxLayout *namefield=new QHBoxLayout();
98 leftbox->addLayout( namefield );
99 namefield->addWidget(label);
100 d->m_name=new QLineEdit(mainwidget);
101 d->m_name->setFocus();
102 connect(d->m_name, SIGNAL(textChanged(QString)),
103 this, SLOT(slotNameChanged(QString)));
104 namefield->addWidget(d->m_name);
105
106 label=new QLabel(i18nc("Group as in Template Group", "Group:"), mainwidget);
107 leftbox->addWidget(label);
108 d->m_groups = new QTreeWidget(mainwidget);
109 leftbox->addWidget(d->m_groups);
110 d->m_groups->setColumnCount(1);
111 d->m_groups->setHeaderHidden(true);
112 d->m_groups->setRootIsDecorated(true);
113 d->m_groups->setSortingEnabled(true);
114
116 d->m_groups->sortItems(0, Qt::AscendingOrder);
117
118 QHBoxLayout *bbox=new QHBoxLayout();
119 leftbox->addLayout( bbox );
120 d->m_add=new QPushButton(i18nc("Group as in Template Group", "&Add Group..."), mainwidget);
121 connect(d->m_add, SIGNAL(clicked()), this, SLOT(slotAddGroup()));
122 bbox->addWidget(d->m_add);
123 d->m_remove=new QPushButton(i18n("&Remove"), mainwidget);
124 connect(d->m_remove, SIGNAL(clicked()), this, SLOT(slotRemove()));
125 bbox->addWidget(d->m_remove);
126
127 QVBoxLayout *rightbox=new QVBoxLayout();
128 mbox->addLayout( rightbox );
129 QGroupBox *pixbox = new QGroupBox(i18n("Picture"), mainwidget);
130 rightbox->addWidget(pixbox);
131 QVBoxLayout *pixlayout=new QVBoxLayout(pixbox );
132 d->m_default=new QRadioButton(i18n("&Preview"), pixbox);
133 d->m_default->setChecked(true);
134 connect(d->m_default, SIGNAL(clicked()), this, SLOT(slotDefault()));
135 pixlayout->addWidget(d->m_default);
136 QHBoxLayout *custombox=new QHBoxLayout();
137 d->m_custom=new QRadioButton(i18n("Custom:"), pixbox);
138 d->m_custom->setChecked(false);
139 connect(d->m_custom, SIGNAL(clicked()), this, SLOT(slotCustom()));
140 custombox->addWidget(d->m_custom);
141 d->m_select=new QPushButton(i18n("&Select..."), pixbox);
142 connect(d->m_select, SIGNAL(clicked()), this, SLOT(slotSelect()));
143 custombox->addWidget(d->m_select);
144 custombox->addStretch(1);
145 pixlayout->addLayout(custombox);
146 d->m_preview=new QLabel(pixbox); // setPixmap() -> auto resize?
147 pixlayout->addWidget(d->m_preview, 0, Qt::AlignCenter);
148 pixlayout->addStretch(1);
149
150 d->m_defaultTemplate = new QCheckBox( i18n("Use the new template as default"), mainwidget );
151 d->m_defaultTemplate->setChecked( true );
152 d->m_defaultTemplate->setVisible( false );
153 d->m_defaultTemplate->setToolTip(i18n("Use the new template every time Krita starts"));
154 rightbox->addWidget( d->m_defaultTemplate );
155
156 enableButtonOk(false);
157 d->m_changed=false;
158 updatePixmap();
159
160 connect(d->m_groups, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()));
161
162 d->m_remove->setEnabled(d->m_groups->currentItem());
163 connect(this, SIGNAL(okClicked()), this, SLOT(slotOk()));
164}
165
169
171{
172 const QTreeWidgetItem* item = d->m_groups->currentItem();
173 d->m_remove->setEnabled( item );
174 if ( ! item )
175 return;
176
177 if ( item->parent() != 0 )
178 {
179 d->m_name->setText( item->text( 0 ) );
180 }
181}
182
183void KisTemplateCreateDia::createTemplate(const QString &templatesResourcePath,
184 const char *suffix,
185 KisDocument *document, QWidget *parent)
186{
187 Q_UNUSED(suffix);
188 QString fileName;
189 {
190 QTemporaryFile tempFile;
191 if (!tempFile.open()) {
192 qWarning("Creation of temporary file to store template failed.");
193 return;
194 }
195 fileName = tempFile.fileName();
196 }
197
198 bool retval = document->exportDocumentSync(fileName, KisDocument::nativeFormatMimeType());
199 if (!retval) {
200 qWarning("Could not save template");
201 return;
202 }
203 const QPixmap thumbnail = document->generatePreview(QSize(thumbnailExtent, thumbnailExtent));
204 KisTemplateCreateDia *dia = new KisTemplateCreateDia(templatesResourcePath, fileName, thumbnail, parent);
205 dia->exec();
206 delete dia;
207
208 QDir d;
209 d.remove(fileName);
210}
211
212static void saveAsQuadraticPng(const QPixmap &pixmap, const QString &fileName)
213{
214 QImage icon = pixmap.toImage();
215 icon.convertTo(QImage::Format_ARGB32);
216 const int iconExtent = qMax(icon.width(), icon.height());
217 icon = icon.copy((icon.width() - iconExtent) / 2, (icon.height() - iconExtent) / 2, iconExtent, iconExtent);
218 icon.save(fileName, "PNG");
219}
220
222
223 // get the current item, if there is one...
224 QTreeWidgetItem *item = d->m_groups->currentItem();
225 if (!item)
226 item = d->m_groups->topLevelItem(0);
227 if (!item) { // safe :)
230 return;
231 }
232 // is it a group or a template? anyway - get the group :)
233 if (item->parent() != 0)
234 item=item->parent();
235 if (!item) { // *very* safe :P
238 return;
239 }
240
241 KisTemplateGroup *group=d->m_tree.find(item->text(0));
242 if (!group) { // even safer
245 return;
246 }
247
248 if (d->m_name->text().isEmpty()) {
251 return;
252 }
253
254 // copy the tmp file and the picture the app provides
256 dir += group->name();
257 QString templateDir = dir+"/.source/";
258 QString iconDir = dir+"/.icon/";
259
260 QString file = KisTemplates::trimmed(d->m_name->text());
261 QString tmpIcon = ".icon/"+file;
262 tmpIcon += ".png";
263 QString icon=iconDir+file;
264 icon += ".png";
265
266 QString ext = ".kra";
267
268 QString dest = templateDir + file + ext;
269 if (QFile::exists(dest)) {
270 do {
271 file = file.prepend( '_' );
272 dest = templateDir + file + ext;
273 tmpIcon=".icon/" + file + ".png";
274 icon=iconDir + file + ".png";
275 }
276 while (QFile(dest).exists());
277 }
278 bool ignore = false;
279 KisTemplate *t = new KisTemplate(d->m_name->text(), QString(), ".source/"+ file + ext, tmpIcon, "", "", false, true);
280 if (!group->add(t)) {
281 KisTemplate *existingTemplate=group->find(d->m_name->text());
282 if (existingTemplate && !existingTemplate->isHidden()) {
283 if (QMessageBox::warning(this,
284 i18nc("@title:window", "Krita"),
285 i18n("Do you really want to overwrite the existing '%1' template?", existingTemplate->name()),
286 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
287 group->add(t, true);
288 }
289 else {
290 delete t;
291 return;
292 }
293 }
294 else {
295 ignore = true;
296 }
297 }
298
299 QDir path;
300 if (!path.mkpath(templateDir) || !path.mkpath(iconDir)) {
303 return;
304 }
305
306 QString orig;
307 orig = d->m_filePath;
308 // don't overwrite the hidden template file with a new non-hidden one
309 if (!ignore) {
310 if (!QFile::copy(d->m_filePath, dest)) {
311 qWarning() << "Could not copy" << d->m_filePath << "to" << dest;
312 }
313
314 // save the picture as icon
315 if (d->m_default->isChecked() && !d->m_thumbnail.isNull()) {
317 } else if (!d->m_customPixmap.isNull()) {
319 } else {
320 warnUI << "Could not save the preview picture!";
321 }
322 }
323
324 // if there's a .directory file, we copy this one, too
325 bool ready=false;
326 QStringList tmp=group->dirs();
327 for(QStringList::ConstIterator it=tmp.constBegin(); it!=tmp.constEnd() && !ready; ++it) {
328 if ((*it).contains(dir)==0) {
329 orig = (*it) + ".directory";
330 // Check if we can read the file
331 if (QFile(orig).exists()) {
332 dest = dir + "/.directory";
333 // We copy the file with overwrite
334 if (!QFile(orig).copy(dest)) {
335 warnKrita << "Failed to copy from" << orig << "to" << dest;
336 }
337 ready = true;
338 }
339 }
340 }
341
343
344 if ( d->m_defaultTemplate->isChecked() )
345 {
346
347 KConfigGroup grp( KSharedConfig::openConfig(), "TemplateChooserDialog");
348 grp.writeEntry( "LastReturnType", "Template" );
349 grp.writePathEntry( "FullTemplateName", dir + '/' + t->file() );
350 grp.writePathEntry( "AlwaysUseTemplate", dir + '/' + t->file() );
351 }
352}
353
355
356 d->m_default->setChecked(true);
357 d->m_custom->setChecked(false);
358 updatePixmap();
359}
360
362
363 d->m_default->setChecked(false);
364 d->m_custom->setChecked(true);
365 if (d->m_customFile.isEmpty())
366 slotSelect();
367 else
368 updatePixmap();
369}
370
372
373 d->m_default->setChecked(false);
374 d->m_custom->setChecked(true);
375
376 KoFileDialog dlg(this, KoFileDialog::OpenFile, "TemplateImages");
377 dlg.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
378 dlg.setImageFilters();
379 dlg.setCaption(i18n("Select an image"));
380 QString fn = dlg.filename();
381 if (fn.isEmpty()) {
382 if (d->m_customFile.isEmpty()) {
383 d->m_default->setChecked(true);
384 d->m_custom->setChecked(false);
385 }
386 return;
387 }
388 QImage image(fn);
389 if (image.isNull()) {
390 QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn));
391 }
392 d->m_customFile = fn;
393 d->m_customPixmap = QPixmap();
394 updatePixmap();
395}
396
397void KisTemplateCreateDia::slotNameChanged(const QString &name) {
398
399 if ( ( name.trimmed().isEmpty() || !d->m_groups->topLevelItem(0) ) && !d->m_changed )
400 enableButtonOk(false);
401 else
402 enableButtonOk(true);
403}
404
406
407 const QString name = QInputDialog::getText(this, i18nc("Group as in Template Group", "Add Group"), i18nc("Group as in Template Group", "Enter group name:"));
408 KisTemplateGroup *group = d->m_tree.find(name);
409 if (group && !group->isHidden()) {
410 QMessageBox::information( this, i18n("This name has already been used."), i18nc("Group as in Template Group", "Add Group") );
411 return;
412 }
414 dir+=name;
415 KisTemplateGroup *newGroup=new KisTemplateGroup(name, dir, 0, true);
416 d->m_tree.add(newGroup);
417 QTreeWidgetItem *item = new QTreeWidgetItem(d->m_groups, QStringList() << name);
418 d->m_groups->setCurrentItem(item);
419 d->m_groups->sortItems(0, Qt::AscendingOrder);
420 d->m_name->setFocus();
421 enableButtonOk(true);
422 d->m_changed=true;
423}
424
426
427 QTreeWidgetItem *item = d->m_groups->currentItem();
428 if (!item)
429 return;
430
431 QString what;
432 QString removed;
433 if (item->parent() == 0) {
434 what = i18nc("Group as in Template Group", "Do you really want to remove that group?");
435 removed = i18nc("@title:window", "Remove Group");
436 } else {
437 what = i18n("Do you really want to remove that template?");
438 removed = i18nc("@title:window", "Remove Template");
439 }
440
441 if (QMessageBox::warning(this,
442 removed,
443 what,
444 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox:: No) {
445 d->m_name->setFocus();
446 return;
447 }
448
449 if (item->parent() == 0) {
450 KisTemplateGroup *group=d->m_tree.find(item->text(0));
451 if (group)
452 group->setHidden(true);
453 }
454 else {
455 bool done=false;
457 QList<KisTemplateGroup*>::const_iterator it = groups.constBegin();
458 for(; it != groups.constEnd() && !done; ++it) {
459 KisTemplate *t = (*it)->find(item->text(0));
460
461 if (t) {
462 t->setHidden(true);
463 done=true;
464 }
465 }
466 }
467 delete item;
468 item=0;
469 d->m_name->setFocus();
470 d->m_changed=true;
472}
473
475
476 if (d->m_default->isChecked() && !d->m_thumbnail.isNull()) {
477 d->m_preview->setPixmap(d->m_thumbnail);
478 }
479 else if (d->m_custom->isChecked() && !d->m_customFile.isEmpty()) {
480
481 if (d->m_customPixmap.isNull()) {
482 dbgUI <<"Trying to load picture" << d->m_customFile;
483 // use the code in KisTemplate to load the image... hacky, I know :)
484 KisTemplate t("foo", "bar", QString(), d->m_customFile);
486 }
487 else {
488 warnUI << "Trying to load picture";
489 }
490
491 if (!d->m_customPixmap.isNull()) {
492 d->m_preview->setPixmap(d->m_customPixmap);
493 }
494 else {
495 d->m_preview->setText(i18n("Could not load picture."));
496 }
497 }
498 else {
499 d->m_preview->setText(i18n("No picture available."));
500 }
501}
502
504
505 Q_FOREACH (KisTemplateGroup *group, d->m_tree.groups()) {
506 if (group->isHidden())
507 continue;
508 QTreeWidgetItem *groupItem=new QTreeWidgetItem(d->m_groups, QStringList() << group->name());
509
510 Q_FOREACH (KisTemplate *t, group->templates()) {
511 if (t->isHidden())
512 continue;
513 (void)new QTreeWidgetItem(groupItem, QStringList() << t->name());
514 }
515 }
516}
QList< QString > QStringList
static void saveAsQuadraticPng(const QPixmap &pixmap, const QString &fileName)
static const int thumbnailExtent
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
static QByteArray nativeFormatMimeType()
KisTemplateCreateDiaPrivate(const QString &templatesResourcePath, const QString &filePath, const QPixmap &thumbnail)
KisTemplateCreateDiaPrivate *const d
KisTemplateCreateDia(const QString &templatesResourcePath, const QString &filePath, const QPixmap &thumbnail, QWidget *parent=0)
void slotNameChanged(const QString &name)
static void createTemplate(const QString &templatesResourcePath, const char *suffix, KisDocument *document, QWidget *parent=0)
QString name() const
KisTemplate * find(const QString &name) const
QStringList dirs() const
QList< KisTemplate * > templates() const
bool isHidden() const
If all children are hidden, we are hidden too.
bool add(KisTemplate *t, bool force=false, bool touch=true)
void setHidden(bool hidden=true) const
if we should hide, we hide all the children
QString templatesResourcePath() const
bool add(KisTemplateGroup *g)
KisTemplateGroup * find(const QString &name) const
QList< KisTemplateGroup * > groups() const
void setHidden(bool hidden=true)
Definition KisTemplate.h:48
QString name() const
Definition KisTemplate.h:28
const QPixmap & loadPicture()
QString file() const
Definition KisTemplate.h:34
bool isHidden() const
Definition KisTemplate.h:45
A dialog base class with standard buttons and predefined layouts.
Definition KoDialog.h:116
QWidget * mainWidget()
Definition KoDialog.cpp:368
virtual void slotButtonClicked(int button)
Definition KoDialog.cpp:820
void enableButtonOk(bool state)
Definition KoDialog.cpp:615
virtual void setCaption(const QString &caption)
Definition KoDialog.cpp:498
void setButtons(ButtonCodes buttonMask)
Definition KoDialog.cpp:195
void setDefaultButton(ButtonCode id)
Definition KoDialog.cpp:302
@ Ok
Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
Definition KoDialog.h:127
@ Cancel
Show Cancel-button. (this button reject()s the dialog; result set to QDialog::Rejected)
Definition KoDialog.h:130
void okClicked()
void setImageFilters()
setImageFilters sets the name filters for the file dialog to all image formats Qt's QImageReader supp...
QString filename()
Get the file name the user selected in the file dialog.
void setDefaultDir(const QString &defaultDir, bool force=false)
setDefaultDir set the default directory to defaultDir.
void setCaption(const QString &caption)
static QString saveLocation(const QString &type, const QString &suffix=QString(), bool create=true)
#define warnKrita
Definition kis_debug.h:87
#define dbgUI
Definition kis_debug.h:52
#define warnUI
Definition kis_debug.h:94
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
QString trimmed(const QString &string)