File indexing completed on 2024-03-24 16:41:38

0001 /********************************************************************************************
0002     begin                : Fri Aug 1 2003
0003     copyright            : (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net)
0004                            (C) 2007 by Holger Danielsson (holger.danielsson@versanet.de)
0005                            (C) 2009-2018 by Michel Ludwig (michel.ludwig@kdemail.net)
0006 *********************************************************************************************/
0007 
0008 /***************************************************************************
0009  *                                                                         *
0010  *   This program is free software; you can redistribute it and/or modify  *
0011  *   it under the terms of the GNU General Public License as published by  *
0012  *   the Free Software Foundation; either version 2 of the License, or     *
0013  *   (at your option) any later version.                                   *
0014  *                                                                         *
0015  ***************************************************************************/
0016 
0017 // 2007-03-12 dani
0018 //  - use KileDocument::Extensions
0019 //  - allowed extensions are always defined as list, f.e.: .tex .ltx .latex
0020 
0021 #include "kileproject.h"
0022 
0023 #include <QStringList>
0024 #include <QFileInfo>
0025 #include <QDir>
0026 
0027 #include <KLocalizedString>
0028 #include <KMessageBox>
0029 #include <KShell>
0030 #include <QUrl>
0031 
0032 #include "documentinfo.h"
0033 #include "kiledebug.h"
0034 #include "kiledocmanager.h"
0035 #include "kiletoolmanager.h"
0036 #include "kileinfo.h"
0037 #include "kileextensions.h"
0038 #include "livepreview.h"
0039 #include "utilities.h"
0040 
0041 /**
0042  * Since project file version 3, project files 'consist' of two files: one file named '<name>.kilepr' and
0043  * one file named '<name>.kilepr.gui' located in the '.kile' subdirectory of the project directory.
0044  * The former files contains the static structure of the project, and the later contains the current gui display settings
0045  * (like which file is open or on which line and column the cursors are).
0046  */
0047 
0048 
0049 /*
0050  * KileProjectItem
0051  */
0052 KileProjectItem::KileProjectItem(KileProject *project, const QUrl &url, int type) :
0053     m_project(project),
0054     m_url(url),
0055     m_type(type),
0056     m_docinfo(Q_NULLPTR),
0057     m_parent(Q_NULLPTR),
0058     m_child(Q_NULLPTR),
0059     m_sibling(Q_NULLPTR),
0060     m_order(-1)
0061 {
0062     m_bOpen = m_archive = true;
0063 
0064     if (project) {
0065         project->add(this);
0066     }
0067 }
0068 
0069 void KileProjectItem::setOrder(int i)
0070 {
0071     m_order = i;
0072 }
0073 
0074 void KileProjectItem::setParent(KileProjectItem *projectItem)
0075 {
0076     m_parent = projectItem;
0077 
0078     //update parent info
0079     if (m_parent) {
0080         if (m_parent->firstChild()) {
0081             //get last child
0082             KileProjectItem *sib = m_parent->firstChild();
0083             while (sib->sibling()) {
0084                 sib = sib->sibling();
0085             }
0086 
0087             sib->setSibling(this);
0088         }
0089         else {
0090             m_parent->setChild(this);
0091         }
0092     }
0093     else {
0094         setChild(0);
0095         setSibling(0);
0096     }
0097 }
0098 
0099 void KileProjectItem::load()
0100 {
0101     KConfigGroup projectConfigGroup = m_project->configGroupForItem(this, KileProject::ProjectFile);
0102     KConfigGroup guiConfigGroup = m_project->configGroupForItem(this, KileProject::GUIFile);
0103     // project: archive, highlight, mode
0104     // gui: column, encoding, line, open, order
0105     setEncoding(projectConfigGroup.readEntry("encoding", QString()));
0106     setMode(projectConfigGroup.readEntry("mode", QString()));
0107     setHighlight(projectConfigGroup.readEntry("highlight", QString()));
0108     setArchive(projectConfigGroup.readEntry("archive", true));
0109     setOpenState(guiConfigGroup.readEntry("open", true));
0110     setOrder(guiConfigGroup.readEntry("order", -1));
0111 }
0112 
0113 void KileProjectItem::save()
0114 {
0115     KConfigGroup projectConfigGroup = m_project->configGroupForItem(this, KileProject::ProjectFile);
0116     KConfigGroup guiConfigGroup = m_project->configGroupForItem(this, KileProject::GUIFile);
0117     // project: archive, highlight, mode
0118     // gui: encoding, open, order
0119     projectConfigGroup.writeEntry("encoding", encoding());
0120     projectConfigGroup.writeEntry("mode", mode());
0121     projectConfigGroup.writeEntry("highlight", highlight());
0122     projectConfigGroup.writeEntry("archive", archive());
0123     guiConfigGroup.writeEntry("open", isOpen());
0124     guiConfigGroup.writeEntry("order", order());
0125 }
0126 
0127 void KileProjectItem::loadDocumentAndViewSettings()
0128 {
0129     if(!m_docinfo) {
0130         return;
0131     }
0132     KTextEditor::Document *document = m_docinfo->getDocument();
0133     if(!document) {
0134         return;
0135     }
0136     QList<KTextEditor::View*> viewList = document->views();
0137     loadDocumentSettings(document);
0138     int i = 0;
0139     for(QList<KTextEditor::View*>::iterator it = viewList.begin(); it != viewList.end(); ++it) {
0140         loadViewSettings(*it, i);
0141         ++i;
0142     }
0143 }
0144 
0145 void KileProjectItem::saveDocumentAndViewSettings()
0146 {
0147     if(!m_docinfo) {
0148         return;
0149     }
0150     KTextEditor::Document *document = m_docinfo->getDocument();
0151     if(!document) {
0152         return;
0153     }
0154     QList<KTextEditor::View*> viewList = document->views();
0155     saveDocumentSettings(document);
0156     int i = 0;
0157     for(QList<KTextEditor::View*>::iterator it = viewList.begin(); it != viewList.end(); ++it) {
0158         saveViewSettings(*it, i);
0159         ++i;
0160     }
0161 }
0162 
0163 void KileProjectItem::loadViewSettings(KTextEditor::View *view, int viewIndex)
0164 {
0165     KConfigGroup configGroup = m_project->configGroupForItemViewSettings(this, viewIndex);
0166     view->readSessionConfig(configGroup);
0167 }
0168 
0169 void KileProjectItem::saveViewSettings(KTextEditor::View *view, int viewIndex)
0170 {
0171     KConfigGroup configGroup = m_project->configGroupForItemViewSettings(this, viewIndex);
0172     view->writeSessionConfig(configGroup);
0173 }
0174 
0175 void KileProjectItem::loadDocumentSettings(KTextEditor::Document *document)
0176 {
0177     KConfigGroup configGroup = m_project->configGroupForItemDocumentSettings(this);
0178     if(!configGroup.exists()) {
0179         return;
0180     }
0181     document->readSessionConfig(configGroup, QSet<QString>() << "SkipUrl");
0182 }
0183 
0184 void KileProjectItem::saveDocumentSettings(KTextEditor::Document *document)
0185 {
0186     KConfigGroup configGroup = m_project->configGroupForItemDocumentSettings(this);
0187     document->writeSessionConfig(configGroup, QSet<QString>() << "SkipUrl");
0188 }
0189 
0190 void KileProjectItem::print(int level)
0191 {
0192     QString str;
0193     str.fill('\t', level);
0194     KILE_DEBUG_MAIN << str << "+" << url().fileName();
0195 
0196     if (firstChild()) {
0197         firstChild()->print(++level);
0198     }
0199 
0200     if (sibling()) {
0201         sibling()->print(level);
0202     }
0203 }
0204 
0205 void KileProjectItem::allChildren(QList<KileProjectItem*> *list) const
0206 {
0207     KileProjectItem *firstChildItem = firstChild();
0208 
0209 //  KILE_DEBUG_MAIN << "\tKileProjectItem::allChildren(" << list->count() << ")";
0210     while(firstChildItem != Q_NULLPTR) {
0211         list->append(firstChildItem);
0212 //      KILE_DEBUG_MAIN << "\t\tappending " << firstChildItem->url().fileName();
0213         firstChildItem->allChildren(list);
0214         firstChildItem = firstChildItem->sibling();
0215     }
0216 }
0217 
0218 void KileProjectItem::setInfo(KileDocument::TextInfo *docinfo)
0219 {
0220     m_docinfo = docinfo;
0221     if(docinfo)
0222     {
0223         connect(docinfo,SIGNAL(urlChanged(KileDocument::Info*,QUrl)), this, SLOT(slotChangeURL(KileDocument::Info*,QUrl)));
0224         connect(docinfo,SIGNAL(depChanged()), m_project, SLOT(buildProjectTree()));
0225     }
0226 }
0227 
0228 void KileProjectItem::changeURL(const QUrl &url)
0229 {
0230     // don't allow empty URLs
0231     if(!url.isEmpty() && m_url != url)
0232     {
0233         m_url = url;
0234         emit(urlChanged(this));
0235     }
0236 }
0237 
0238 void KileProjectItem::slotChangeURL(KileDocument::Info*, const QUrl &url)
0239 {
0240     changeURL(url);
0241 }
0242 
0243 /*
0244  * KileProject
0245  */
0246 
0247 // for creating an empty project
0248 KileProject::KileProject(const QString& name, const QUrl &url, KileDocument::Extensions *extensions)
0249     : QObject(Q_NULLPTR), m_invalid(false), m_masterDocument(QString()), m_useMakeIndexOptions(false),
0250       m_config(Q_NULLPTR), m_guiConfig(Q_NULLPTR), m_extmanager(extensions)
0251 {
0252     m_name = name;
0253     init(url);
0254 
0255     //create the project file
0256     KConfigGroup configGroup = m_config->group("General");
0257     configGroup.writeEntry("name", m_name);
0258     configGroup.writeEntry("kileprversion", KILE_PROJECTFILE_VERSION);
0259     configGroup.writeEntry("kileversion", kileFullVersion);
0260 
0261     load();
0262 }
0263 
0264 // for opening an existing project, 'load()' still has to be called separately!
0265 KileProject::KileProject(const QUrl &url, KileDocument::Extensions *extensions)
0266     : QObject(Q_NULLPTR), m_invalid(false), m_masterDocument(QString()), m_useMakeIndexOptions(false),
0267       m_config(Q_NULLPTR), m_guiConfig(Q_NULLPTR), m_extmanager(extensions)
0268 {
0269     init(url);
0270 }
0271 
0272 KileProject::~KileProject()
0273 {
0274     KILE_DEBUG_MAIN << "DELETING KILEPROJECT " <<  m_projecturl.url();
0275     emit(aboutToBeDestroyed(this));
0276     delete m_guiConfig;
0277     delete m_config;
0278 
0279     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0280         delete *it;
0281     }
0282 }
0283 
0284 void KileProject::init(const QUrl &url)
0285 {
0286     m_projecturl = KileUtilities::canonicalUrl(url);
0287 
0288     m_baseurl = m_projecturl.adjusted(QUrl::RemoveFilename);
0289 
0290     KILE_DEBUG_MAIN << "KileProject m_baseurl = " << m_baseurl.toLocalFile();
0291 
0292     m_config = new KConfig(m_projecturl.toLocalFile(), KConfig::SimpleConfig);
0293 }
0294 
0295 void KileProject::setLastDocument(const QUrl &url)
0296 {
0297     if (item(url) != 0) {
0298         m_lastDocument = KileUtilities::canonicalUrl(url);
0299     }
0300 }
0301 
0302 void KileProject::setExtensions(KileProjectItem::Type type, const QString & ext)
0303 {
0304     if (type == KileProjectItem::ProjectFile || type >= KileProjectItem::Other)
0305     {
0306         qWarning() << "ERROR: invalid project item type:" << type;
0307         return;
0308     }
0309 
0310     // first we take all standard extensions
0311     QStringList standardExtList;
0312     if(type == KileProjectItem::Source) {
0313         standardExtList = (m_extmanager->latexDocuments()).split(' ');
0314     }
0315     else if(type == KileProjectItem::Package) {
0316         standardExtList = (m_extmanager->latexPackages()).split(' ');
0317     }
0318     else if(type == KileProjectItem::Image) {
0319         standardExtList = (m_extmanager->images()).split(' ');
0320     }
0321     else if(type == KileProjectItem::Bibliography) {
0322         standardExtList = (m_extmanager->bibtex()).split(' ');
0323     }
0324 
0325     // now we scan user-defined list and accept all extension,
0326     // except standard extensions of course
0327     QString userExt;
0328     if(!ext.isEmpty()) {
0329         QStringList userExtList;
0330 
0331         QStringList::ConstIterator it;
0332         QStringList list = ext.split(' ');
0333         for(it = list.constBegin(); it != list.constEnd(); ++it) {
0334             // some tiny extension checks
0335             if((*it).length() < 2 || (*it)[0] != '.') {
0336                 continue;
0337             }
0338 
0339             // some of the old definitions are wrong, so we test them all
0340             if(type == KileProjectItem::Source || type == KileProjectItem::Package) {
0341                 if(!(m_extmanager->isLatexDocument(*it) || m_extmanager->isLatexPackage(*it))) {
0342                     standardExtList << (*it);
0343                     userExtList << (*it);
0344                 }
0345             }
0346             else if(type == KileProjectItem::Image) {
0347                 if(!m_extmanager->isImage(*it)) {
0348                     standardExtList << (*it);
0349                     userExtList << (*it);
0350                 }
0351             }
0352             else if(type == KileProjectItem::Bibliography) {
0353                 if(!m_extmanager->isBibFile(*it)) {
0354                     standardExtList << (*it);
0355                     userExtList << (*it);
0356                 }
0357             }
0358         }
0359         if(userExtList.count() > 0) {
0360             userExt = userExtList.join(" ");
0361         }
0362     }
0363 
0364     // now we build a regular expression for all extensions
0365     // (used to search for a filename with a valid extension)
0366     QString pattern = standardExtList.join("|");
0367     pattern.replace('.', "\\.");
0368     pattern = '('+ pattern +")$";
0369 
0370     // and save it
0371     m_reExtensions[type-1].setPattern(pattern);
0372 
0373     // if the list of user-defined extensions has changed
0374     // we save the new value and (re)build the project tree
0375     if (m_extensions[type-1] != userExt) {
0376         m_extensions[type-1] = userExt;
0377         buildProjectTree();
0378     }
0379 }
0380 
0381 void KileProject::setDefaultGraphicExt(const QString & ext) {
0382     m_defGraphicExt = ext;
0383 }
0384 
0385 const QString & KileProject::defaultGraphicExt() {
0386     return m_defGraphicExt;
0387 }
0388 
0389 void KileProject::setType(KileProjectItem *projectItem)
0390 {
0391     if(projectItem->path().right(7) == ".kilepr") {
0392         projectItem->setType(KileProjectItem::ProjectFile);
0393         return;
0394     }
0395 
0396     bool unknown = true;
0397     for(int i = KileProjectItem::Source; i < KileProjectItem::Other; ++i) {
0398         if(m_reExtensions[i-1].indexIn(projectItem->url().fileName()) != -1) {
0399             projectItem->setType(i);
0400             unknown = false;
0401             break;
0402         }
0403     }
0404 
0405     if(unknown) {
0406         projectItem->setType(KileProjectItem::Other);
0407     }
0408 }
0409 
0410 void KileProject::readMakeIndexOptions()
0411 {
0412     QString grp = KileTool::groupFor("MakeIndex", m_config);
0413 
0414     //get the default value
0415     KSharedConfig::Ptr cfg = KSharedConfig::openConfig();
0416     KConfigGroup configGroup = cfg->group(KileTool::groupFor("MakeIndex", KileTool::configName("MakeIndex", cfg.data())));
0417     QString deflt = configGroup.readEntry("options", "'%S'.idx");
0418 
0419     if (useMakeIndexOptions() && !grp.isEmpty()) {
0420         KConfigGroup makeIndexGroup = m_config->group(grp);
0421         QString val = makeIndexGroup.readEntry("options", deflt);
0422         if ( val.isEmpty() ) val = deflt;
0423         setMakeIndexOptions(val);
0424     }
0425     else { //use default value
0426         setMakeIndexOptions(deflt);
0427     }
0428 }
0429 
0430 void KileProject::writeUseMakeIndexOptions()
0431 {
0432     if ( useMakeIndexOptions() )
0433         KileTool::setConfigName("MakeIndex", "Default", m_config);
0434     else
0435         KileTool::setConfigName("MakeIndex", "", m_config);
0436 }
0437 
0438 QString KileProject::addBaseURL(const QString &path)
0439 {
0440     KILE_DEBUG_MAIN << "===addBaseURL(const QString & " << path << " )";
0441     if(path.isEmpty()) {
0442         return path;
0443     }
0444 
0445     else if(QDir::isAbsolutePath(path)) {
0446         return KileUtilities::canonicalUrl(QUrl::fromLocalFile(path)).toLocalFile();
0447     }
0448     else {
0449         return  KileUtilities::canonicalUrl(QUrl::fromLocalFile(m_baseurl.adjusted(QUrl::StripTrailingSlash).toLocalFile() + '/' + path)).toLocalFile();
0450     }
0451 }
0452 
0453 QString KileProject::removeBaseURL(const QString &path)
0454 {
0455     if(QDir::isAbsolutePath(path)) {
0456         QFileInfo info(path);
0457         QString relPath = findRelativePath(path);
0458         KILE_DEBUG_MAIN << "removeBaseURL path is" << path << " , relPath is " << relPath;
0459         return relPath;
0460     }
0461     else {
0462         return path;
0463     }
0464 }
0465 
0466 bool KileProject::appearsToBeValidProjectFile()
0467 {
0468     if(!m_config->hasGroup("General")) {
0469         return false;
0470     }
0471 
0472     KConfigGroup generalGroup = m_config->group("General");
0473     return generalGroup.hasKey("name") && generalGroup.hasKey("kileprversion") && generalGroup.hasKey("kileversion");
0474 }
0475 
0476 int KileProject::getProjectFileVersion()
0477 {
0478     KConfigGroup generalGroup = m_config->group("General");
0479 
0480     return generalGroup.readEntry("kileprversion", 0);
0481 }
0482 
0483 // WARNING: before calling this method, the project file must be of the current 'kileprversion'!
0484 //          also assumes that 'm_name' has been set correctly already if this is a fresh (empty) project!
0485 bool KileProject::load()
0486 {
0487     KILE_DEBUG_MAIN << "KileProject: loading..." << Qt::endl;
0488 
0489     if(!ensurePrivateKileDirectoryExists(m_projecturl)) {
0490         return false;
0491     }
0492 
0493     delete m_guiConfig;
0494     m_guiConfig = new KConfig(getPathForGUISettingsProjectFile(m_projecturl), KConfig::SimpleConfig);
0495 
0496     //load general settings/options
0497     KConfigGroup generalGroup = m_config->group("General");
0498     m_name = generalGroup.readEntry("name", m_name);
0499 
0500     m_defGraphicExt = generalGroup.readEntry("def_graphic_ext", QString());
0501 
0502     QString master = addBaseURL(generalGroup.readEntry("masterDocument", QString()));
0503     KILE_DEBUG_MAIN << "masterDoc == " << master;
0504     setMasterDocument(master);
0505 
0506     setExtensions(KileProjectItem::Source, generalGroup.readEntry("src_extensions",m_extmanager->latexDocuments()));
0507     setExtensions(KileProjectItem::Package, generalGroup.readEntry("pkg_extensions",m_extmanager->latexPackages()));
0508     setExtensions(KileProjectItem::Image, generalGroup.readEntry("img_extensions",m_extmanager->images()));
0509     setExtensions(KileProjectItem::Bibliography, generalGroup.readEntry("bib_extensions", m_extmanager->bibtex()));
0510 
0511     setQuickBuildConfig(KileTool::configName("QuickBuild", m_config));
0512 
0513     if( KileTool::configName("MakeIndex",m_config).compare("Default") == 0) {
0514         setUseMakeIndexOptions(true);
0515     }
0516     else {
0517         setUseMakeIndexOptions(false);
0518     }
0519 
0520     readMakeIndexOptions();
0521 
0522     QUrl projectUrl;
0523     KileProjectItem *projectItem;
0524     const QStringList groups = m_config->groupList();
0525 
0526     //retrieve all the project files and create and initialize project items for them
0527     for (const auto& group : groups) {
0528         if(!m_config->hasGroup(group)) { // 'group' might have been deleted
0529             continue;                // work around bug 384039
0530         }
0531         if (group.left(5) == "item:") {
0532             QString path = group.mid(5);
0533             if (QDir::isAbsolutePath(path)) {
0534                 projectUrl = QUrl::fromLocalFile(path);
0535             }
0536             else {
0537                 projectUrl = m_baseurl.adjusted(QUrl::StripTrailingSlash);
0538                 projectUrl.setPath(projectUrl.path() + '/' + path);
0539             }
0540             projectItem = new KileProjectItem(this, KileUtilities::canonicalUrl(projectUrl));
0541             setType(projectItem);
0542 
0543             KConfigGroup configGroup = m_config->group(group);
0544             // path has to be set before we can load it
0545             projectItem->changePath(group.mid(5));
0546             projectItem->load();
0547             connect(projectItem, SIGNAL(urlChanged(KileProjectItem*)), this, SLOT(itemRenamed(KileProjectItem*)) );
0548         }
0549     }
0550 
0551     // only call this after all items are created, otherwise setLastDocument doesn't accept the url
0552     KConfigGroup guiGeneralGroup = m_guiConfig->group("General");
0553     setLastDocument(QUrl::fromLocalFile(addBaseURL(guiGeneralGroup.readEntry("lastDocument", QString()))));
0554 
0555     generalGroup = m_config->group("General");
0556 
0557     readBibliographyBackendSettings(generalGroup);
0558 
0559     KileTool::LivePreviewManager::readLivePreviewStatusSettings(guiGeneralGroup, this);
0560 
0561 //  dump();
0562 
0563     return true;
0564 }
0565 
0566 bool KileProject::save()
0567 {
0568     KILE_DEBUG_MAIN << "KileProject: saving..." << Qt::endl;
0569 
0570     KConfigGroup generalGroup = m_config->group("General");
0571     KConfigGroup guiGeneralGroup = m_guiConfig->group("General");
0572 
0573     generalGroup.writeEntry("name", m_name);
0574     generalGroup.writeEntry("kileprversion", KILE_PROJECTFILE_VERSION);
0575     generalGroup.writeEntry("kileversion", kileFullVersion);
0576     generalGroup.writeEntry("def_graphic_ext", m_defGraphicExt);
0577 
0578     KILE_DEBUG_MAIN << "KileProject::save() masterDoc = " << removeBaseURL(m_masterDocument);
0579     generalGroup.writeEntry("masterDocument", removeBaseURL(m_masterDocument));
0580     guiGeneralGroup.writeEntry("lastDocument", removeBaseURL(m_lastDocument.toLocalFile()));
0581 
0582     writeBibliographyBackendSettings(generalGroup);
0583 
0584     KileTool::LivePreviewManager::writeLivePreviewStatusSettings(guiGeneralGroup, this);
0585 
0586     writeConfigEntry("src_extensions",m_extmanager->latexDocuments(),KileProjectItem::Source);
0587     writeConfigEntry("pkg_extensions",m_extmanager->latexPackages(),KileProjectItem::Package);
0588     writeConfigEntry("img_extensions",m_extmanager->images(),KileProjectItem::Image);
0589     writeConfigEntry("bib_extensions", m_extmanager->bibtex(), KileProjectItem::Bibliography);
0590     // only to avoid problems with older versions
0591     generalGroup.writeEntry("src_extIsRegExp", false);
0592     generalGroup.writeEntry("pkg_extIsRegExp", false);
0593     generalGroup.writeEntry("img_extIsRegExp", false);
0594 
0595     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0596         (*it)->save();
0597     }
0598 
0599     KileTool::setConfigName("QuickBuild", quickBuildConfig(), m_config);
0600 
0601     writeUseMakeIndexOptions();
0602     if(useMakeIndexOptions()) {
0603         QString grp = KileTool::groupFor("MakeIndex", m_config);
0604         if(grp.isEmpty()) {
0605             grp = "Default";
0606         }
0607         KConfigGroup configGroup = m_config->group(grp);
0608         configGroup.writeEntry("options", makeIndexOptions());
0609     }
0610 
0611     m_config->sync();
0612     m_guiConfig->sync();
0613 
0614     // dump();
0615 
0616     return true;
0617 }
0618 
0619 void KileProject::writeConfigEntry(const QString &key, const QString &standardExt, KileProjectItem::Type type)
0620 {
0621     KConfigGroup generalGroup = m_config->group("General");
0622     QString userExt = extensions(type);
0623     if(userExt.isEmpty()) {
0624         generalGroup.writeEntry(key, standardExt);
0625     }
0626     else {
0627         generalGroup.writeEntry(key, standardExt + ' ' + extensions(type));
0628     }
0629 }
0630 
0631 KConfigGroup KileProject::configGroupForItem(KileProjectItem *projectItem, ConfigScope scope) const
0632 {
0633     KConfig* cfgObject = (scope == GUIFile ? m_guiConfig : m_config);
0634     return cfgObject->group("item:" + projectItem->path());
0635 }
0636 
0637 KConfigGroup KileProject::configGroupForItemDocumentSettings(KileProjectItem *projectItem) const
0638 {
0639     return m_guiConfig->group("document-settings,item:" + projectItem->path());
0640 }
0641 
0642 KConfigGroup KileProject::configGroupForItemViewSettings(KileProjectItem *projectItem, int viewIndex) const
0643 {
0644     return m_guiConfig->group("view-settings,view=" + QString::number(viewIndex) + ",item:" + projectItem->path());
0645 }
0646 
0647 void KileProject::removeConfigGroupsForItem(KileProjectItem *projectItem)
0648 {
0649     QString itemString = "item:" + projectItem->path();
0650     const QStringList groupList = m_config->groupList();
0651     for (const auto& groupName : groupList) {
0652         if(!m_config->hasGroup(groupName)) { // 'groupName' might have been deleted
0653             continue;                    // work around bug 384039
0654         }
0655         if(groupName.indexOf(itemString) >= 0) {
0656             m_config->deleteGroup(groupName);
0657         }
0658     }
0659 }
0660 
0661 static bool isAncestorOf(const KileProjectItem *toBeChecked, KileProjectItem *parent)
0662 {
0663     KileProjectItem *projectItem = parent;
0664     while(projectItem != Q_NULLPTR) {
0665         if(projectItem == toBeChecked) {
0666             return true;
0667         }
0668         projectItem = projectItem->parent();
0669     }
0670     return false;
0671 }
0672 
0673 void KileProject::buildProjectTree()
0674 {
0675     KILE_DEBUG_MAIN << "==KileProject::buildProjectTree==========================";
0676 
0677     //determine the parent doc for each item (TODO:an item can only have one parent, not necessarily true for LaTeX docs)
0678 
0679     QStringList deps;
0680     QString dep;
0681     KileProjectItem *itm;
0682 
0683     //clean first
0684     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0685         (*it)->setParent(0);
0686     }
0687 
0688     //use the dependencies list of the documentinfo object to determine the parent
0689     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0690         //set the type correctly (changing m_extensions causes a call to buildProjectTree)
0691         setType(*it);
0692         KileDocument::Info *docinfo = (*it)->getInfo();
0693 
0694         if(docinfo) {
0695             QUrl parentUrl = docinfo->url();
0696             if(parentUrl.isLocalFile()) {
0697                 // strip the file name from 'parentUrl'
0698                 parentUrl = QUrl::fromUserInput(QFileInfo(parentUrl.path()).path());
0699             }
0700             else {
0701                 parentUrl = m_baseurl;
0702             }
0703             deps = docinfo->dependencies();
0704             for(int i = 0; i < deps.count(); ++i) {
0705                 dep = deps[i];
0706 
0707                 QUrl inputUrl;
0708 
0709                 if(m_extmanager->isTexFile(dep)) {
0710                     inputUrl = QUrl::fromLocalFile(KileInfo::checkOtherPaths(parentUrl, dep, KileInfo::texinputs));
0711                 }
0712                 else if(m_extmanager->isBibFile(dep)) {
0713                     inputUrl = QUrl::fromLocalFile(KileInfo::checkOtherPaths(parentUrl, dep, KileInfo::bibinputs));
0714                 }
0715                 itm = item(inputUrl);
0716                 if(itm && (itm->parent() == 0)
0717                         && !isAncestorOf(itm, *it)) { // avoid circular references if a file should
0718                     // include itself in a circular way
0719                     itm->setParent(*it);
0720                 }
0721             }
0722         }
0723     }
0724 
0725     //make a list of all the root items (items with parent == 0)
0726     m_rootItems.clear();
0727     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0728         if((*it)->parent() == Q_NULLPTR) {
0729             m_rootItems.append(*it);
0730         }
0731     }
0732 
0733     emit(projectTreeChanged(this));
0734 }
0735 
0736 KileProjectItem* KileProject::item(const QUrl &url)
0737 {
0738     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0739         if((*it)->url() == url) {
0740             return *it;
0741         }
0742     }
0743 
0744     return Q_NULLPTR;
0745 }
0746 
0747 KileProjectItem* KileProject::item(const KileDocument::Info *info)
0748 {
0749     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0750         KileProjectItem *current = *it;
0751 
0752         if (current->getInfo() == info) {
0753             return current;
0754         }
0755     }
0756 
0757     return Q_NULLPTR;
0758 }
0759 
0760 void KileProject::add(KileProjectItem* projectItem)
0761 {
0762     KILE_DEBUG_MAIN << "KileProject::add projectitem" << projectItem->url().toLocalFile();
0763 
0764     setType(projectItem);
0765 
0766     projectItem->changePath(findRelativePath(projectItem->url()));
0767     connect(projectItem, SIGNAL(urlChanged(KileProjectItem*)), this, SLOT(itemRenamed(KileProjectItem*)) );
0768 
0769     m_projectItems.append(projectItem);
0770 
0771     emit projectItemAdded(this, projectItem);
0772 
0773     // dump();
0774 }
0775 
0776 void KileProject::remove(KileProjectItem* projectItem)
0777 {
0778     KILE_DEBUG_MAIN << projectItem->path();
0779     removeConfigGroupsForItem(projectItem);
0780     m_projectItems.removeAll(projectItem);
0781 
0782     emit projectItemRemoved(this, projectItem);
0783 
0784     // dump();
0785 }
0786 
0787 void KileProject::itemRenamed(KileProjectItem *projectItem)
0788 {
0789     KILE_DEBUG_MAIN << "==KileProject::itemRenamed==========================";
0790     KILE_DEBUG_MAIN << "\t" << projectItem->url().fileName();
0791     removeConfigGroupsForItem(projectItem);
0792 
0793     projectItem->changePath(findRelativePath(projectItem->url()));
0794 }
0795 
0796 QString KileProject::findRelativePath(const QString &path)
0797 {
0798     return this->findRelativePath(QUrl::fromLocalFile(path));
0799 }
0800 
0801 QString KileProject::findRelativePath(const QUrl &url)
0802 {
0803     KILE_DEBUG_MAIN << "QString KileProject::findRelativePath(const QUrl " << url.path() << ")";
0804 
0805     if ( m_baseurl.toLocalFile() == url.toLocalFile() ) {
0806         return "./";
0807     }
0808     const QString path = QDir(m_baseurl.path()).relativeFilePath(url.path());
0809     KILE_DEBUG_MAIN << "relPath is " << path;
0810     return path;
0811 }
0812 
0813 bool KileProject::contains(const QUrl &url)
0814 {
0815     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0816         if((*it)->url() == url) {
0817             return true;
0818         }
0819     }
0820 
0821     return false;
0822 }
0823 
0824 bool KileProject::contains(const KileDocument::Info *info)
0825 {
0826     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0827         if((*it)->getInfo() == info) {
0828             return true;
0829         }
0830     }
0831     return false;
0832 }
0833 
0834 KileProjectItem *KileProject::rootItem(KileProjectItem *projectItem) const
0835 {
0836     //find the root item (i.e. the eldest parent)
0837     KileProjectItem *root = projectItem;
0838     while(root->parent() != Q_NULLPTR) {
0839         root = root->parent();
0840     }
0841 
0842     //check if this root item is a LaTeX root
0843     if(root->getInfo()) {
0844         if (root->getInfo()->isLaTeXRoot()) {
0845             return root;
0846         }
0847         else {
0848             //if not, see if we can find another root item that is a LaTeX root
0849             for(QList<KileProjectItem*>::const_iterator it = m_rootItems.begin(); it != m_rootItems.end(); ++it) {
0850                 KileProjectItem *current = *it;
0851                 if(current->getInfo() && current->getInfo()->isLaTeXRoot()) {
0852                     return current;
0853                 }
0854             }
0855         }
0856 
0857         //no LaTeX root found, return previously found root
0858         return root;
0859     }
0860 
0861     //root is not a valid item (getInfo() return 0L), return original item
0862     return projectItem;
0863 }
0864 
0865 void KileProject::dump()
0866 {
0867     KILE_DEBUG_MAIN << "KileProject::dump() " << m_name;
0868     for(QList<KileProjectItem*>::iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0869         KileProjectItem *projectItem = *it;
0870         KILE_DEBUG_MAIN << "item " << projectItem << " has path: "  << projectItem->path();
0871         KILE_DEBUG_MAIN << "item->type() " << projectItem->type();
0872         KILE_DEBUG_MAIN << "OpenState: " << projectItem->isOpen();
0873     }
0874 }
0875 
0876 QString KileProject::archiveFileList() const
0877 {
0878     KILE_DEBUG_MAIN << "KileProject::archiveFileList()";
0879 
0880     QString list;
0881     for(QList<KileProjectItem*>::const_iterator it = m_projectItems.begin(); it != m_projectItems.end(); ++it) {
0882         if ((*it)->archive()) {
0883             list.append(KShell::quoteArg((*it)->path()) + ' ');
0884         }
0885     }
0886     return list;
0887 }
0888 
0889 void KileProject::setMasterDocument(const QString & master) {
0890 
0891     if(!master.isEmpty()) {
0892 
0893         QFileInfo fi(master);
0894         if(fi.exists())
0895             m_masterDocument = master;
0896         else {
0897             m_masterDocument.clear();
0898             KILE_DEBUG_MAIN << "setMasterDocument: masterDoc=Q_NULLPTR";
0899         }
0900 
0901     }
0902     else {
0903         m_masterDocument.clear();
0904     }
0905 
0906     emit (masterDocumentChanged(m_masterDocument));
0907 }
0908 
0909 namespace {
0910 
0911 void moveConfigGroupKeysAsStrings(KConfig *src, KConfig *dst, const QString& groupName, const QStringList &keysToMove)
0912 {
0913     KConfigGroup srcGroup(src, groupName);
0914     KConfigGroup dstGroup(dst, groupName);
0915 
0916     for(const QString& key : keysToMove) {
0917         if(srcGroup.hasKey(key)) {
0918             QString value = srcGroup.readEntry(key, QStringLiteral(""));
0919             dstGroup.writeEntry(key, value);
0920             srcGroup.deleteEntry(key);
0921         }
0922     }
0923 }
0924 
0925 void deleteConfigGroupKeys(KConfig *src, const QString& groupName, const QStringList &keysToDelete)
0926 {
0927     KConfigGroup srcGroup(src, groupName);
0928 
0929     for(const QString& key : keysToDelete) {
0930         srcGroup.deleteEntry(key);
0931     }
0932 }
0933 }
0934 
0935 bool KileProject::migrateProjectFileToCurrentVersion()
0936 {
0937     if(getProjectFileVersion() < KILE_PROJECTFILE_VERSION) {
0938         return migrateProjectFileToVersion3();
0939     }
0940     return true;
0941 }
0942 
0943 bool KileProject::migrateProjectFileToVersion3()
0944 {
0945     KILE_DEBUG_MAIN << "Migrating project file" << m_projecturl << "to version 3";
0946 
0947     // (1) Every config group starting with "document-settings," or "view-settings," will be moved to the GUI config file
0948     // (2) In every group named "item:..." the keys "column" and "line" are deleted
0949     // (3) In every group named "item:..." the keys "open" and "order" are moved to a new group of the same name
0950     //     in the GUI project file
0951     // (4) In the "General" group the keys "lastDocument", "kile_livePreviewEnabled", "kile_livePreviewStatusUserSpecified",
0952     //     "kile_livePreviewTool" are moved to the "General" group in the GUI project file
0953 
0954     if(!ensurePrivateKileDirectoryExists(m_projecturl)) {
0955         return false;
0956     }
0957 
0958     KConfig projectGUIFile(getPathForGUISettingsProjectFile(m_projecturl), KConfig::SimpleConfig);
0959 
0960     QStringList keysToMoveInItemGroups, keysToDeleteInItemGroups, keysToMoveInGeneralGroup;
0961 
0962     keysToMoveInItemGroups
0963             << QStringLiteral("column")
0964             << QStringLiteral("line")
0965             << QStringLiteral("open")
0966             << QStringLiteral("order");
0967 
0968     keysToDeleteInItemGroups
0969             << QStringLiteral("column")
0970             << QStringLiteral("line");
0971 
0972     keysToMoveInGeneralGroup
0973             << QStringLiteral("lastDocument")
0974             << QStringLiteral("kile_livePreviewEnabled")
0975             << QStringLiteral("kile_livePreviewStatusUserSpecified")
0976             << QStringLiteral("kile_livePreviewTool");
0977 
0978     const QStringList groups = m_config->groupList();
0979     for (const auto& groupName : groups) {
0980         if(!m_config->hasGroup(groupName)) { // 'groupName' might have been deleted
0981             continue;                    // work around bug 384039
0982         }
0983 
0984         // these ones we move completely
0985         if(groupName.startsWith(QLatin1String("document-settings,")) || groupName.startsWith(QLatin1String("view-settings,"))) {
0986             KConfigGroup oldGroup(m_config, groupName);
0987             KConfigGroup guiGroup(&projectGUIFile, groupName);
0988             oldGroup.copyTo(&guiGroup);
0989             m_config->deleteGroup(groupName);
0990             continue;
0991         }
0992 
0993         if(groupName.startsWith(QLatin1String("item:"))) {
0994             deleteConfigGroupKeys(m_config, groupName, keysToDeleteInItemGroups);
0995             moveConfigGroupKeysAsStrings(m_config, &projectGUIFile, groupName, keysToMoveInItemGroups);
0996         }
0997         else if(groupName == QLatin1String("General")) {
0998             moveConfigGroupKeysAsStrings(m_config, &projectGUIFile, groupName, keysToMoveInGeneralGroup);
0999         }
1000     }
1001 
1002     if(!projectGUIFile.sync()) {
1003         return false;
1004     }
1005 
1006     KConfigGroup configGroup = m_config->group("General");
1007     configGroup.writeEntry("kileprversion", KILE_PROJECTFILE_VERSION);
1008     configGroup.writeEntry("kileversion", kileFullVersion);
1009 
1010     return m_config->sync();
1011 }