Warning, file /office/calligra/libs/main/KoTemplateCreateDia.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002    This file is part of the KDE project
0003    Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
0004                  2000 Werner Trobin <trobin@kde.org>
0005    Copyright (C) 2004 Nicolas GOUTTE <goutte@kde.org>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include <KoTemplateCreateDia.h>
0024 
0025 #include <QFile>
0026 #include <QDir>
0027 #include <QLabel>
0028 #include <QRadioButton>
0029 #include <QPushButton>
0030 #include <QCheckBox>
0031 #include <QVBoxLayout>
0032 #include <QPixmap>
0033 #include <QHBoxLayout>
0034 #include <QTreeWidget>
0035 #include <QTreeWidgetItem>
0036 #include <QGroupBox>
0037 #include <QStandardPaths>
0038 #include <QApplication>
0039 #include <QInputDialog>
0040 #include <QUrl>
0041 #include <QTemporaryFile>
0042 #include <QLineEdit>
0043 
0044 #include <KoIcon.h>
0045 #include <KoDocument.h>
0046 #include <KoTemplates.h>
0047 #include <KoTemplateTree.h>
0048 #include <KoTemplateGroup.h>
0049 #include <KoTemplate.h>
0050 #include <KoResourcePaths.h>
0051 
0052 #include <klocalizedstring.h>
0053 #include <kicondialog.h>
0054 #include <kmessagebox.h>
0055 #include <MainDebug.h>
0056 #include <KoNetAccess.h>
0057 #include <kiconloader.h>
0058 #include <kconfiggroup.h>
0059 #include <ksharedconfig.h>
0060 #include <kio/job.h>
0061 #include <kstandardguiitem.h>
0062 
0063 // ODF thumbnail extent
0064 static const int thumbnailExtent = 128;
0065 
0066 class KoTemplateCreateDiaPrivate {
0067 public:
0068     KoTemplateCreateDiaPrivate(const QString &filePath, const QPixmap &thumbnail)
0069          : m_filePath(filePath)
0070          , m_thumbnail(thumbnail)
0071     {
0072         m_tree=0;
0073         m_name=0;
0074         m_default=0;
0075         m_custom=0;
0076         m_select=0;
0077         m_preview=0;
0078         m_groups=0;
0079         m_add=0;
0080         m_remove=0;
0081         m_defaultTemplate=0;
0082     }
0083     ~KoTemplateCreateDiaPrivate() {
0084         delete m_tree;
0085     }
0086 
0087     KoTemplateTree *m_tree;
0088     QLineEdit *m_name;
0089     QRadioButton *m_default, *m_custom;
0090     QPushButton *m_select;
0091     QLabel *m_preview;
0092     QString m_customFile;
0093     QPixmap m_customPixmap;
0094     QTreeWidget *m_groups;
0095     QPushButton *m_add, *m_remove;
0096     QCheckBox *m_defaultTemplate;
0097     QString m_filePath;
0098     QPixmap m_thumbnail;
0099     bool m_changed;
0100 };
0101 
0102 
0103 /****************************************************************************
0104  *
0105  * Class: koTemplateCreateDia
0106  *
0107  ****************************************************************************/
0108 
0109 KoTemplateCreateDia::KoTemplateCreateDia(const QString &templatesResourcePath,
0110                                          const QString &filePath, const QPixmap &thumbnail, QWidget *parent)
0111   : KoDialog(parent)
0112   , d(new KoTemplateCreateDiaPrivate(filePath, thumbnail))
0113 {
0114 
0115     setButtons( KoDialog::Ok|KoDialog::Cancel );
0116     setDefaultButton( KoDialog::Ok );
0117     setCaption( i18n( "Create Template" ) );
0118     setModal( true );
0119     setObjectName( "template create dia" );
0120 
0121     QWidget *mainwidget=mainWidget();
0122     QHBoxLayout *mbox=new QHBoxLayout( mainwidget );
0123     QVBoxLayout* leftbox = new QVBoxLayout();
0124     mbox->addLayout( leftbox );
0125 
0126     QLabel *label=new QLabel(i18nc("Template name", "Name:"), mainwidget);
0127     QHBoxLayout *namefield=new QHBoxLayout();
0128     leftbox->addLayout( namefield );
0129     namefield->addWidget(label);
0130     d->m_name=new QLineEdit(mainwidget);
0131     d->m_name->setFocus();
0132     connect(d->m_name, SIGNAL(textChanged(QString)),
0133             this, SLOT(slotNameChanged(QString)));
0134     namefield->addWidget(d->m_name);
0135 
0136     label=new QLabel(i18n("Group:"), mainwidget);
0137     leftbox->addWidget(label);
0138     d->m_groups = new QTreeWidget(mainwidget);
0139     leftbox->addWidget(d->m_groups);
0140     d->m_groups->setColumnCount(1);
0141     d->m_groups->setHeaderHidden(true);
0142     d->m_groups->setRootIsDecorated(true);
0143     d->m_groups->setSortingEnabled(true);
0144 
0145     d->m_tree = new KoTemplateTree(templatesResourcePath, true);
0146     fillGroupTree();
0147     d->m_groups->sortItems(0, Qt::AscendingOrder);
0148 
0149     QHBoxLayout *bbox=new QHBoxLayout();
0150     leftbox->addLayout( bbox );
0151     d->m_add=new QPushButton(i18n("&Add Group..."), mainwidget);
0152     connect(d->m_add, SIGNAL(clicked()), this, SLOT(slotAddGroup()));
0153     bbox->addWidget(d->m_add);
0154     d->m_remove=new QPushButton(i18n("&Remove"), mainwidget);
0155     connect(d->m_remove, SIGNAL(clicked()), this, SLOT(slotRemove()));
0156     bbox->addWidget(d->m_remove);
0157 
0158     QVBoxLayout *rightbox=new QVBoxLayout();
0159     mbox->addLayout( rightbox );
0160     QGroupBox *pixbox = new QGroupBox(i18n("Picture"), mainwidget);
0161     rightbox->addWidget(pixbox);
0162     QVBoxLayout *pixlayout=new QVBoxLayout(pixbox );
0163     d->m_default=new QRadioButton(i18n("&Preview"), pixbox);
0164     d->m_default->setChecked(true);
0165     connect(d->m_default, SIGNAL(clicked()), this, SLOT(slotDefault()));
0166     pixlayout->addWidget(d->m_default);
0167     QHBoxLayout *custombox=new QHBoxLayout();
0168     d->m_custom=new QRadioButton(i18n("Custom:"), pixbox);
0169     d->m_custom->setChecked(false);
0170     connect(d->m_custom, SIGNAL(clicked()), this, SLOT(slotCustom()));
0171     custombox->addWidget(d->m_custom);
0172     d->m_select=new QPushButton(i18n("&Select..."), pixbox);
0173     connect(d->m_select, SIGNAL(clicked()), this, SLOT(slotSelect()));
0174     custombox->addWidget(d->m_select);
0175     custombox->addStretch(1);
0176     pixlayout->addLayout(custombox);
0177     d->m_preview=new QLabel(pixbox); // setPixmap() -> auto resize?
0178     pixlayout->addWidget(d->m_preview, 0, Qt::AlignCenter);
0179     pixlayout->addStretch(1);
0180 
0181     d->m_defaultTemplate = new QCheckBox( i18n("Use the new template as default"), mainwidget );
0182     d->m_defaultTemplate->setChecked( true );
0183     // QT5TODO: think about restoring this
0184 //     d->m_defaultTemplate->setToolTip( i18n("Use the new template every time %1 starts",componentData.aboutData()->programName() ) );
0185     rightbox->addWidget( d->m_defaultTemplate );
0186 
0187     enableButtonOk(false);
0188     d->m_changed=false;
0189     updatePixmap();
0190 
0191     connect(d->m_groups, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()));
0192 
0193     d->m_remove->setEnabled(d->m_groups->currentItem());
0194     connect(this,SIGNAL(okClicked()),this,SLOT(slotOk()));
0195 }
0196 
0197 KoTemplateCreateDia::~KoTemplateCreateDia() {
0198     delete d;
0199 }
0200 
0201 void KoTemplateCreateDia::slotSelectionChanged()
0202 {
0203     const QTreeWidgetItem* item = d->m_groups->currentItem();
0204     d->m_remove->setEnabled( item );
0205     if ( ! item )
0206         return;
0207 
0208     if ( item->parent() != nullptr )
0209     {
0210         d->m_name->setText( item->text( 0 ) );
0211     }
0212 }
0213 
0214 void KoTemplateCreateDia::createTemplate(const QString &templatesResourcePath,
0215                                          const char *suffix,
0216                                          KoDocument *document, QWidget *parent)
0217 {
0218     QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/") + qAppName() + QLatin1String("_XXXXXX") + suffix);
0219     //Check that creation of temp file was successful
0220     if (!tempFile->open()) {
0221         delete tempFile;
0222         qWarning("Creation of temporary file to store template failed.");
0223         return;
0224     }
0225     const QString fileName = tempFile->fileName();
0226     tempFile->close(); // need to close on Windows before we can open it again to save
0227     delete tempFile; // now the file has disappeared and we can create a new file with the generated name
0228 
0229     document->saveNativeFormat(fileName);
0230 
0231     const QPixmap thumbnail = document->generatePreview(QSize(thumbnailExtent, thumbnailExtent));
0232 
0233     KoTemplateCreateDia *dia = new KoTemplateCreateDia(templatesResourcePath, fileName, thumbnail, parent);
0234     dia->exec();
0235     delete dia;
0236 
0237     QDir d;
0238     d.remove(fileName);
0239 }
0240 
0241 static void
0242 saveAsQuadraticPng(const QPixmap &pixmap, const QString &fileName)
0243 {
0244     QImage icon = pixmap.toImage();
0245     icon = icon.convertToFormat(QImage::Format_ARGB32);
0246     const int iconExtent = qMax(icon.width(), icon.height());
0247     icon = icon.copy((icon.width() - iconExtent) / 2, (icon.height() - iconExtent) / 2, iconExtent, iconExtent);
0248     icon.save(fileName, "PNG");
0249 }
0250 
0251 void KoTemplateCreateDia::slotOk() {
0252 
0253     // get the current item, if there is one...
0254     QTreeWidgetItem *item = d->m_groups->currentItem();
0255     if(!item)
0256         item = d->m_groups->topLevelItem(0);
0257     if(!item) {    // safe :)
0258         d->m_tree->writeTemplateTree();
0259         slotButtonClicked( KoDialog::Cancel );
0260         return;
0261     }
0262     // is it a group or a template? anyway - get the group :)
0263     if(item->parent() != nullptr)
0264         item=item->parent();
0265     if(!item) {    // *very* safe :P
0266         d->m_tree->writeTemplateTree();
0267         slotButtonClicked( KoDialog::Cancel );
0268         return;
0269     }
0270 
0271     KoTemplateGroup *group=d->m_tree->find(item->text(0));
0272     if(!group) {    // even safer
0273         d->m_tree->writeTemplateTree();
0274         slotButtonClicked( KoDialog::Cancel );
0275         return;
0276     }
0277 
0278     if(d->m_name->text().isEmpty()) {
0279         d->m_tree->writeTemplateTree();
0280         slotButtonClicked( KoDialog::Cancel );
0281         return;
0282     }
0283 
0284     // copy the tmp file and the picture the app provides
0285     QString dir = KoResourcePaths::saveLocation("data", d->m_tree->templatesResourcePath());
0286     dir+=group->name();
0287     QString templateDir=dir+"/.source/";
0288     QString iconDir=dir+"/.icon/";
0289 
0290     QString file=KoTemplates::trimmed(d->m_name->text());
0291     QString tmpIcon=".icon/"+file;
0292     tmpIcon+=".png";
0293     QString icon=iconDir+file;
0294     icon+=".png";
0295 
0296     // try to find the extension for the template file :P
0297     const int pos = d->m_filePath.lastIndexOf(QLatin1Char('.'));
0298     QString ext;
0299     if ( pos > -1 )
0300         ext = d->m_filePath.mid(pos);
0301     else
0302         qWarning(/*30004*/) << "Template extension not found!";
0303 
0304     QUrl dest;
0305     dest.setPath(templateDir+file+ext);
0306     if (QFile::exists( dest.toLocalFile())) {
0307         do {
0308             file.prepend( '_' );
0309             dest.setPath( templateDir + file + ext );
0310             tmpIcon=".icon/"+file+".png";
0311             icon=iconDir+file+".png";
0312         }
0313         while ( KIO::NetAccess::exists( dest, KIO::NetAccess::DestinationSide, this ) );
0314     }
0315     bool ignore = false;
0316     debugMain <<"Trying to create template:" << d->m_name->text() <<"URL=" <<".source/"+file+ext <<" ICON=" << tmpIcon;
0317     KoTemplate *t=new KoTemplate(d->m_name->text(), QString(), ".source/"+file+ext, tmpIcon, "", "", "", "", "", false, false, true);
0318     if(!group->add(t)) {
0319         KoTemplate *existingTemplate=group->find(d->m_name->text());
0320         if(existingTemplate && !existingTemplate->isHidden()) {
0321             if(KMessageBox::warningYesNo(this, i18n("Do you really want to overwrite"
0322                                                     " the existing '%1' template?",existingTemplate->name()))==KMessageBox::Yes)
0323                 group->add(t, true);
0324             else
0325             {
0326                 delete t;
0327                 return;
0328             }
0329         }
0330         else
0331             ignore = true;
0332     }
0333 
0334     QDir dummyDir;
0335     if(!dummyDir.mkpath(templateDir) || !dummyDir.mkpath(iconDir)) {
0336         d->m_tree->writeTemplateTree();
0337         slotButtonClicked( KoDialog::Cancel );
0338         return;
0339     }
0340 
0341     QUrl orig;
0342     orig.setPath(d->m_filePath);
0343     // don't overwrite the hidden template file with a new non-hidden one
0344     if ( !ignore )
0345     {
0346         QFile::copy(d->m_filePath, dest.path());
0347         // save the picture as icon
0348         // (needs to be square, otherwise KIconLoader dpes nasty changes)
0349         if(d->m_default->isChecked() && !d->m_thumbnail.isNull()) {
0350             saveAsQuadraticPng(d->m_thumbnail, icon);
0351         } else if(!d->m_customPixmap.isNull()) {
0352             saveAsQuadraticPng(d->m_customPixmap, icon);
0353         } else {
0354             qWarning(/*30004*/) << "Could not save the preview picture!";
0355         }
0356     }
0357 
0358     // if there's a .directory file, we copy this one, too
0359     bool ready=false;
0360     QStringList tmp=group->dirs();
0361     for(QStringList::ConstIterator it=tmp.constBegin(); it!=tmp.constEnd() && !ready; ++it) {
0362         if((*it).contains(dir)==0) {
0363             orig.setPath( (*it)+".directory" );
0364             // Check if we can read the file
0365             if( KIO::NetAccess::exists(orig, KIO::NetAccess::SourceSide, this) ) {
0366                 dest.setPath( dir+"/.directory" );
0367                 // We copy the file with overwrite
0368                 KIO::FileCopyJob *job = KIO::file_copy( orig, dest, -1, KIO::Overwrite | KIO::HideProgressInfo);
0369                 job->exec();
0370 
0371                 ready=true;
0372             }
0373         }
0374     }
0375 
0376     d->m_tree->writeTemplateTree();
0377 
0378     if ( d->m_defaultTemplate->isChecked() )
0379     {
0380 
0381       KConfigGroup grp(KSharedConfig::openConfig(), "TemplateChooserDialog");
0382       grp.writeEntry( "LastReturnType", "Template" );
0383       grp.writePathEntry( "FullTemplateName", dir + '/' + t->file() );
0384       grp.writePathEntry( "AlwaysUseTemplate", dir + '/' + t->file() );
0385     }
0386 }
0387 
0388 void KoTemplateCreateDia::slotDefault() {
0389 
0390     d->m_default->setChecked(true);
0391     d->m_custom->setChecked(false);
0392     updatePixmap();
0393 }
0394 
0395 void KoTemplateCreateDia::slotCustom() {
0396 
0397     d->m_default->setChecked(false);
0398     d->m_custom->setChecked(true);
0399     if(d->m_customFile.isEmpty())
0400         slotSelect();
0401     else
0402         updatePixmap();
0403 }
0404 
0405 void KoTemplateCreateDia::slotSelect() {
0406 
0407     d->m_default->setChecked(false);
0408     d->m_custom->setChecked(true);
0409 
0410     QString name = KIconDialog::getIcon();
0411     if( name.isEmpty() ) {
0412         if(d->m_customFile.isEmpty()) {
0413             d->m_default->setChecked(true);
0414             d->m_custom->setChecked(false);
0415         }
0416         return;
0417     }
0418     const QString path = KIconLoader::global()->iconPath(name, -thumbnailExtent);
0419     d->m_customFile = path;
0420     d->m_customPixmap=QPixmap();
0421     updatePixmap();
0422 }
0423 
0424 void KoTemplateCreateDia::slotNameChanged(const QString &name) {
0425 
0426     if( ( name.trimmed().isEmpty() || !d->m_groups->topLevelItem(0) ) && !d->m_changed )
0427         enableButtonOk(false);
0428     else
0429         enableButtonOk(true);
0430 }
0431 
0432 void KoTemplateCreateDia::slotAddGroup() {
0433     bool ok=false;
0434     const QString name = QInputDialog::getText(this, i18n("Add Group"), i18n("Enter group name:"),
0435                                                QLineEdit::Normal, QString(), &ok);
0436     if(!ok)
0437         return;
0438     KoTemplateGroup *group=d->m_tree->find(name);
0439     if(group && !group->isHidden())
0440     {
0441         KMessageBox::information( this, i18n("This name is already used."), i18n("Add Group") );
0442         return;
0443     }
0444     QString dir = KoResourcePaths::saveLocation("data", d->m_tree->templatesResourcePath());
0445     dir+=name;
0446     KoTemplateGroup *newGroup=new KoTemplateGroup(name, dir, 0, true);
0447     d->m_tree->add(newGroup);
0448     QTreeWidgetItem *item = new QTreeWidgetItem(d->m_groups, QStringList() << name);
0449     d->m_groups->setCurrentItem(item);
0450     d->m_groups->sortItems(0, Qt::AscendingOrder);
0451     d->m_name->setFocus();
0452     enableButtonOk(true);
0453     d->m_changed=true;
0454 }
0455 
0456 void KoTemplateCreateDia::slotRemove() {
0457 
0458     QTreeWidgetItem *item = d->m_groups->currentItem();
0459     if(!item)
0460         return;
0461 
0462     QString what;
0463         QString removed;
0464         if (item->parent() == nullptr) {
0465                 what =  i18n("Do you really want to remove that group?");
0466                 removed = i18n("Remove Group");
0467         } else {
0468                 what =  i18n("Do you really want to remove that template?");
0469         removed = i18n("Remove Template");
0470         }
0471 
0472     if(KMessageBox::warningContinueCancel(this, what,
0473                                  removed,KStandardGuiItem::del())==KMessageBox::Cancel) {
0474         d->m_name->setFocus();
0475         return;
0476     }
0477 
0478     if(item->parent() == nullptr) {
0479         KoTemplateGroup *group=d->m_tree->find(item->text(0));
0480         if(group)
0481             group->setHidden(true);
0482     }
0483     else {
0484         bool done=false;
0485         QList<KoTemplateGroup*> groups = d->m_tree->groups();
0486         QList<KoTemplateGroup*>::const_iterator it = groups.constBegin();
0487         for(; it != groups.constEnd() && !done; ++it) {
0488             KoTemplate *t = (*it)->find(item->text(0));
0489 
0490             if(t) {
0491                 t->setHidden(true);
0492                 done=true;
0493             }
0494         }
0495     }
0496     delete item;
0497     item=0;
0498     enableButtonOk(true);
0499     d->m_name->setFocus();
0500     d->m_changed=true;
0501 }
0502 
0503 void KoTemplateCreateDia::updatePixmap() {
0504 
0505     if(d->m_default->isChecked() && !d->m_thumbnail.isNull())
0506         d->m_preview->setPixmap(d->m_thumbnail);
0507     else if(d->m_custom->isChecked() && !d->m_customFile.isEmpty()) {
0508         if(d->m_customPixmap.isNull()) {
0509             debugMain <<"Trying to load picture" << d->m_customFile;
0510             // use the code in KoTemplate to load the image... hacky, I know :)
0511             KoTemplate t("foo", "bar", QString(), d->m_customFile);
0512             d->m_customPixmap=t.loadPicture();
0513         }
0514         else
0515             qWarning(/*30004*/) << "Trying to load picture";
0516 
0517         if(!d->m_customPixmap.isNull())
0518             d->m_preview->setPixmap(d->m_customPixmap);
0519         else
0520             d->m_preview->setText(i18n("Could not load picture."));
0521     }
0522     else
0523         d->m_preview->setText(i18n("No picture available."));
0524 }
0525 
0526 void KoTemplateCreateDia::fillGroupTree() {
0527 
0528     foreach(KoTemplateGroup *group, d->m_tree->groups()) {
0529         if(group->isHidden())
0530             continue;
0531         QTreeWidgetItem *groupItem=new QTreeWidgetItem(d->m_groups, QStringList() << group->name());
0532 
0533         foreach(KoTemplate *t, group->templates()) {
0534             if(t->isHidden())
0535                 continue;
0536             (void)new QTreeWidgetItem(groupItem, QStringList() << t->name());
0537         }
0538     }
0539 }