1 /* This file is part of the KDE project
2    Copyright (C) 2000 Werner Trobin <trobin@kde.org>
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "KoTemplateTree.h"
21 
22 #include <QDir>
23 #include <QPrinter>
24 #include <QUrl>
25 
26 #include <kdesktopfile.h>
27 #include <kconfig.h>
28 #include <MainDebug.h>
29 #include <klocalizedstring.h>
30 #include <kconfiggroup.h>
31 
32 #include <KoNetAccess.h>
33 #include <KoResourcePaths.h>
34 #include <KoTemplate.h>
35 #include <KoTemplateGroup.h>
36 #include <KoTemplates.h>
37 
KoTemplateTree(const QString & templatesResourcePath,bool readTree)38 KoTemplateTree::KoTemplateTree(const QString &templatesResourcePath, bool readTree) :
39         m_templatesResourcePath(templatesResourcePath), m_defaultGroup(0),
40         m_defaultTemplate(0)
41 {
42     if (readTree)
43         readTemplateTree();
44 }
45 
~KoTemplateTree()46 KoTemplateTree::~KoTemplateTree()
47 {
48     qDeleteAll(m_groups);
49 }
50 
readTemplateTree()51 void KoTemplateTree::readTemplateTree()
52 {
53 
54     readGroups();
55     readTemplates();
56 }
57 
writeTemplateTree()58 void KoTemplateTree::writeTemplateTree()
59 {
60 
61     const QString localDir = KoResourcePaths::saveLocation("data", m_templatesResourcePath);
62 
63     foreach (KoTemplateGroup *group, m_groups) {
64         //kDebug( 30003 ) <<"---------------------------------";
65         //kDebug( 30003 ) <<"group:" << group->name();
66 
67         bool touched = false;
68         const QList<KoTemplate*> templates = group->templates();
69         QList<KoTemplate*>::ConstIterator it = templates.begin();
70         for (; it != templates.end() && !touched && !group->touched(); ++it)
71             touched = (*it)->touched();
72 
73         if (group->touched() || touched) {
74             //kDebug( 30003 ) <<"touched";
75             if (!group->isHidden()) {
76                 //kDebug( 30003 ) <<"not hidden";
77                 QDir().mkpath(localDir + group->name()); // create the local group dir
78             } else {
79                 //kDebug( 30003 ) <<"hidden";
80                 if (group->dirs().count() == 1 && group->dirs().contains(localDir)) {
81                     //kDebug( 30003 ) <<"local only";
82                     KIO::NetAccess::del(QUrl::fromLocalFile(group->dirs().first()), 0);
83                     //kDebug( 30003 ) <<"removing:" << group->dirs().first();
84                 } else {
85                     //kDebug( 30003 ) <<"global";
86                     QDir().mkpath(localDir + group->name());
87                 }
88             }
89         }
90         foreach (KoTemplate *t, templates) {
91             if (t->touched()) {
92                 //kDebug( 30003 ) <<"++template:" << t->name();
93                 writeTemplate(t, group, localDir);
94             }
95             if (t->isHidden() && t->touched()) {
96                 //kDebug( 30003 ) <<"+++ delete local template ##############";
97                 writeTemplate(t, group, localDir);
98                 QFile::remove(t->file());
99                 QFile::remove(t->picture());
100             }
101         }
102     }
103 }
104 
add(KoTemplateGroup * g)105 void KoTemplateTree::add(KoTemplateGroup *g)
106 {
107 
108     KoTemplateGroup *group = find(g->name());
109     if (group == nullptr)
110         m_groups.append(g);
111     else {
112         group->addDir(g->dirs().first()); // "...there can be only one..." (Queen)
113         delete g;
114         g = nullptr;
115     }
116 }
117 
find(const QString & name) const118 KoTemplateGroup *KoTemplateTree::find(const QString &name) const
119 {
120     QList<KoTemplateGroup*>::const_iterator it = m_groups.begin();
121     KoTemplateGroup* ret = nullptr;
122 
123     while (it != m_groups.end()) {
124         if ((*it)->name() == name) {
125             ret = *it;
126             break;
127         }
128 
129         ++it;
130     }
131 
132     return ret;
133 }
134 
readGroups()135 void KoTemplateTree::readGroups()
136 {
137 
138     const QStringList dirs = KoResourcePaths::findDirs("data", m_templatesResourcePath);
139     foreach(const QString & dirName, dirs) {
140         //kDebug( 30003 ) <<"dir:" << *it;
141         QDir dir(dirName);
142         // avoid the annoying warning
143         if (!dir.exists())
144             continue;
145         QStringList templateDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
146         foreach(const QString & templateDirName, templateDirs) {
147             QDir templateDir(dirName + templateDirName);
148             QString name = templateDirName;
149             QString defaultTab;
150             int sortingWeight = 1000;
151             if (templateDir.exists(".directory")) {
152                 KDesktopFile config(templateDir.absoluteFilePath(".directory"));
153                 KConfigGroup dg = config.desktopGroup();
154                 name = dg.readEntry("Name");
155                 defaultTab = dg.readEntry("X-KDE-DefaultTab");
156                 sortingWeight = dg.readEntry("X-KDE-SortingWeight", 1000);
157                 //kDebug( 30003 ) <<"name:" << name;
158             }
159             KoTemplateGroup *g = new KoTemplateGroup(name, templateDir.absolutePath() + QDir::separator(), sortingWeight);
160             add(g);
161             if (defaultTab == "true")
162                 m_defaultGroup = g;
163         }
164     }
165 }
166 
readTemplates()167 void KoTemplateTree::readTemplates()
168 {
169     QString dontShow = "hide nothing at all - show all the templates, please, and let the user make the choice";
170 
171     foreach (KoTemplateGroup* group, m_groups) {
172         QStringList dirs = group->dirs();
173         for (QStringList::ConstIterator it = dirs.constBegin(); it != dirs.constEnd(); ++it) {
174             QDir d(*it);
175             if (!d.exists())
176                 continue;
177             QStringList files = d.entryList(QDir::Files | QDir::Readable, QDir::Name);
178             for (int i = 0; i < files.count(); ++i) {
179                 QString filePath = *it + files[i];
180                 //kDebug( 30003 ) <<"filePath:" << filePath;
181                 QString icon;
182                 QString text;
183                 QString description;
184                 QString hidden_str;
185                 QString fileName;
186                 QString color;
187                 QString swatch;
188                 QString variantName;
189                 QString thumbnail;
190                 bool wide = false;
191                 bool hidden = false;
192                 bool defaultTemplate = false;
193                 QString templatePath;
194                 QString measureSystem;
195                 // If a desktop file, then read the name from it.
196                 // Otherwise (or if no name in it?) use file name
197                 if (KDesktopFile::isDesktopFile(filePath)) {
198                     KConfig _config(filePath, KConfig::SimpleConfig);
199                     KConfigGroup config(&_config, "Desktop Entry");
200                     if (config.readEntry("Type") == "Link") {
201                         text = config.readEntry("Name");
202                         fileName = filePath;
203                         description = config.readEntry("Comment");
204                         //kDebug( 30003 ) <<"name:" << text;
205                         icon = config.readEntry("Icon");
206                         if (icon[0] != '/' && // allow absolute paths for icons
207                                 QFile::exists(*it + icon)) // allow icons from icontheme
208                             icon = *it + icon;
209                         //kDebug( 30003 ) <<"icon2:" << icon;
210                         color = config.readEntry("X-KDE-Color");
211                         swatch = config.readEntry("X-KDE-Swatch");
212                         wide = config.readEntry("X-KDE-TemplateIsWideFormat", false);
213                         variantName = config.readEntry("X-KDE-VariantName");
214                         thumbnail = config.readEntry("X-KDE-Thumbnail");
215                         hidden = config.readEntry("X-KDE-Hidden", false);
216                         defaultTemplate = config.readEntry("X-KDE-DefaultTemplate", false);
217                         measureSystem = config.readEntry("X-KDE-MeasureSystem").toLower();
218 
219                         // Don't add a template that is for the wrong measure system
220                         if (measureSystem == dontShow)
221                             continue;
222 
223                         //kDebug( 30003 ) <<"hidden:" << hidden_str;
224                         templatePath = config.readPathEntry("URL", QString());
225                         //kDebug( 30003 ) <<"Link to :" << templatePath;
226                         if (templatePath[0] != '/') {
227                             if (templatePath.left(6) == "file:/") // I doubt this will happen
228                                 templatePath = templatePath.right(templatePath.length() - 6);
229                             //else
230                             //  kDebug( 30003 ) <<"dirname=" << *it;
231                             templatePath = *it + templatePath;
232                             //kDebug( 30003 ) <<"templatePath:" << templatePath;
233                         }
234                     } else
235                         continue; // Invalid
236                 }
237                 // The else if and the else branch are here for compat. with the old system
238                 else if (files[i].right(4) != ".png")
239                     // Ignore everything that is not a PNG file
240                     continue;
241                 else {
242                     // Found a PNG file - the template must be here in the same dir.
243                     icon = filePath;
244                     QFileInfo fi(filePath);
245                     text = fi.baseName();
246                     templatePath = filePath; // Note that we store the .png file as the template !
247                     // That's the way it's always been done. Then the app replaces the extension...
248                 }
249                 KoTemplate *t = new KoTemplate(text, description, templatePath, icon, fileName,
250                                                measureSystem, color, swatch, variantName, wide, hidden);
251                 if (!thumbnail.isEmpty())
252                     t->setThumbnail(*it + thumbnail);
253                 group->add(t, false, false); // false -> we aren't a "user", false -> don't
254                 // "touch" the group to avoid useless
255                 // creation of dirs in .kde/blah/...
256                 if (defaultTemplate)
257                     m_defaultTemplate = t;
258             }
259         }
260     }
261 }
262 
writeTemplate(KoTemplate * t,KoTemplateGroup * group,const QString & localDir)263 void KoTemplateTree::writeTemplate(KoTemplate *t, KoTemplateGroup *group,
264                                    const QString &localDir)
265 {
266     QString fileName;
267     if (t->isHidden()) {
268         fileName = t->fileName();
269         // try to remove the file
270         if (QFile::remove(fileName) || !QFile::exists(fileName)) {
271             QFile::remove(t->name());
272             QFile::remove(t->picture());
273             return;
274         }
275     }
276     // be sure that the template's file name is unique so we don't overwrite an other
277     QString const path = localDir + group->name() + '/';
278     QString const name = KoTemplates::trimmed(t->name());
279     fileName = path + name + ".desktop";
280     if (t->isHidden() && QFile::exists(fileName))
281         return;
282     QString fill;
283     while (QFile(fileName).exists()) {
284         fill += '_';
285         fileName = path + fill + name + ".desktop";
286     }
287 
288     KConfig _config(fileName, KConfig::SimpleConfig);
289     KConfigGroup config(&_config, "Desktop Entry");
290     config.writeEntry("Type", "Link");
291     config.writePathEntry("URL", t->file());
292     config.writeEntry("Name", t->name());
293     config.writeEntry("Icon", t->picture());
294     config.writeEntry("X-KDE-Hidden", t->isHidden());
295 }
296