File indexing completed on 2024-04-21 15:55:39

0001 /*****************************************************************************
0002 *   Copyright (C) 2004 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net)      *
0003 *             (C) 2006-2018 by Michel Ludwig (michel.ludwig@kdemail.net)     *
0004 *             (C) 2007 by Holger Danielsson (holger.danielsson@versanet.de)  *
0005 ******************************************************************************/
0006 
0007 /***************************************************************************
0008  *                                                                         *
0009  *   This program is free software; you can redistribute it and/or modify  *
0010  *   it under the terms of the GNU General Public License as published by  *
0011  *   the Free Software Foundation; either version 2 of the License, or     *
0012  *   (at your option) any later version.                                   *
0013  *                                                                         *
0014  ***************************************************************************/
0015 
0016 #include "kiledocmanager.h"
0017 
0018 #include <QAbstractItemView>
0019 #include <QApplication>
0020 #include <QDir>
0021 #include <QDropEvent>
0022 #include <QFile>
0023 #include <QLabel>
0024 #include <QList>
0025 #include <QMimeData>
0026 #include <QMimeType>
0027 #include <QMimeDatabase>
0028 #include <QProgressDialog>
0029 #include <QPushButton>
0030 #include <QTemporaryFile>
0031 #include <QTextCodec>
0032 #include <QUrl>
0033 
0034 #include <KConfigGroup>
0035 #include <KEncodingFileDialog>
0036 #include <KIO/CopyJob>
0037 #include <KIO/DeleteJob>
0038 #include <KIO/FileCopyJob>
0039 #include <KIO/StatJob>
0040 #include <KJobWidgets>
0041 #include <KLocalizedString>
0042 #include <KMessageBox>
0043 #include <KTextEditor/Document>
0044 #include <KTextEditor/Editor>
0045 #include <KTextEditor/View>
0046 
0047 #include "dialogs/cleandialog.h"
0048 #include "dialogs/listselector.h"
0049 #include "dialogs/managetemplatesdialog.h"
0050 #include "dialogs/newfilewizard.h"
0051 #include "dialogs/projectdialogs.h"
0052 #include "documentinfo.h"
0053 #include "errorhandler.h"
0054 #include "kileconfig.h"
0055 #include "kiledebug.h"
0056 #include "kileinfo.h"
0057 #include "kileproject.h"
0058 #include "kilestdtools.h"
0059 #include "kiletool_enums.h"
0060 #include "kiletool.h"
0061 #include "kiletoolmanager.h"
0062 #include "kileviewmanager.h"
0063 #include "livepreview.h"
0064 #include "parser/parsermanager.h"
0065 #include "scriptmanager.h"
0066 #include "templates.h"
0067 #include "utilities.h"
0068 #include "widgets/filebrowserwidget.h"
0069 #include "widgets/konsolewidget.h"
0070 #include "widgets/projectview.h"
0071 #include "widgets/structurewidget.h"
0072 
0073 /*
0074  * Newly created text documents have an empty URL and a non-empty document name.
0075  */
0076 
0077 #define MAX_NUMBER_OF_STORED_SETTINGS 50
0078 
0079 namespace KileDocument
0080 {
0081 
0082 Manager::Manager(KileInfo *info, QObject *parent, const char *name) :
0083     QObject(parent),
0084     m_ki(info),
0085     m_progressDialog(Q_NULLPTR),
0086     m_currentlySavingAll(false),
0087     m_currentlyOpeningFile(false)
0088 {
0089     setObjectName(name);
0090     m_editor = KTextEditor::Editor::instance();
0091     if(!m_editor) {
0092         KMessageBox::error(m_ki->mainWindow(), i18n("No editor component found. Please check your KDE installation."),
0093                            i18n("No editor component found."));
0094     }
0095 }
0096 
0097 Manager::~Manager()
0098 {
0099     KILE_DEBUG_MAIN << "==KileDocument::Manager::~Manager()=========";
0100     if(m_progressDialog.isNull()) {
0101         delete m_progressDialog.data();
0102     }
0103 }
0104 
0105 void Manager::readConfig()
0106 {
0107 }
0108 
0109 void Manager::writeConfig()
0110 {
0111 }
0112 
0113 void Manager::trashDoc(TextInfo *docinfo, KTextEditor::Document *doc /*= Q_NULLPTR */ )
0114 {
0115     KILE_DEBUG_MAIN << "==void Manager::trashDoc(" << docinfo->url().toLocalFile() << ")=====";
0116 
0117     if(m_ki->isOpen(docinfo->url())) {
0118         return;
0119     }
0120 
0121     if(doc) {
0122         doc = docinfo->getDoc();
0123     }
0124 
0125     //look for doc before we detach the docinfo
0126     //if we do it the other way around, docFor will always return nil
0127     if(!doc) {
0128         doc = docFor(docinfo->url());
0129     }
0130 
0131     KILE_DEBUG_MAIN << "DETACHING " << docinfo;
0132     docinfo->detach();
0133 
0134     KILE_DEBUG_MAIN << "\tTRASHING " <<  doc;
0135     if(!doc) {
0136         return;
0137     }
0138 
0139     KILE_DEBUG_MAIN << "just checking: docinfo->getDoc() =  " << docinfo->getDoc();
0140     KILE_DEBUG_MAIN << "just checking: docFor(docinfo->url()) = " << docFor(docinfo->url());
0141 
0142     for (int i = 0; i < m_textInfoList.count(); ++i) {
0143         if((m_textInfoList.at(i) != docinfo) && (m_textInfoList.at(i)->getDoc() == doc)) {
0144             KMessageBox::information(0, i18n("The internal structure of Kile is corrupted (probably due to a bug in Kile). Please select Save All from the File menu and close Kile.\nThe Kile team apologizes for any inconvenience and would appreciate a bug report."));
0145             qWarning() << "docinfo " << m_textInfoList.at(i) << " url " << m_textInfoList.at(i)->url().fileName() << " has a wild pointer!!!";
0146         }
0147     }
0148 
0149     KILE_DEBUG_MAIN << "DELETING doc";
0150     delete doc;
0151 }
0152 
0153 // update all Info's with changed user commands
0154 void Manager::updateInfos()
0155 {
0156     for(QList<TextInfo*>::iterator it = m_textInfoList.begin(); it != m_textInfoList.end(); ++it) {
0157         (*it)->updateStructLevelInfo();
0158     }
0159 }
0160 
0161 bool Manager::isOpeningFile()
0162 {
0163     return m_currentlyOpeningFile;
0164 }
0165 
0166 KTextEditor::Editor* Manager::getEditor()
0167 {
0168     return m_editor;
0169 }
0170 
0171 KTextEditor::Document* Manager::docFor(const QUrl &url)
0172 {
0173     for(QList<TextInfo*>::iterator it = m_textInfoList.begin(); it != m_textInfoList.end(); ++it) {
0174         TextInfo *info = *it;
0175 
0176         if(m_ki->similarOrEqualURL(info->url(), url)) {
0177             return info->getDoc();
0178         }
0179     }
0180 
0181     return Q_NULLPTR;
0182 }
0183 
0184 TextInfo* Manager::getInfo() const
0185 {
0186     KTextEditor::Document *doc = m_ki->activeTextDocument();
0187     if(doc) {
0188         return textInfoFor(doc);
0189     }
0190     else {
0191         return Q_NULLPTR;
0192     }
0193 }
0194 
0195 TextInfo* Manager::textInfoFor(const QUrl &url)
0196 {
0197     if(url.isEmpty()) {
0198         return Q_NULLPTR;
0199     }
0200 
0201     KILE_DEBUG_MAIN << "==KileInfo::textInfoFor(" << url << ")==========================";
0202 
0203     for(QList<TextInfo*>::iterator it = m_textInfoList.begin(); it != m_textInfoList.end(); ++it) {
0204         TextInfo *info = *it;
0205 
0206         if (info->url() == url) {
0207             return info;
0208         }
0209     }
0210 
0211     // the URL might belong to a TextInfo* which currently doesn't have a KTextEditor::Document*
0212     // associated with it, i.e. a project item which isn't open in the editor
0213     for(QList<KileProject*>::iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0214         KileProjectItem *item = (*it)->item(url);
0215 
0216         // all project items (across different projects) that share a URL have the same TextInfo*;
0217         // so, the first one we find is good enough
0218         if(item) {
0219             KileDocument::TextInfo *info = item->getInfo();
0220             if(info) {
0221                 return info;
0222             }
0223         }
0224     }
0225 
0226     KILE_DEBUG_MAIN << "\tCOULD NOT find info for " << url;
0227     return Q_NULLPTR;
0228 }
0229 
0230 TextInfo* Manager::textInfoFor(KTextEditor::Document* doc) const
0231 {
0232     if(!doc) {
0233         return Q_NULLPTR;
0234     }
0235 
0236     // TextInfo* objects that contain KTextEditor::Document* pointers must be open in the editor, i.e.
0237     // we don't have to look through the project items
0238     for(QList<TextInfo*>::const_iterator it = m_textInfoList.begin(); it != m_textInfoList.end(); ++it) {
0239         if((*it)->getDoc() == doc) {
0240             return (*it);
0241         }
0242     }
0243 
0244     KILE_DEBUG_MAIN << "\tCOULD NOT find info for" << doc->url() << "by searching via a KTextEditor::Document*";
0245     return Q_NULLPTR;
0246 }
0247 
0248 QUrl Manager::urlFor(TextInfo* textInfo)
0249 {
0250     KileProjectItem *item = itemFor(textInfo);
0251 
0252     QUrl url;
0253     if(item) {
0254         url = item->url(); // all items with 'textInfo' share the same URL
0255     }
0256     else {
0257         KTextEditor::Document *document = textInfo->getDoc();
0258         if(document) {
0259             url = document->url();
0260         }
0261     }
0262     return url;
0263 }
0264 
0265 KileProject* Manager::projectForMember(const QUrl &memberUrl)
0266 {
0267     for(QList<KileProject*>::iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0268         KileProject *project = *it;
0269 
0270         if(project->contains(memberUrl)) {
0271             return project;
0272         }
0273     }
0274     return Q_NULLPTR;
0275 }
0276 
0277 KileProject* Manager::projectFor(const QUrl &projecturl)
0278 {
0279     //find project with url = projecturl
0280     for(QList<KileProject*>::iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0281         KileProject *project = *it;
0282         if(project->url() == projecturl) {
0283             return project;
0284         }
0285     }
0286 
0287     return Q_NULLPTR;
0288 }
0289 
0290 KileProject* Manager::projectFor(const QString &name)
0291 {
0292     //find project with url = projecturl
0293     for(QList<KileProject*>::iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0294         KileProject *project = *it;
0295 
0296         if (project->name() == name) {
0297             return project;
0298         }
0299     }
0300 
0301     return Q_NULLPTR;
0302 }
0303 
0304 KileProjectItem* Manager::itemFor(const QUrl &url, KileProject *project /*=0L*/) const
0305 {
0306     if (!project) {
0307         for(QList<KileProject*>::const_iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0308             KileProject *curProject = *it;
0309 
0310             KileProjectItem *item = curProject->item(url);
0311             if(item) {
0312                 return item;
0313             }
0314         }
0315         return Q_NULLPTR;
0316     }
0317     else {
0318         return project->item(url);
0319     }
0320 }
0321 
0322 KileProjectItem* Manager::itemFor(TextInfo *docinfo, KileProject *project /*=0*/) const
0323 {
0324     if (!project) {
0325         for(QList<KileProject*>::const_iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0326             KileProject *curProject = *it;
0327 
0328             KileProjectItem *item = curProject->item(docinfo);
0329             if(item) {
0330                 return item;
0331             }
0332         }
0333         return Q_NULLPTR;
0334     }
0335     else {
0336         return project->item(docinfo);
0337     }
0338 }
0339 
0340 QList<KileProjectItem*> Manager::itemsFor(Info *docinfo) const
0341 {
0342     if(!docinfo) {
0343         return QList<KileProjectItem*>();
0344     }
0345 
0346     KILE_DEBUG_MAIN << "==KileInfo::itemsFor(" << docinfo->url().fileName() << ")============";
0347     QList<KileProjectItem*> list;
0348     for(QList<KileProject*>::const_iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0349         KileProject *project = *it;
0350 
0351         KILE_DEBUG_MAIN << "\tproject: " << (*it)->name();
0352         if(project->contains(docinfo)) {
0353             KILE_DEBUG_MAIN << "\t\tcontains";
0354             list.append(project->item(docinfo));
0355         }
0356     }
0357 
0358     return list;
0359 }
0360 
0361 QList<KileProjectItem*> Manager::itemsFor(const QUrl &url) const
0362 {
0363     QList<KileProjectItem*> list;
0364     for(QList<KileProject*>::const_iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
0365         KileProject *project = *it;
0366 
0367         if(project->contains(url)) {
0368             list.append(project->item(url));
0369         }
0370     }
0371 
0372     return list;
0373 }
0374 
0375 bool Manager::isProjectOpen()
0376 {
0377     return ( m_projects.count() > 0 );
0378 }
0379 
0380 KileProject* Manager::activeProject()
0381 {
0382     KTextEditor::Document *doc = m_ki->activeTextDocument();
0383 
0384     if (doc) {
0385         return projectForMember(doc->url());
0386     }
0387     else {
0388         return Q_NULLPTR;
0389     }
0390 }
0391 
0392 KileProjectItem* Manager::activeProjectItem()
0393 {
0394     KileProject *curpr = activeProject();
0395     KTextEditor::Document *doc = m_ki->activeTextDocument();
0396 
0397     if (curpr && doc) {
0398         QList<KileProjectItem*> list = curpr->items();
0399 
0400         for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
0401             KileProjectItem *item = *it;
0402 
0403             if (item->url() == doc->url()) {
0404                 return item;
0405             }
0406         }
0407     }
0408 
0409     return Q_NULLPTR;
0410 }
0411 
0412 TextInfo* Manager::createTextDocumentInfo(KileDocument::Type type, const QUrl &url, const QUrl& baseDirectory)
0413 {
0414     TextInfo *docinfo = Q_NULLPTR;
0415 
0416     // check whether this URL belongs to an opened project and a TextInfo* object has already
0417     // been created for that URL
0418     docinfo = textInfoFor(url);
0419 
0420     if(!docinfo) {
0421         switch(type) {
0422         case LaTeX:
0423             KILE_DEBUG_MAIN << "CREATING LaTeXInfo for " << url.url();
0424             docinfo = new LaTeXInfo(m_ki->extensions(),
0425                                     m_ki->abbreviationManager(),
0426                                     m_ki->latexCommands(),
0427                                     m_ki->editorExtension(),
0428                                     m_ki->configurationManager(),
0429                                     m_ki->codeCompletionManager(),
0430                                     m_ki->livePreviewManager(),
0431                                     m_ki->viewManager(),
0432                                     m_ki->parserManager());
0433             break;
0434         case BibTeX:
0435             KILE_DEBUG_MAIN << "CREATING BibInfo for " << url.url();
0436             docinfo = new BibInfo(m_ki->extensions(),
0437                                   m_ki->abbreviationManager(),
0438                                   m_ki->parserManager(),
0439                                   m_ki->latexCommands());
0440             break;
0441         case Script:
0442             KILE_DEBUG_MAIN << "CREATING ScriptInfo for " << url.url();
0443             docinfo = new ScriptInfo(m_ki->extensions(),
0444                                      m_ki->abbreviationManager(),
0445                                      m_ki->parserManager());
0446             break;
0447         case Undefined: // fall through
0448         case Text: // fall through
0449         default:
0450             KILE_DEBUG_MAIN << "CREATING TextInfo for " << url.url();
0451             docinfo = new TextInfo(m_ki->extensions(),
0452                                    m_ki->abbreviationManager(),
0453                                    m_ki->parserManager());
0454         }
0455         docinfo->setBaseDirectory(baseDirectory);
0456         emit(documentInfoCreated(docinfo));
0457         m_textInfoList.append(docinfo);
0458     }
0459 
0460     KILE_DEBUG_MAIN << "DOCINFO: returning " << docinfo << " " << docinfo->url().fileName();
0461     return docinfo;
0462 }
0463 
0464 void Manager::recreateTextDocumentInfo(TextInfo *oldinfo)
0465 {
0466     QList<KileProjectItem*> list = itemsFor(oldinfo);
0467     QUrl url = oldinfo->url();
0468     TextInfo *newinfo = createTextDocumentInfo(m_ki->extensions()->determineDocumentType(url), url, oldinfo->getBaseDirectory());
0469 
0470     newinfo->setDoc(oldinfo->getDoc());
0471 
0472     for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
0473         (*it)->setInfo(newinfo);
0474     }
0475 
0476     removeTextDocumentInfo(oldinfo);
0477 
0478     emit(updateStructure(false, newinfo));
0479 }
0480 
0481 bool Manager::removeTextDocumentInfo(TextInfo *docinfo, bool closingproject /* = false */)
0482 {
0483     KILE_DEBUG_MAIN << "==Manager::removeTextDocumentInfo(Info *docinfo)=====";
0484     QList<KileProjectItem*> itms = itemsFor(docinfo);
0485     bool oneItem = false;
0486     if(itms.count() == 1) {
0487         oneItem = true;
0488     }
0489 
0490     if(itms.count() == 0 || ( closingproject && oneItem )) {
0491         KILE_DEBUG_MAIN << "\tremoving " << docinfo <<  " count = " << m_textInfoList.count();
0492 
0493         // we still have to stop parsing for 'docinfo'
0494         QUrl url = urlFor(docinfo);
0495         if(url.isValid()) {
0496             m_ki->parserManager()->stopDocumentParsing(url);
0497         }
0498 
0499         m_textInfoList.removeAll(docinfo);
0500 
0501         emit(closingDocument(docinfo));
0502 
0503         cleanupDocumentInfoForProjectItems(docinfo);
0504         delete docinfo;
0505 
0506         return true;
0507     }
0508 
0509     KILE_DEBUG_MAIN << "\tnot removing " << docinfo;
0510     return false;
0511 }
0512 
0513 
0514 KTextEditor::Document* Manager::createDocument(const QUrl &url, TextInfo *docinfo, const QString& encoding,
0515         const QString& mode,
0516         const QString& highlight)
0517 {
0518     KILE_DEBUG_MAIN << "==KTextEditor::Document* Manager::createDocument()===========";
0519 
0520     KTextEditor::Document *doc = Q_NULLPTR;
0521 
0522     if(!m_editor) {
0523         return Q_NULLPTR;
0524     }
0525 
0526     doc = docFor(url);
0527     if (doc) {
0528         qWarning() << url << " already has a document!";
0529         return doc;
0530     }
0531     doc = m_editor->createDocument(Q_NULLPTR);
0532     KILE_DEBUG_MAIN << "appending document " <<  doc;
0533 
0534     connect(doc, &KTextEditor::Document::canceled, [=] (const QString &errMsg) {
0535         if(!errMsg.isEmpty()) {
0536             KMessageBox::error(m_ki->mainWindow(), i18n("The URL \"%1\" couldn't be opened.\n\n%2", url.toDisplayString(), errMsg),
0537                                i18n("Cannot open URL"));
0538         }
0539         else {
0540             KMessageBox::error(m_ki->mainWindow(), i18n("The URL \"%1\" couldn't be opened.", url.toDisplayString()), i18n("Cannot open URL"));
0541         }
0542     });
0543 
0544     docinfo->setDoc(doc); // do this here to set up all the signals correctly in 'TextInfo'
0545     doc->setEncoding(encoding);
0546 
0547     KILE_DEBUG_MAIN << "url is = " << docinfo->url();
0548 
0549     if(!url.isEmpty()) {
0550         bool r = doc->openUrl(url);
0551         if(!r) {
0552             KILE_WARNING_MAIN << "couldn't open the url" << url;
0553             docinfo->detach();
0554             delete doc;
0555             return Q_NULLPTR;
0556         }
0557         // don't add scripts to the recent files
0558         if(r && docinfo->getType() != Script) {
0559             emit(addToRecentFiles(url));
0560         }
0561     }
0562 
0563     //handle changes of the document
0564     connect(doc, &KTextEditor::Document::documentNameChanged, this, &KileDocument::Manager::documentNameChanged);
0565     connect(doc, &KTextEditor::Document::documentUrlChanged, this, &KileDocument::Manager::documentUrlChanged);
0566     connect(doc, &KTextEditor::Document::readWriteChanged, this, &KileDocument::Manager::documentReadWriteStateChanged);
0567 
0568     connect(doc, &KTextEditor::Document::modifiedChanged, this, &KileDocument::Manager::newDocumentStatus);
0569     KTextEditor::ModificationInterface *modificationInterface = qobject_cast<KTextEditor::ModificationInterface*>(doc);
0570     if(modificationInterface) {
0571         modificationInterface->setModifiedOnDiskWarning(true);
0572         connect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)),
0573                 this, SIGNAL(documentModificationStatusChanged(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)));
0574     }
0575 
0576     if(!mode.isEmpty()) {
0577         docinfo->setMode(mode);     // this ensures that mode passed with the mode parameter is actually used
0578     }
0579     if(!highlight.isEmpty()) {
0580         docinfo->setHighlightingMode(highlight);
0581     }
0582 
0583     {
0584         // FIXME: the whole structure updating stuff needs to be rewritten; updates should originate from
0585         //        the docinfo only, i.e. the structure view should just react to changes!
0586 
0587         // small 'trick' to select the right overloaded slot:
0588         void (KileWidget::StructureWidget::*slot)(KileDocument::Info *) = &KileWidget::StructureWidget::update;
0589         connect(docinfo, &KileDocument::Info::completed, m_ki->structureWidget(), slot);
0590     }
0591 
0592     KILE_DEBUG_MAIN << "createDocument: url " << doc->url();
0593     KILE_DEBUG_MAIN << "createDocument: SANITY check: " << (docinfo->getDoc() == docFor(docinfo->url()));
0594     return doc;
0595 }
0596 
0597 // WARNING: 'item' must have been set up with a TextInfo* object already
0598 KTextEditor::View* Manager::loadItem(KileDocument::Type type, KileProjectItem *item, const QString & text, bool openProjectItemViews)
0599 {
0600     KTextEditor::View *view = Q_NULLPTR;
0601 
0602     KILE_DEBUG_MAIN << "==loadItem(" << item->url() << ")======";
0603 
0604     if(item->type() != KileProjectItem::Image) {
0605         view = loadText(type, item->url(), item->encoding(), openProjectItemViews && item->isOpen(), item->mode(), item->highlight(), text);
0606         KILE_DEBUG_MAIN << "\tloadItem: docfor = " << docFor(item->url());
0607 
0608         TextInfo *docinfo = item->getInfo();
0609 
0610         KILE_DEBUG_MAIN << "\tloadItem: docinfo = " << docinfo << " doc = " << docinfo->getDoc() << " docfor = " << docFor(docinfo->url());
0611         if ( docinfo->getDoc() != docFor(docinfo->url()) ) qWarning() << "docinfo->getDoc() != docFor()";
0612     }
0613     else {
0614         KILE_DEBUG_MAIN << "\tloadItem: no document generated";
0615         TextInfo *docinfo = item->getInfo();
0616 
0617         if(!docFor(item->url())) {
0618             docinfo->detach();
0619             KILE_DEBUG_MAIN << "\t\t\tdetached";
0620         }
0621     }
0622 
0623     return view;
0624 }
0625 
0626 KTextEditor::View* Manager::loadText(KileDocument::Type type, const QUrl &url, const QString& encoding,
0627                                      bool create,
0628                                      const QString& mode,
0629                                      const QString& highlight,
0630                                      const QString& text,
0631                                      int index,
0632                                      const QUrl &baseDirectory)
0633 {
0634     KILE_DEBUG_MAIN << "==loadText(" << url.url() << ")=================";
0635     //if doc already opened, update the structure view and return the view
0636     if(!url.isEmpty() && m_ki->isOpen(url)) {
0637         return m_ki->viewManager()->switchToTextView(url);
0638     }
0639 
0640     TextInfo *docinfo = createTextDocumentInfo(type, url, baseDirectory);
0641     KTextEditor::Document *doc = createDocument(url, docinfo, encoding, mode, highlight);
0642     if(!doc) {
0643         removeTextDocumentInfo(docinfo);
0644         return Q_NULLPTR;
0645     }
0646 
0647     m_ki->structureWidget()->clean(docinfo);
0648 
0649     if(!text.isEmpty()) {
0650         doc->setText(text);
0651     }
0652 
0653     if (doc && create) {
0654         return m_ki->viewManager()->createTextView(docinfo, index);
0655     }
0656 
0657     KILE_DEBUG_MAIN << "just after createView()";
0658     KILE_DEBUG_MAIN << "\tdocinfo = " << docinfo << " doc = " << docinfo->getDoc() << " docfor = " << docFor(docinfo->url());
0659 
0660     return Q_NULLPTR;
0661 }
0662 
0663 //FIXME: template stuff should be in own class
0664 KTextEditor::View* Manager::loadTemplate(TemplateItem *sel)
0665 {
0666     KILE_DEBUG_MAIN << "templateitem *sel = " << sel;
0667     QString text;
0668 
0669     if(!sel) {
0670         return Q_NULLPTR;
0671     }
0672 
0673     if (sel->name() != KileTemplate::Manager::defaultEmptyTemplateCaption()
0674             && sel->name() != KileTemplate::Manager::defaultEmptyLaTeXTemplateCaption()
0675             && sel->name() != KileTemplate::Manager::defaultEmptyBibTeXTemplateCaption()) {
0676         if(!m_editor) {
0677             return Q_NULLPTR;
0678         }
0679         //create a new document to open the template in
0680         KTextEditor::Document *tempdoc = m_editor->createDocument(Q_NULLPTR);
0681 
0682         if (!tempdoc->openUrl(QUrl::fromLocalFile(sel->path()))) {
0683             KMessageBox::error(m_ki->mainWindow(), i18n("Could not find template: %1", sel->name()), i18n("File Not Found"));
0684         }
0685         else {
0686             //substitute templates variables
0687             text = tempdoc->text();
0688             delete tempdoc;
0689             replaceTemplateVariables(text);
0690         }
0691     }
0692 
0693     KileDocument::Type type = sel->type();
0694     //always set the base directory for scripts
0695     return createDocumentWithText(text, type, QString(), (type == KileDocument::Script ? QUrl::fromLocalFile(m_ki->scriptManager()->getLocalScriptDirectory()) : QUrl()));
0696 }
0697 
0698 KTextEditor::View* Manager::createDocumentWithText(const QString& text, KileDocument::Type type /* = KileDocument::Undefined */, const QString& /* extension */, const QUrl &baseDirectory)
0699 {
0700     KTextEditor::View *view = loadText(type, QUrl(), QString(), true, QString(), QString(), text, -1, baseDirectory);
0701     if(view) {
0702         //FIXME this shouldn't be necessary!!!
0703         view->document()->setModified(true);
0704         newDocumentStatus(view->document());
0705     }
0706 
0707     return view;
0708 }
0709 
0710 KTextEditor::View* Manager::createNewJScript()
0711 {
0712     KTextEditor::View *view = createDocumentWithText(QString(), Script, "js", QUrl::fromLocalFile(m_ki->scriptManager()->getLocalScriptDirectory()));
0713     emit(updateStructure(false, Q_NULLPTR));
0714     emit(updateModeStatus());
0715     return view;
0716 }
0717 
0718 KTextEditor::View* Manager::createNewLaTeXDocument()
0719 {
0720     KTextEditor::View *view = createDocumentWithText(QString(), LaTeX);
0721     emit(updateStructure(false, Q_NULLPTR));
0722     emit(updateModeStatus());
0723     return view;
0724 }
0725 
0726 void Manager::replaceTemplateVariables(QString &line)
0727 {
0728     line=line.replace("$$AUTHOR$$", KileConfig::author());
0729     line=line.replace("$$DOCUMENTCLASSOPTIONS$$", KileConfig::documentClassOptions());
0730     if (!KileConfig::templateEncoding().isEmpty()) {
0731         line=line.replace("$$INPUTENCODING$$", "\\usepackage["+ KileConfig::templateEncoding() +"]{inputenc}");
0732     }
0733     else {
0734         line = line.remove("$$INPUTENCODING$$");
0735     }
0736 }
0737 
0738 void Manager::createTemplate()
0739 {
0740     KTextEditor::View *view = m_ki->viewManager()->currentTextView();
0741     if (view) {
0742         if (view->document()->isModified()) {
0743             KMessageBox::information(m_ki->mainWindow(),i18n("Please save the file first."));
0744             return;
0745         }
0746     }
0747     else {
0748         KMessageBox::information(m_ki->mainWindow(),i18n("Open/create a document first."));
0749         return;
0750     }
0751 
0752     QUrl url = view->document()->url();
0753     KileDocument::Type type = m_ki->extensions()->determineDocumentType(url);
0754 
0755     if(type == KileDocument::Undefined || type == KileDocument::Text) {
0756         KMessageBox::information(m_ki->mainWindow(),i18n("A template for this type of document cannot be created."));
0757         return;
0758     }
0759 
0760     ManageTemplatesDialog mtd(m_ki->templateManager(), url, i18n("Create Template From Document"));
0761     mtd.exec();
0762 }
0763 
0764 void Manager::removeTemplate()
0765 {
0766     ManageTemplatesDialog mtd(m_ki->templateManager(), i18n("Remove Template"));
0767     mtd.exec();
0768 }
0769 
0770 void Manager::fileNew(KileDocument::Type type)
0771 {
0772     NewFileWizard *nfw = new NewFileWizard(m_ki->templateManager(), type, m_ki->mainWindow());
0773     if(nfw->exec()) {
0774         KTextEditor::View *view = loadTemplate(nfw->getSelection());
0775         if(view) {
0776             if(nfw->useWizard()) {
0777                 emit(startWizard());
0778             }
0779             emit(updateStructure(false, Q_NULLPTR));
0780             emit(updateModeStatus());
0781         }
0782     }
0783     delete nfw;
0784 }
0785 
0786 void Manager::fileNewScript()
0787 {
0788     fileNew(KileDocument::Script);
0789 }
0790 
0791 void Manager::fileNew(const QUrl &url)
0792 {
0793     QFile file(url.toLocalFile());
0794     
0795     //create the directory structure first
0796     QFileInfo fileInfo(file);
0797     QDir dir = fileInfo.absolutePath();
0798     dir.mkpath(".");
0799     
0800     //create an empty file
0801     file.open(QIODevice::ReadWrite);
0802     file.close();
0803 
0804     fileOpen(url, QString());
0805 }
0806 
0807 void Manager::fileOpen()
0808 {
0809     //determine the starting dir for the file dialog
0810     QString compileName = m_ki->getCompileName();
0811     QString currentDir;
0812     if(QFileInfo::exists(compileName)) {
0813         currentDir = QFileInfo(compileName).absolutePath();
0814     }
0815     else {
0816         currentDir = m_ki->fileSelector()->currentUrl().toLocalFile();
0817     }
0818 
0819     // use a filter for fileOpen dialog
0820     Extensions *extensions = m_ki->extensions();
0821     QString filter = extensions->fileFilterKDEStyle(true, {KileDocument::Extensions::TEX,
0822                      KileDocument::Extensions::PACKAGES,
0823                      KileDocument::Extensions::BIB,
0824                      KileDocument::Extensions::METAPOST
0825                                                           });
0826 
0827     // try to get the current encoding, this is kind of ugly ...
0828     QString encoding = m_ki->toolManager()->config()->group("Kate Document Defaults").readEntry("Encoding","");
0829 
0830     //get the URLs
0831     KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenUrlsAndEncoding(encoding, QUrl::fromLocalFile(currentDir), filter, m_ki->mainWindow(), i18n("Open Files"));
0832 
0833     //open them
0834     const QList<QUrl>& urls = result.URLs;
0835     for (QList<QUrl>::ConstIterator i = urls.begin(); i != urls.end(); ++i) {
0836         const QUrl& url = *i;
0837         if(m_ki->extensions()->isProjectFile(url)) { // this can happen... (bug 317432)
0838             KILE_DEBUG_MAIN << "file is a project file:" << url;
0839             projectOpen(url);
0840             continue;
0841         }
0842 
0843         fileOpen(url, result.encoding);
0844     }
0845 }
0846 
0847 void Manager::fileSelected(const KFileItem& file)
0848 {
0849     fileSelected(file.url());
0850 }
0851 
0852 void Manager::fileSelected(const KileProjectItem * item)
0853 {
0854     fileOpen(item->url(), item->encoding());
0855 }
0856 
0857 void Manager::fileSelected(const QUrl &url)
0858 {
0859     fileOpen(url, QString());
0860 }
0861 
0862 void Manager::saveURL(const QUrl &url)
0863 {
0864     KTextEditor::Document *doc = docFor(url);
0865     if(doc) {
0866         doc->save();
0867     }
0868 }
0869 
0870 void Manager::newDocumentStatus(KTextEditor::Document *doc)
0871 {
0872     KILE_DEBUG_MAIN << "void Manager::newDocumentStatus(Kate::Document)" << Qt::endl;
0873     if(!doc) {
0874         return;
0875     }
0876 
0877     // sync terminal
0878     m_ki->texKonsole()->sync();
0879 
0880     emit(documentModificationStatusChanged(doc, doc->isModified(), KTextEditor::ModificationInterface::OnDiskUnmodified));
0881 }
0882 
0883 bool Manager::fileSaveAll(bool disUntitled)
0884 {
0885     // this can occur when autosaving should take place when we
0886     // are still busy with it (KIO::NetAccess keeps the event loop running)
0887     if(m_currentlySavingAll) {
0888         return true;
0889     }
0890     m_currentlySavingAll = true;
0891     KTextEditor::View *view = Q_NULLPTR;
0892     QFileInfo fi;
0893     bool oneSaveFailed = false;
0894     QUrl url, backupUrl;
0895 
0896     KILE_DEBUG_MAIN << "===Kile::fileSaveAll(disUntitled = " << disUntitled <<")";
0897 
0898     for(int i = 0; i < m_ki->viewManager()->textViewCount(); ++i) {
0899         view = m_ki->viewManager()->textView(i);
0900 
0901         if(view && view->document()->isModified()) {
0902             url = view->document()->url();
0903             fi.setFile(url.toLocalFile());
0904 
0905             if(!disUntitled || !(disUntitled && url.isEmpty()))  { // either we don't disregard untitled docs, or the doc has a title
0906                 KILE_DEBUG_MAIN << "trying to save: " << url.toLocalFile();
0907                 bool saveResult = view->document()->documentSave();
0908                 fi.refresh();
0909 
0910                 if(!saveResult) {
0911                     oneSaveFailed = true;
0912                     m_ki->errorHandler()->printMessage(KileTool::Error,
0913                                                        i18n("Kile encountered problems while saving the file %1. Do you have enough free disk space left?", url.toDisplayString()),
0914                                                        i18n("Saving"));
0915                 }
0916             }
0917         }
0918     }
0919 
0920     /*
0921      This may look superfluos but actually it is not, in the case of multiple modified docs it ensures that the structure view keeps synchronized with the currentTextView
0922      And if we only have one masterdoc or none nothing goes wrong.
0923     */
0924     emit(updateStructure(false, Q_NULLPTR));
0925     m_currentlySavingAll = false;
0926     return !oneSaveFailed;
0927 }
0928 
0929 void Manager::fileSaveCompiledDocument()
0930 {
0931     const QString compiledDocumentFileName = m_ki->livePreviewManager()->getPreviewFile();
0932 
0933     QFileInfo fileInfo(compiledDocumentFileName);
0934     if(!fileInfo.exists() || !fileInfo.isReadable()) {
0935         KILE_WARNING_MAIN << "file doesn't exist or cannot be read:" << compiledDocumentFileName;
0936         return;
0937     }
0938     QMimeDatabase db;
0939 
0940     QStringList nameFilters;
0941     {
0942         QMimeType detectedMimeType = db.mimeTypeForFile(fileInfo);
0943         if(!detectedMimeType.isDefault()) { // All files (*)
0944             nameFilters << detectedMimeType.filterString();
0945         }
0946     }
0947     nameFilters << i18n("Any files (*)");
0948 
0949     QFileDialog *dlg = new QFileDialog(m_ki->mainWindow(), i18n("Save Compiled Document As..."));
0950     dlg->setModal(true);
0951     dlg->setNameFilters(nameFilters);
0952     dlg->selectFile(fileInfo.fileName());
0953     dlg->setAcceptMode(QFileDialog::AcceptSave);
0954 
0955     connect(dlg,  &QFileDialog::urlSelected,
0956             this, [compiledDocumentFileName](const QUrl& url) {
0957                       if(!url.isValid()) {
0958                           return;
0959                       }
0960                       // the QFileDialog will take care of asking for overwrite permission (if the chosen file exists already)
0961                       KIO::CopyJob *copyJob = KIO::copy(QUrl::fromLocalFile(compiledDocumentFileName), url, KIO::Overwrite);
0962                       QObject::connect(copyJob, &KIO::CopyJob::finished, copyJob, &QObject::deleteLater);
0963                   });
0964     dlg->exec();
0965 }
0966 
0967 TextInfo* Manager::fileOpen(const QUrl &url, const QString& encoding, int index)
0968 {
0969     m_currentlyOpeningFile = true;
0970     KILE_DEBUG_MAIN << "==Kile::fileOpen==========================";
0971 
0972     if(url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) {
0973         KILE_DEBUG_MAIN << "tried to open directory" << url;
0974         KMessageBox::error(m_ki->mainWindow(), i18n("The URL \"%1\" cannot be opened\nas it is a directory.", url.toDisplayString()),
0975                            i18n("Cannot open directory"));
0976         m_currentlyOpeningFile = false; // has to be before the 'switchToTextView' call as
0977         // it emits signals that are handled by the live preview manager
0978         return Q_NULLPTR;
0979     }
0980 
0981     KILE_DEBUG_MAIN << "url is " << url.url();
0982     const QUrl realurl = KileUtilities::canonicalUrl(url);
0983     KILE_DEBUG_MAIN << "canonical url is " << realurl.url();
0984 
0985     if(m_ki->isOpen(realurl)) {
0986         m_currentlyOpeningFile = false; // has to be before the 'switchToTextView' call as
0987         // it emits signals that are handled by the live preview manager
0988         m_ki->viewManager()->switchToTextView(realurl);
0989         return textInfoFor(realurl);
0990     }
0991 
0992     KTextEditor::View *view = loadText(m_ki->extensions()->determineDocumentType(realurl), realurl, encoding, true, QString(), QString(), QString(), index);
0993     if(!view) {
0994         m_currentlyOpeningFile = false; // has to be before the 'switchToTextView' call as
0995         // it emits signals that are handled by the live preview manager
0996         return Q_NULLPTR;
0997     }
0998     QList<KileProjectItem*> itemList = itemsFor(realurl);
0999     TextInfo *textInfo = textInfoFor(realurl);
1000 
1001     for(QList<KileProjectItem*>::iterator it = itemList.begin(); it != itemList.end(); ++it) {
1002         (*it)->setInfo(textInfo);
1003     }
1004 
1005     if(itemList.isEmpty()) {
1006         emit addToProjectView(realurl);
1007         loadDocumentAndViewSettings(textInfo);
1008     }
1009     else if(view) {
1010         KileProjectItem *item = itemList.first();
1011         item->loadDocumentAndViewSettings();
1012     }
1013 
1014     emit(updateStructure(false, Q_NULLPTR));
1015     emit(updateModeStatus());
1016     // update undefined references in this file
1017     emit(updateReferences(textInfoFor(realurl)));
1018     m_currentlyOpeningFile = false;
1019     emit documentOpened(textInfo);
1020     return textInfo;
1021 }
1022 
1023 bool Manager::fileSave(KTextEditor::View *view)
1024 {
1025     // the 'data' property can be set by the view manager
1026     QAction *action = dynamic_cast<QAction*>(QObject::sender());
1027     if(action) {
1028         QVariant var = action->data();
1029         if(!view && var.isValid()) {
1030             view = var.value<KTextEditor::View*>();
1031             // the 'data' property for the relevant actions is cleared
1032             // inside the view manager
1033         }
1034     }
1035     if(!view) {
1036         view = m_ki->viewManager()->currentTextView();
1037     }
1038     if(!view) {
1039         return false;
1040     }
1041     QUrl url = view->document()->url();
1042     if(url.isEmpty()) { // newly created document
1043         return fileSaveAs(view);
1044     }
1045     else {
1046         bool ret = view->document()->documentSave();
1047         emit(updateStructure(false, textInfoFor(view->document())));
1048         return ret;
1049     }
1050 }
1051 
1052 bool Manager::fileSaveAs(KTextEditor::View* view)
1053 {
1054     // the 'data' property can be set by the view manager
1055     QAction *action = dynamic_cast<QAction*>(QObject::sender());
1056     if(action) {
1057         QVariant var = action->data();
1058         if(!view && var.isValid()) {
1059             view = var.value<KTextEditor::View*>();
1060             // the 'data' property for the relevant actions is cleared
1061             // inside the view manager
1062         }
1063     }
1064     if(!view) {
1065         view = m_ki->viewManager()->currentTextView();
1066     }
1067     if(!view) {
1068         return false;
1069     }
1070 
1071     KTextEditor::Document* doc = view->document();
1072     Q_ASSERT(doc);
1073     KileDocument::TextInfo* info = textInfoFor(doc);
1074     Q_ASSERT(info);
1075     QUrl startUrl = info->url();
1076     QUrl oldURL = info->url();
1077     if(startUrl.isEmpty()) {
1078         QUrl baseDirectory = info->getBaseDirectory();
1079         if(baseDirectory.isEmpty()) {
1080             startUrl = QUrl("kfiledialog:///KILE_LATEX_SAVE_DIR");
1081         }
1082         else {
1083             startUrl = baseDirectory;
1084         }
1085     }
1086 
1087     KILE_DEBUG_MAIN << "startUrl is " << startUrl;
1088     KEncodingFileDialog::Result result;
1089     QUrl saveAsUrl;
1090     while(true) {
1091         QString filter = m_ki->extensions()->fileFilterKDEStyle(true, info->getFileFilter());
1092 
1093         result = KEncodingFileDialog::getSaveUrlAndEncoding(doc->encoding(), startUrl, filter, m_ki->mainWindow(), i18n("Save File"));
1094         if(result.URLs.isEmpty() || result.URLs.first().isEmpty()) {
1095             return false;
1096         }
1097         saveAsUrl = result.URLs.first();
1098         if(info->getType() == KileDocument::LaTeX) {
1099             saveAsUrl = Info::makeValidTeXURL(saveAsUrl, m_ki->mainWindow(),
1100                                             m_ki->extensions()->isTexFile(saveAsUrl), false); // don't check for file existence
1101         }
1102 
1103         if(!checkForFileOverwritePermission(saveAsUrl)) {
1104             continue;
1105         }
1106         break;
1107     }
1108     doc->setEncoding(result.encoding);
1109     if(!doc->saveAs(saveAsUrl)) {
1110         return false;
1111     }
1112     if(oldURL != saveAsUrl) {
1113         if(info->isDocumentTypePromotionAllowed()) {
1114             recreateTextDocumentInfo(info);
1115             info = textInfoFor(doc);
1116         }
1117         m_ki->structureWidget()->updateUrl(info);
1118         emit addToRecentFiles(saveAsUrl);
1119         emit addToProjectView(doc->url());
1120     }
1121     emit(documentSavedAs(view, info));
1122     return true;
1123 }
1124 
1125 bool Manager::checkForFileOverwritePermission(const QUrl& url)
1126 {
1127     auto statJob = KIO::statDetails(url, KIO::StatJob::SourceSide, KIO::StatNoDetails);
1128     KJobWidgets::setWindow(statJob, m_ki->mainWindow());
1129     if (statJob->exec()) { // check for writing possibility
1130         int r =  KMessageBox::warningContinueCancel(m_ki->mainWindow(), i18n("A file with the name \"%1\" exists already. Do you want to overwrite it?",
1131                                                     url.fileName()), i18n("Overwrite File?"), KStandardGuiItem::overwrite());
1132         if(r != KMessageBox::Continue) {
1133             return false;
1134         }
1135     }
1136     return true;
1137 }
1138 
1139 bool Manager::fileCloseAllOthers(KTextEditor::View *currentView)
1140 {
1141     // the 'data' property can be set by the view manager
1142     QAction *action = dynamic_cast<QAction*>(QObject::sender());
1143     if(action) {
1144         QVariant var = action->data();
1145         if(!currentView && var.isValid()) {
1146             // the 'data' property for the relevant actions is cleared
1147             // inside the view manager
1148             currentView = var.value<KTextEditor::View*>();
1149         }
1150     }
1151     if(!currentView) {
1152         currentView = m_ki->viewManager()->currentTextView();
1153     }
1154     if(!currentView) {
1155         return false;
1156     }
1157 
1158     QList<KTextEditor::View*> viewList;
1159     for(int i = 0; i < m_ki->viewManager()->textViewCount(); ++i) {
1160         KTextEditor::View *view = m_ki->viewManager()->textView(i);
1161         if(currentView == view) {
1162             continue;
1163         }
1164         viewList.push_back(view);
1165 
1166     }
1167     for(QList<KTextEditor::View*>::iterator it = viewList.begin();
1168             it != viewList.end(); ++it) {
1169         if (!fileClose(*it)) {
1170             return false;
1171         }
1172     }
1173     return true;
1174 }
1175 
1176 bool Manager::fileCloseAll()
1177 {
1178     KTextEditor::View * view = m_ki->viewManager()->currentTextView();
1179 
1180     //assumes one view per doc here
1181     while(m_ki->viewManager()->textViewCount() > 0) {
1182         view = m_ki->viewManager()->textView(0);
1183         if (!fileClose(view->document())) {
1184             return false;
1185         }
1186     }
1187     return true;
1188 }
1189 
1190 bool Manager::fileClose(const QUrl &url)
1191 {
1192     KTextEditor::Document *doc = docFor(url);
1193     if(!doc) {
1194         return true;
1195     }
1196     else {
1197         return fileClose(doc);
1198     }
1199 }
1200 
1201 bool Manager::fileClose(KTextEditor::View *view)
1202 {
1203     // the 'data' property can be set by the view manager
1204     QAction *action = dynamic_cast<QAction*>(QObject::sender());
1205     if(action) {
1206         QVariant var = action->data();
1207         if(!view && var.isValid()) {
1208             view = var.value<KTextEditor::View*>();
1209             // the 'data' property for the relevant actions is cleared
1210             // inside the view manager
1211         }
1212     }
1213     if(!view) {
1214         view = m_ki->viewManager()->currentTextView();
1215     }
1216     if(!view) {
1217         return false;
1218     }
1219     return fileClose(view->document());
1220 }
1221 
1222 bool Manager::fileClose(KTextEditor::Document *doc /* = 0L*/, bool closingproject /*= false*/)
1223 {
1224     KILE_DEBUG_MAIN << "==Kile::fileClose==========================";
1225 
1226     if(!doc) {
1227         doc = m_ki->activeTextDocument();
1228     }
1229 
1230     if(!doc) {
1231         return true;
1232     }
1233 
1234     //FIXME: remove from docinfo map, remove from dirwatch
1235     KILE_DEBUG_MAIN << "doc->url().toLocalFile()=" << doc->url().toLocalFile();
1236 
1237     const QUrl url = doc->url();
1238 
1239     TextInfo *docinfo= textInfoFor(doc);
1240     if(!docinfo) {
1241         qWarning() << "no DOCINFO for " << url.url();
1242         return true;
1243     }
1244     bool inProject = false;
1245     QList<KileProjectItem*> items = itemsFor(docinfo);
1246     for(QList<KileProjectItem*>::iterator it = items.begin(); it != items.end(); ++it) {
1247         KileProjectItem *item = *it;
1248 
1249         //FIXME: refactor here
1250         if(item && doc) {
1251             storeProjectItem(item, doc);
1252             inProject = true;
1253         }
1254     }
1255 
1256     if(!inProject) {
1257         KILE_DEBUG_MAIN << "not in project";
1258         saveDocumentAndViewSettings(docinfo);
1259     }
1260 
1261     if(doc->closeUrl()) {
1262         // docinfo may have been recreated from 'Untitled' doc to a named doc
1263         if(url.isEmpty()) {
1264             docinfo= textInfoFor(doc);
1265         }
1266 
1267         if(KileConfig::cleanUpAfterClose()) {
1268             cleanUpTempFiles(url, true); // yes we pass here url and not docinfo->url()
1269         }
1270 
1271         //FIXME: use signal/slot
1272         if( doc->views().count() > 0) {
1273             m_ki->viewManager()->removeView(doc->views().first());
1274         }
1275         //remove the decorations
1276 
1277         trashDoc(docinfo, doc);
1278         m_ki->structureWidget()->clean(docinfo);
1279         removeTextDocumentInfo(docinfo, closingproject);
1280 
1281         emit removeFromProjectView(url);
1282         emit updateModeStatus();
1283     }
1284     else {
1285         return false;
1286     }
1287 
1288     return true;
1289 }
1290 
1291 void Manager::buildProjectTree(const QUrl &url)
1292 {
1293     KileProject * project = projectFor(url);
1294 
1295     if (project) {
1296         buildProjectTree(project);
1297     }
1298 }
1299 
1300 void Manager::buildProjectTree(KileProject *project)
1301 {
1302     if(!project) {
1303         project = activeProject();
1304     }
1305 
1306     if(!project) {
1307         project = selectProject(i18n("Refresh Project Tree"));
1308     }
1309 
1310     if (project) {
1311         //TODO: update structure for all docs
1312         project->buildProjectTree();
1313     }
1314     else if (m_projects.count() == 0) {
1315         KMessageBox::error(m_ki->mainWindow(), i18n("The current document is not associated to a project. Please activate a document that is associated to the project you want to build the tree for, then choose Refresh Project Tree again."),i18n( "Could Not Refresh Project Tree"));
1316     }
1317 }
1318 
1319 void Manager::projectNew()
1320 {
1321     KileNewProjectDialog *dlg = new KileNewProjectDialog(m_ki->templateManager(), m_ki->extensions(), m_ki->mainWindow());
1322 
1323     if (dlg->exec())
1324     {
1325         TextInfo *newTextInfo = Q_NULLPTR;
1326 
1327         KileProject *project = dlg->project();
1328 
1329         //add the project file to the project
1330         KileProjectItem *item = new KileProjectItem(project, project->url());
1331         createTextInfoForProjectItem(item);
1332         item->setOpenState(false);
1333         projectOpenItem(item);
1334 
1335         if(dlg->createNewFile()) {
1336             m_currentlyOpeningFile = true; // don't let live preview interfere
1337 
1338             QString filename = dlg->file();
1339 
1340             //create the new document and fill it with the template
1341             KTextEditor::View *view = loadTemplate(dlg->getSelection());
1342 
1343             if(view) {
1344                 //derive the URL from the base url of the project
1345                 QUrl url = project->baseURL();
1346                 url = url.adjusted(QUrl::StripTrailingSlash);
1347                 url.setPath(url.path() + '/' + filename);
1348 
1349                 newTextInfo = textInfoFor(view->document());
1350 
1351                 //save the new file
1352                 //FIXME: this needs proper error handling
1353                 view->document()->saveAs(url);
1354                 emit(documentModificationStatusChanged(view->document(),
1355                                                        false, KTextEditor::ModificationInterface::OnDiskUnmodified));
1356 
1357                 //add this file to the project
1358                 item = new KileProjectItem(project, url);
1359                 item->setInfo(newTextInfo);
1360 
1361                 //docinfo->updateStruct(m_kwStructure->level());
1362                 emit(updateStructure(false, newTextInfo));
1363             }
1364 
1365             m_currentlyOpeningFile = false;
1366         }
1367 
1368         project->buildProjectTree();
1369         project->save();
1370         addProject(project);
1371 
1372         emit(updateModeStatus());
1373         emit(addToRecentProjects(project->url()));
1374 
1375         if(newTextInfo) {
1376             emit documentOpened(newTextInfo);
1377         }
1378     }
1379 }
1380 
1381 void Manager::addProject(KileProject *project)
1382 {
1383     KILE_DEBUG_MAIN << "==void Manager::addProject(const KileProject *project)==========";
1384     m_projects.append(project);
1385     KILE_DEBUG_MAIN << "\tnow " << m_projects.count() << " projects";
1386     emit addToProjectView(project);
1387     connect(project, SIGNAL(projectTreeChanged(const KileProject*)), this, SIGNAL(projectTreeChanged(const KileProject*)));
1388 }
1389 
1390 KileProject* Manager::selectProject(const QString& caption)
1391 {
1392     QStringList list;
1393     for(QList<KileProject*>::iterator it = m_projects.begin(); it != m_projects.end(); ++it) {
1394         list.append((*it)->name());
1395     }
1396 
1397     KileProject *project = Q_NULLPTR;
1398     QString name;
1399     if (list.count() > 1) {
1400         KileListSelector *dlg  = new KileListSelector(list, caption, i18n("Select Project"), true, m_ki->mainWindow());
1401         if (dlg->exec()) {
1402             if(!dlg->hasSelection()) {
1403                 return Q_NULLPTR;
1404             }
1405             name = dlg->selectedItems().first();
1406         }
1407         delete dlg;
1408     }
1409     else if (list.count() == 0) {
1410         return Q_NULLPTR;
1411     }
1412     else {
1413         name = m_projects.first()->name();
1414     }
1415 
1416     project = projectFor(name);
1417 
1418     return project;
1419 }
1420 
1421 void Manager::addToProject(const QUrl &url)
1422 {
1423     KILE_DEBUG_MAIN << "===Kile::addToProject(const QUrl &url =" << url.url() << ")";
1424 
1425     KileProject *project = selectProject(i18n("Add to Project"));
1426 
1427     if (project) {
1428         addToProject(project, url);
1429     }
1430 }
1431 
1432 void Manager::addToProject(KileProject* project, const QUrl &url)
1433 {
1434     const QUrl realurl = KileUtilities::canonicalUrl(url);
1435     QFileInfo fi(realurl.toLocalFile());
1436 
1437     if (project->contains(realurl)) {
1438         m_ki->errorHandler()->printMessage(KileTool::Info,
1439                                            i18n("The file %1 is already member of the project %2", realurl.fileName(), project->name()),
1440                                            i18n("Add to Project"));
1441         return;
1442     }
1443     else if(!fi.exists() || !fi.isReadable())
1444     {
1445         m_ki->errorHandler()->printMessage(KileTool::Info,
1446                                            i18n("The file %1 can not be added because it does not exist or is not readable", realurl.fileName()),
1447                                            i18n("Add to Project"));
1448         return;
1449     }
1450 
1451     KileProjectItem *item = new KileProjectItem(project, realurl);
1452     createTextInfoForProjectItem(item);
1453     item->setOpenState(m_ki->isOpen(realurl));
1454     projectOpenItem(item);
1455     emit addToProjectView(item);
1456     buildProjectTree(project);
1457 }
1458 
1459 void Manager::removeFromProject(KileProjectItem *item)
1460 {
1461     if (item && item->project()) {
1462         KILE_DEBUG_MAIN << "\tprojecturl = " << item->project()->url().toLocalFile() << ", url = " << item->url().toLocalFile();
1463 
1464         if (item->project()->url() == item->url()) {
1465             KMessageBox::error(m_ki->mainWindow(), i18n("This file is the project file, which holds all the information about your project.  As such, it cannot be removed from the project."), i18n("Cannot Remove File From Project"));
1466             return;
1467         }
1468 
1469         emit removeItemFromProjectView(item, m_ki->isOpen(item->url()));
1470 
1471         KileProject *project = item->project();
1472         project->remove(item);
1473 
1474         // update undefined references in all project files
1475         updateProjectReferences(project);
1476         project->buildProjectTree();
1477     }
1478 }
1479 
1480 // WARNING: 'item' must have been set up with a TextInfo* object already
1481 void Manager::projectOpenItem(KileProjectItem *item, bool openProjectItemViews)
1482 {
1483     KILE_DEBUG_MAIN << "==Kile::projectOpenItem==========================";
1484     KILE_DEBUG_MAIN << "\titem:" << item->url().toLocalFile();
1485 
1486     if (m_ki->isOpen(item->url())) { //remove item from projectview (this file was opened before as a normal file)
1487         emit removeFromProjectView(item->url());
1488     }
1489 
1490     KileDocument::TextInfo* itemInfo = item->getInfo();
1491     Q_ASSERT(itemInfo);
1492 
1493     if(item->isOpen()) {
1494         KTextEditor::View *view = loadItem(m_ki->extensions()->determineDocumentType(item->url()), item, QString(), openProjectItemViews);
1495         if (view) {
1496             item->loadDocumentAndViewSettings();
1497         }
1498         // make sure that the item has been parsed, even if it isn't shown;
1499         // this is necessary to identify the correct LaTeX root document (bug 233667);
1500         m_ki->structureWidget()->update(itemInfo, true);
1501     }
1502     else if(item->type() == KileProjectItem::Source || item->type() == KileProjectItem::Package || item->type() == KileProjectItem::Bibliography) {
1503         // 'item' is not shown (and it is either a LaTeX source file or package), i.e. its
1504         // contents won't be loaded into a KTextEditor::Document; so, we have to do it:
1505         // we are loading the contents of the project item into the docinfo
1506         // for a moment
1507         itemInfo->setDocumentContents(loadTextURLContents(item->url(), item->encoding()));
1508         // in order to pass the contents to the parser
1509         m_ki->structureWidget()->update(itemInfo, true);
1510         // now we don't need the contents anymore
1511         itemInfo->setDocumentContents(QStringList());
1512     }
1513 }
1514 
1515 void Manager::createTextInfoForProjectItem(KileProjectItem *item)
1516 {
1517     item->setInfo(createTextDocumentInfo(m_ki->extensions()->determineDocumentType(item->url()),
1518                                          item->url(), item->project()->baseURL()));
1519 }
1520 
1521 void Manager::projectOpen(const QUrl &url, int step, int max, bool openProjectItemViews)
1522 {
1523     KILE_DEBUG_MAIN << "==Kile::projectOpen==========================";
1524     KILE_DEBUG_MAIN << "\tfilename: " << url.fileName();
1525 
1526     const QUrl realurl = KileUtilities::canonicalUrl(url);
1527 
1528     if(m_ki->projectIsOpen(realurl)) {
1529         if(m_progressDialog) {
1530             m_progressDialog->hide();
1531         }
1532 
1533         KMessageBox::information(m_ki->mainWindow(), i18n("<p>The project \"%1\" is already open.</p>"
1534                                  "<p>If you wanted to reload the project, close the project before you re-open it.</p>", url.fileName()),
1535                                  i18n("Project Already Open"));
1536         return;
1537     }
1538 
1539     QFileInfo fi(realurl.toLocalFile());
1540     if(!fi.isReadable()) {
1541         if(m_progressDialog) {
1542             m_progressDialog->hide();
1543         }
1544 
1545         if (KMessageBox::warningTwoActions(m_ki->mainWindow(),
1546                                            i18n("<p>The project file for the project \"%1\" does not exist or it is not readable.</p>"
1547                                                 "<p>Do you want to remove this project from the recent projects list?</p>",
1548                                                 url.fileName()),
1549                                            i18n("Could Not Open Project"),
1550                                            KStandardGuiItem::remove(), KStandardGuiItem::cancel()) == KMessageBox::PrimaryAction) {
1551             emit(removeFromRecentProjects(realurl));
1552         }
1553         return;
1554     }
1555 
1556     if(!m_progressDialog) {
1557         createProgressDialog();
1558     }
1559 
1560     KileProject *kp = new KileProject(realurl, m_ki->extensions());
1561 
1562     if(!kp->appearsToBeValidProjectFile()) {
1563         if(m_progressDialog) {
1564             m_progressDialog->hide();
1565         }
1566 
1567         KMessageBox::error(m_ki->mainWindow(), i18n("<p>The file \"%1\" cannot be opened as it does not appear to be a project file.</p>",
1568                            url.fileName()),
1569                            i18n("Impossible to Open Project File"));
1570         delete kp;
1571         return;
1572     }
1573 
1574     if(kp->getProjectFileVersion() > KILE_PROJECTFILE_VERSION) {
1575         if(m_progressDialog) {
1576             m_progressDialog->hide();
1577         }
1578 
1579         KMessageBox::error(m_ki->mainWindow(), i18n("<p>The project \"%1\" cannot be opened as it was created <br/>by a newer version of Kile.</p>",
1580                            url.fileName()),
1581                            i18n("Impossible to Open Project"));
1582         delete kp;
1583         return;
1584     }
1585 
1586     if(!kp->isOfCurrentVersion()) {
1587         if(m_progressDialog) {
1588             m_progressDialog->hide();
1589         }
1590 
1591         if(KMessageBox::questionTwoActions(m_ki->mainWindow(), i18n("<p>The project file \"%1\" was created by a previous version of Kile.<br/>"
1592                                            "It needs to be updated before it can be opened.</p>"
1593                                            "<p>Do you want to update it?</p>", url.fileName()),
1594                                            i18n("Project File Needs to be Updated"),
1595                                            KStandardGuiItem::ok(), KStandardGuiItem::cancel())  == KMessageBox::SecondaryAction) {
1596             delete kp;
1597             return;
1598         }
1599 
1600         if(!kp->migrateProjectFileToCurrentVersion()) {
1601             if (KMessageBox::warningTwoActions(m_ki->mainWindow(), i18n("<p>The project file \"%1\" could be not updated.</p>"
1602                                                "<p>Do you want to remove this project from the recent projects list?</p>", url.fileName()),
1603                                                i18n("Could Not Update Project File"),
1604                                                KStandardGuiItem::remove(), KStandardGuiItem::cancel())  == KMessageBox::PrimaryAction) {
1605                 emit(removeFromRecentProjects(realurl));
1606             }
1607             delete kp;
1608             return;
1609         }
1610     }
1611 
1612     m_progressDialog->show();
1613 
1614     kp->load();
1615 
1616     if(kp->isInvalid()) {
1617         if(m_progressDialog) {
1618             m_progressDialog->hide();
1619         }
1620         delete kp;
1621         return;
1622     }
1623 
1624     emit(addToRecentProjects(realurl));
1625 
1626     QList<KileProjectItem*> list = kp->items();
1627     int project_steps = list.count();
1628     m_progressDialog->setMaximum(project_steps * max);
1629     project_steps *= step;
1630     m_progressDialog->setValue(project_steps);
1631 
1632     // open the project files in the correct order
1633     QVector<KileProjectItem*> givenPositionVector(list.count(), Q_NULLPTR);
1634     QList<KileProjectItem*> notCorrectlyOrderedList;
1635     for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
1636         KileProjectItem *item = *it;
1637         int order = item->order();
1638 
1639         if(order >= 0 && order >= list.count()) {
1640             order = -1;
1641         }
1642         if(!item->isOpen() || order < 0 || givenPositionVector[order] != Q_NULLPTR) {
1643             notCorrectlyOrderedList.push_back(item);
1644         }
1645         else {
1646             givenPositionVector[order] = item;
1647         }
1648     }
1649 
1650     QList<KileProjectItem*> orderedList;
1651     for(int i = 0; i < givenPositionVector.size(); ++i) {
1652         KileProjectItem *item = givenPositionVector[i];
1653         if(item) {
1654             orderedList.push_back(item);
1655         }
1656     }
1657     for(QList<KileProjectItem*>::iterator i = notCorrectlyOrderedList.begin(); i != notCorrectlyOrderedList.end(); ++i) {
1658         orderedList.push_back(*i);
1659     }
1660 
1661     addProject(kp);
1662 
1663     // for the parsing to work correctly, all ProjectItems need to have TextInfo* objects, but
1664     // the URL of 'item' might already be associated with a TextInfo* object; for example, through
1665     // a stand-alone document currently being open already, or through a project item that belongs to
1666     // a different project
1667     // => 'createTextDocumentInfo' will take care of that situation as well
1668     for (QList<KileProjectItem*>::iterator i = orderedList.begin(); i != orderedList.end(); ++i) {
1669         createTextInfoForProjectItem(*i);
1670     }
1671 
1672     unsigned int counter = 1;
1673     for (QList<KileProjectItem*>::iterator i = orderedList.begin(); i != orderedList.end(); ++i) {
1674         projectOpenItem(*i, openProjectItemViews);
1675         m_progressDialog->setValue(counter + project_steps);
1676         qApp->processEvents();
1677         ++counter;
1678     }
1679 
1680     kp->buildProjectTree();
1681 
1682     emit(updateStructure(false, Q_NULLPTR));
1683     emit(updateModeStatus());
1684 
1685     // update undefined references in all project files
1686     updateProjectReferences(kp);
1687 
1688     m_ki->viewManager()->switchToTextView(kp->lastDocument());
1689 
1690     emit(projectOpened(kp));
1691 }
1692 
1693 // as all labels are gathered in the project, we can check for unsolved references
1694 void Manager::updateProjectReferences(KileProject *project)
1695 {
1696     QList<KileProjectItem*> list = project->items();
1697     for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
1698         emit(updateReferences((*it)->getInfo()));
1699     }
1700 }
1701 
1702 void Manager::projectOpen()
1703 {
1704     KILE_DEBUG_MAIN << "==Kile::projectOpen==========================";
1705     QUrl url = QFileDialog::getOpenFileUrl(m_ki->mainWindow(), i18n("Open Project"),
1706                                            QUrl::fromLocalFile(KileConfig::defaultProjectLocation()),
1707                                            m_ki->extensions()->fileFilterQtStyle(false, {KileDocument::Extensions::KILE_PROJECT}));
1708 
1709     if(!url.isEmpty()) {
1710         projectOpen(url);
1711     }
1712 }
1713 
1714 
1715 void Manager::projectSave(KileProject *project /* = 0 */)
1716 {
1717     KILE_DEBUG_MAIN << "==Kile::projectSave==========================";
1718     if (!project) {
1719         //find the project that corresponds to the active doc
1720         project= activeProject();
1721     }
1722 
1723     if(!project) {
1724         project = selectProject(i18n("Save Project"));
1725     }
1726 
1727     if(project) {
1728         QList<KileProjectItem*> list = project->items();
1729         KTextEditor::Document *doc = Q_NULLPTR;
1730         KileProjectItem *item = Q_NULLPTR;
1731         TextInfo *docinfo = Q_NULLPTR;
1732 
1733         // determine the order in which the project items are opened
1734         QVector<KileProjectItem*> viewPositionVector(m_ki->viewManager()->getTabCount(), Q_NULLPTR);
1735         for(QList<KileProjectItem*>::iterator i = list.begin(); i != list.end(); ++i) {
1736             docinfo = (*i)->getInfo();
1737             if(docinfo) {
1738                 KTextEditor::View *view = m_ki->viewManager()->textView(docinfo);
1739                 if(view) {
1740                     int position = m_ki->viewManager()->tabIndexOf(view);
1741                     if(position >= 0 && position < viewPositionVector.size()) {
1742                         viewPositionVector[position] = *i;
1743                     }
1744                 }
1745             }
1746         }
1747         int position = 0;
1748         for(int i = 0; i < viewPositionVector.size(); ++i) {
1749             if(viewPositionVector[i] != Q_NULLPTR) {
1750                 viewPositionVector[i]->setOrder(position);
1751                 ++position;
1752             }
1753         }
1754 
1755         //update the open-state of the items
1756         for (QList<KileProjectItem*>::iterator i = list.begin(); i != list.end(); ++i) {
1757             item = *i;
1758             KILE_DEBUG_MAIN << "\tsetOpenState(" << (*i)->url().toLocalFile() << ") to " << m_ki->isOpen(item->url());
1759             item->setOpenState(m_ki->isOpen(item->url()));
1760             docinfo = item->getInfo();
1761 
1762             if(docinfo) {
1763                 doc = docinfo->getDoc();
1764             }
1765             if(doc) {
1766                 storeProjectItem(item, doc);
1767             }
1768 
1769             doc = Q_NULLPTR;
1770             docinfo = Q_NULLPTR;
1771         }
1772 
1773         project->save();
1774     }
1775     else {
1776         KMessageBox::error(m_ki->mainWindow(), i18n("The current document is not associated to a project. Please activate a document that is associated to the project you want to save, then choose Save Project again."),i18n( "Could Determine Active Project"));
1777     }
1778 }
1779 
1780 void Manager::projectAddFiles(const QUrl &url)
1781 {
1782     KileProject *project = projectFor(url);
1783 
1784     if (project) {
1785         projectAddFiles(project,url);
1786     }
1787 }
1788 
1789 void Manager::projectAddFiles(KileProject *project,const QUrl &fileUrl)
1790 {
1791     KILE_DEBUG_MAIN << "==Kile::projectAddFiles()==========================";
1792     if(project == 0) {
1793         project = activeProject();
1794     }
1795 
1796     if(project == 0) {
1797         project = selectProject(i18n("Add Files to Project"));
1798     }
1799 
1800     if (project) {
1801         QString currentDir;
1802         if(fileUrl.isEmpty()) {
1803             currentDir = QFileInfo(project->url().path()).dir().dirName();
1804         }
1805         else {
1806             currentDir = fileUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path();
1807         }
1808 
1809         KILE_DEBUG_MAIN << "currentDir is " << currentDir;
1810         QFileDialog *dlg = new QFileDialog(m_ki->mainWindow(), i18n("Add Files"), currentDir, m_ki->extensions()->fileFilterQtStyle(true, {}));
1811         dlg->setModal(true);
1812         dlg->setFileMode(QFileDialog::ExistingFiles);
1813         dlg->setLabelText(QFileDialog::Accept, i18n("Add"));
1814 
1815         if(dlg->exec()) {
1816             QList<QUrl> urls = dlg->selectedUrls();
1817             for(int i=0; i < urls.count(); ++i) {
1818                 addToProject(project, urls[i]);
1819             }
1820             // update undefined references in all project files
1821             updateProjectReferences(project);
1822         }
1823         delete dlg;
1824 
1825         //open them
1826     }
1827     else if (m_projects.count() == 0) {
1828         KMessageBox::error(m_ki->mainWindow(), i18n("There are no projects opened. Please open the project you want to add files to, then choose Add Files again."),i18n( "Could Not Determine Active Project"));
1829     }
1830 }
1831 
1832 void Manager::toggleArchive(KileProjectItem *item)
1833 {
1834     item->setArchive(!item->archive());
1835 }
1836 
1837 void Manager::projectOptions(const QUrl &url)
1838 {
1839     KileProject *project = projectFor(url);
1840 
1841     if (project) {
1842         projectOptions(project);
1843     }
1844 }
1845 
1846 void Manager::projectOptions(KileProject *project /* = 0*/)
1847 {
1848     KILE_DEBUG_MAIN << "==Kile::projectOptions==========================";
1849     if(!project) {
1850         project = activeProject();
1851     }
1852 
1853     if(!project) {
1854         project = selectProject(i18n("Project Options For"));
1855     }
1856 
1857     if (project) {
1858         KILE_DEBUG_MAIN << "\t" << project->name();
1859         KileProjectOptionsDialog *dlg = new KileProjectOptionsDialog(project, m_ki->extensions(), m_ki->mainWindow());
1860         dlg->exec();
1861     }
1862     else if (m_projects.count() == 0) {
1863         KMessageBox::error(m_ki->mainWindow(), i18n("The current document is not associated to a project. Please activate a document that is associated to the project you want to modify, then choose Project Options again."),i18n( "Could Not Determine Active Project"));
1864     }
1865 }
1866 
1867 bool Manager::projectCloseAll()
1868 {
1869     KILE_DEBUG_MAIN << "==Kile::projectCloseAll==========================";
1870 
1871     while(m_projects.size() > 0) {
1872         if(!projectClose(m_projects.first()->url())) {
1873             return false;
1874         }
1875     }
1876 
1877     return true;
1878 }
1879 
1880 bool Manager::projectClose(const QUrl &url)
1881 {
1882     KILE_DEBUG_MAIN << "==Kile::projectClose==========================";
1883     KileProject *project = 0;
1884 
1885     if (url.isEmpty()) {
1886         project = activeProject();
1887 
1888         if (!project) {
1889             project = selectProject(i18n("Close Project"));
1890         }
1891     }
1892     else {
1893         project = projectFor(url);
1894     }
1895 
1896     if(project) {
1897         KILE_DEBUG_MAIN << "\tclosing:" << project->name();
1898         project->setLastDocument(QUrl::fromLocalFile(m_ki->getName()));
1899 
1900         projectSave(project);
1901 
1902         QList<KileProjectItem*> list = project->items();
1903 
1904         bool close = true;
1905         KTextEditor::Document *doc = Q_NULLPTR;
1906         TextInfo *docinfo = Q_NULLPTR;
1907         for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
1908             KileProjectItem *item = *it;
1909 
1910             doc = Q_NULLPTR;
1911             docinfo = item->getInfo();
1912             if (docinfo) {
1913                 doc = docinfo->getDoc();
1914             }
1915             else {
1916                 continue;
1917             }
1918             if (doc) {
1919                 KILE_DEBUG_MAIN << "\t\tclosing item " << doc->url().toLocalFile();
1920                 bool r = fileClose(doc, true);
1921                 close = close && r;
1922                 if (!close) {
1923                     break;
1924                 }
1925             }
1926             else {
1927                 // we still need to delete the TextInfo object
1928                 removeTextDocumentInfo(docinfo, true);
1929             }
1930         }
1931 
1932         if (close) {
1933             m_projects.removeAll(project);
1934             emit removeFromProjectView(project);
1935             delete project;
1936             emit(updateModeStatus());
1937             return true;
1938         }
1939         else
1940             return false;
1941     }
1942     else if (m_projects.count() == 0)
1943         KMessageBox::error(m_ki->mainWindow(), i18n("The current document is not associated to a project. Please activate a document that is associated to the project you want to close, then choose Close Project again."),i18n( "Could Not Close Project"));
1944 
1945     return true;
1946 }
1947 
1948 void Manager::storeProjectItem(KileProjectItem *item, KTextEditor::Document *doc)
1949 {
1950     KILE_DEBUG_MAIN << "===Kile::storeProjectItem==============";
1951     KILE_DEBUG_MAIN << "\titem = " << item << ", doc = " << doc;
1952     item->setEncoding(doc->encoding());
1953     item->setMode(doc->mode());
1954     item->setHighlight(doc->highlightingMode());
1955     item->saveDocumentAndViewSettings();
1956 }
1957 
1958 void Manager::cleanUpTempFiles(const QUrl &url, bool silent)
1959 {
1960     KILE_DEBUG_MAIN << "===void Manager::cleanUpTempFiles(const QUrl " << url.toLocalFile() << ", bool " << silent << ")===";
1961 
1962     if( url.isEmpty() )
1963         return;
1964 
1965     QStringList extlist;
1966     QFileInfo fi(url.toLocalFile());
1967     const QStringList templist = KileConfig::cleanUpFileExtensions().split(' ');
1968     const QString fileName = fi.fileName();
1969     const QString dirPath = fi.absolutePath();
1970     const QString baseName = fi.completeBaseName();
1971 
1972     for (int i = 0; i < templist.count(); ++i) {
1973         fi.setFile( dirPath + '/' + baseName + templist[i] );
1974         if(fi.exists()) {
1975             extlist.append(templist[i]);
1976         }
1977     }
1978 
1979     if(!silent && fileName.isEmpty()) {
1980         return;
1981     }
1982 
1983     if (!silent && extlist.count() > 0) {
1984         KILE_DEBUG_MAIN << "not silent";
1985         KileDialog::Clean *dialog = new KileDialog::Clean(m_ki->mainWindow(), fileName, extlist);
1986         if (dialog->exec() == QDialog::Accepted) {
1987             extlist = dialog->cleanList();
1988         }
1989         else {
1990             delete dialog;
1991             return;
1992         }
1993 
1994         delete dialog;
1995     }
1996 
1997     if(extlist.count() == 0) {
1998         m_ki->errorHandler()->printMessage(KileTool::Warning, i18n("Nothing to clean for %1", fileName),
1999                                            i18n("Clean"));
2000     }
2001     else {
2002         for(int i = 0; i < extlist.count(); ++i) {
2003             QFile file(dirPath + '/' + baseName + extlist[i]);
2004             KILE_DEBUG_MAIN << "About to remove file = " << file.fileName();
2005             file.remove();
2006         }
2007         m_ki->errorHandler()->printMessage(KileTool::Info,
2008                                            i18n("Cleaning %1: %2", fileName, extlist.join(" ")),
2009                                            i18n("Clean"));
2010     }
2011 }
2012 
2013 void Manager::openDroppedURLs(QDropEvent *e) {
2014     QList<QUrl> urls = e->mimeData()->urls();
2015     Extensions *extensions = m_ki->extensions();
2016 
2017     for(QList<QUrl>::iterator i = urls.begin(); i != urls.end(); ++i) {
2018         QUrl url = *i;
2019         if(extensions->isProjectFile(url)) {
2020             projectOpen(url);
2021         }
2022         else {
2023             fileOpen(url);
2024         }
2025     }
2026 }
2027 
2028 void Manager::reloadXMLOnAllDocumentsAndViews()
2029 {
2030     for(QList<TextInfo*>::iterator it = m_textInfoList.begin(); it != m_textInfoList.end(); ++it) {
2031         KTextEditor::Document *doc = (*it)->getDoc();
2032         // FIXME: 'doc' can be null, for example if it belongs to a project item
2033         //        which has been closed, but this should be improved in the sense
2034         //        that 'm_textInfoList' should only contain 'TextInfo' objects which
2035         //        contain valid pointers to 'KTextEditor::Document' objects.
2036         if(!doc) {
2037             continue;
2038         }
2039         doc->reloadXML();
2040         QList<KTextEditor::View*> views = doc->views();
2041         for(QList<KTextEditor::View*>::iterator viewIt = views.begin(); viewIt != views.end(); ++viewIt) {
2042             (*viewIt)->reloadXML();
2043         }
2044     }
2045 }
2046 
2047 
2048 void Manager::handleParsingComplete(const QUrl &url, KileParser::ParserOutput* output)
2049 {
2050     KILE_DEBUG_MAIN << url << output;
2051     if(!output) {
2052         KILE_DEBUG_MAIN << "NULL output given";
2053         return;
2054     }
2055     KileDocument::TextInfo *textInfo = textInfoFor(url);
2056     if(!textInfo) {
2057         KileProjectItem* item = itemFor(url);
2058         if(item) {
2059             textInfo = item->getInfo();
2060         }
2061         if(!textInfo) {
2062             // this can happen for instance when the document is closed
2063             // while the parser is still running
2064             KILE_DEBUG_MAIN << "no TextInfo object found for" << url << "found";
2065             return;
2066         }
2067     }
2068     textInfo->installParserOutput(output);
2069     m_ki->structureWidget()->updateAfterParsing(textInfo, output->structureViewItems);
2070     delete(output);
2071 }
2072 
2073 // Show all opened projects and switch to another one, if you want
2074 
2075 void Manager::projectShow()
2076 {
2077     if(m_projects.count() <= 1) {
2078         return;
2079     }
2080 
2081     // select the new project
2082     KileProject *project = selectProject(i18n("Switch Project"));
2083     if(!project || project==activeProject()) {
2084         return;
2085     }
2086 
2087     // get last opened document
2088     const QUrl lastdoc = project->lastDocument();
2089     KileProjectItem *docitem = (!lastdoc.isEmpty()) ? itemFor(lastdoc, project) : Q_NULLPTR;
2090 
2091     // if not, we search for the first opened tex file of this project
2092     // if no file is opened, we take the first tex file mentioned in the list
2093     KileProjectItem *first_texitem = Q_NULLPTR;
2094     if(!docitem) {
2095         QList<KileProjectItem*> list = project->items();
2096         for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
2097             KileProjectItem *item = *it;
2098 
2099             QString itempath = item->path();
2100 
2101             // called from QAction 'Show projects...': find the first opened
2102             // LaTeX document or, if that fails, any other opened file
2103             QStringList extlist = (m_ki->extensions()->latexDocuments() + ' ' + m_ki->extensions()->latexPackages()).split(' ');
2104             for(QStringList::Iterator extIt = extlist.begin(); extIt != extlist.end(); ++extIt) {
2105                 if(itempath.indexOf( (*extIt), -(*extIt).length() ) >= 0)  {
2106                     if (m_ki->isOpen(item->url()))  {
2107                         docitem = item;
2108                         break;
2109                     }
2110                     else if(!first_texitem) {
2111                         first_texitem = item;
2112                     }
2113                 }
2114             }
2115             if(docitem) {
2116                 break;
2117             }
2118         }
2119     }
2120 
2121     // did we find one opened file or must we take the first entry
2122     if(!docitem) {
2123         if(!first_texitem) {
2124             return;
2125         }
2126         docitem = first_texitem;
2127     }
2128 
2129     // ok, we can switch to another project now
2130     if (m_ki->isOpen(docitem->url())) {
2131         m_ki->viewManager()->switchToTextView(docitem->url());
2132     }
2133     else {
2134         fileOpen(docitem->url(), docitem->encoding());
2135     }
2136 }
2137 
2138 void Manager::projectRemoveFiles()
2139 {
2140     QList<KileProjectItem*> itemsList = selectProjectFileItems(i18n("Select Files to Remove"));
2141     if(itemsList.count() > 0) {
2142         for(QList<KileProjectItem*>::iterator it = itemsList.begin(); it != itemsList.end(); ++it) {
2143             removeFromProject(*it);
2144         }
2145     }
2146 }
2147 
2148 void Manager::projectShowFiles()
2149 {
2150     KileProjectItem *item = selectProjectFileItem( i18n("Select File") );
2151     if(item) {
2152         if (item->type() == KileProjectItem::ProjectFile) {
2153             dontOpenWarning(item, i18n("Show Project Files"), i18n("project configuration file"));
2154         }
2155         else if(item->type() == KileProjectItem::Image) {
2156             dontOpenWarning(item, i18n("Show Project Files"), i18n("graphics file"));
2157         }
2158         else { // ok, we can switch to another file
2159             if  (m_ki->isOpen(item->url())) {
2160                 m_ki->viewManager()->switchToTextView(item->url());
2161             }
2162             else {
2163                 fileOpen(item->url(), item->encoding() );
2164             }
2165         }
2166     }
2167 }
2168 
2169 void Manager::projectOpenAllFiles()
2170 {
2171     KileProject *project = selectProject(i18n("Select Project"));
2172     if(project) {
2173         projectOpenAllFiles(project->url());
2174     }
2175 }
2176 
2177 void Manager::projectOpenAllFiles(const QUrl &url)
2178 {
2179     KileProject* project;
2180     KTextEditor::Document* doc = Q_NULLPTR;
2181 
2182     if(!url.isValid()) {
2183         return;
2184     }
2185     project = projectFor(url);
2186 
2187     if(!project)
2188         return;
2189 
2190 
2191     if(m_ki->viewManager()->currentTextView()) {
2192         doc = m_ki->viewManager()->currentTextView()->document();
2193     }
2194     // we remember the actual view, so the user gets the same view back after opening
2195 
2196     QList<KileProjectItem*> list = project->items();
2197     for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
2198         KileProjectItem *item = *it;
2199 
2200         if (item->type()==KileProjectItem::ProjectFile) {
2201             dontOpenWarning( item, i18n("Open All Project Files"), i18n("project configuration file") );
2202         }
2203         else if(item->type()==KileProjectItem::Image) {
2204             dontOpenWarning( item, i18n("Open All Project Files"), i18n("graphics file") );
2205         }
2206         else if(!m_ki->isOpen(item->url())) {
2207             fileOpen(item->url(), item->encoding());
2208         }
2209     }
2210 
2211     if(doc) { // we have a doc so switch back to original view
2212         m_ki->viewManager()->switchToTextView(doc->url());
2213     }
2214 }
2215 
2216 QStringList Manager::getProjectFiles()
2217 {
2218     QStringList filelist;
2219 
2220     KileProject *project = activeProject();
2221     if ( project )
2222     {
2223         QList<KileProjectItem*> list = project->items();
2224         for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
2225             KileProjectItem *item = *it;
2226 
2227             if(item->type() != KileProjectItem::ProjectFile && item->type() != KileProjectItem::Image) {
2228                 filelist << item->url().toLocalFile();
2229             }
2230         }
2231     }
2232     return filelist;
2233 }
2234 
2235 void Manager::dontOpenWarning(KileProjectItem *item, const QString &action, const QString &filetype)
2236 {
2237     m_ki->errorHandler()->printMessage(KileTool::Info,
2238                                        i18n("not opened: %1 (%2)", item->url().toLocalFile(), filetype),
2239                                        action);
2240 }
2241 
2242 KileProjectItem* Manager::selectProjectFileItem(const QString &label)
2243 {
2244     // select a project
2245     KileProject *project = selectProject(i18n("Select Project"));
2246     if(!project) {
2247         return Q_NULLPTR;
2248     }
2249 
2250     // get a list of files
2251     QStringList filelist;
2252     QMap<QString, KileProjectItem*> map;
2253     QList<KileProjectItem*> list = project->items();
2254     for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
2255         KileProjectItem *item = *it;
2256 
2257         filelist << item->path();
2258         map[item->path()] = item;
2259     }
2260 
2261     // select one of these files
2262     KileProjectItem *item = Q_NULLPTR;
2263     KileListSelector *dlg  = new KileListSelector(filelist, i18n("Project Files"), label, true, m_ki->mainWindow());
2264     if(dlg->exec()) {
2265         if(dlg->hasSelection()) {
2266             QString name = dlg->selectedItems().first();
2267             if(map.contains(name)) {
2268                 item = map[name];
2269             }
2270             else {
2271                 KMessageBox::error(m_ki->mainWindow(), i18n("Could not determine the selected file."),i18n( "Project Error"));
2272             }
2273         }
2274     }
2275     delete dlg;
2276 
2277     return item;
2278 }
2279 
2280 QList<KileProjectItem*> Manager::selectProjectFileItems(const QString &label)
2281 {
2282     KileProject *project = selectProject(i18n("Select Project"));
2283     if(!project) {
2284         return QList<KileProjectItem*>();
2285     }
2286 
2287     QStringList filelist;
2288     QMap<QString,KileProjectItem *> map;
2289 
2290     QList<KileProjectItem*> list = project->items();
2291     for(QList<KileProjectItem*>::iterator it = list.begin(); it != list.end(); ++it) {
2292         KileProjectItem *item = *it;
2293 
2294         filelist << item->path();
2295         map[item->path()] = item;
2296     }
2297 
2298     QList<KileProjectItem*> itemsList;
2299 
2300     KileListSelector *dlg  = new KileListSelector(filelist, i18n("Project Files"), label, true, m_ki->mainWindow());
2301     dlg->setSelectionMode(QAbstractItemView::ExtendedSelection);
2302     if(dlg->exec()) {
2303         if(dlg->hasSelection()) {
2304             QStringList selectedfiles = dlg->selectedItems();
2305             for(QStringList::Iterator it = selectedfiles.begin(); it != selectedfiles.end(); ++it ) {
2306                 if(map.contains(*it)) {
2307                     itemsList.append(map[(*it)]);
2308                 }
2309                 else {
2310                     KMessageBox::error(m_ki->mainWindow(), i18n("Could not determine the selected file."), i18n( "Project Error"));
2311                 }
2312             }
2313         }
2314     }
2315     delete dlg;
2316 
2317     return itemsList;
2318 }
2319 
2320 // add a new file to the project
2321 //  - only when there is an active project
2322 //  - if the file doesn't already belong to it (checked by addToProject)
2323 
2324 void Manager::projectAddFile(QString filename, bool graphics)
2325 {
2326     KILE_DEBUG_MAIN << "===Kile::projectAddFile==============";
2327     KileProject *project = activeProject();
2328     if(!project) {
2329         return;
2330     }
2331 
2332     QFileInfo fi(filename);
2333     if(!fi.exists()) {
2334         if(graphics) {
2335             return;
2336         }
2337 
2338         // called from InputDialog after a \input- or \include command:
2339         //  - if the chosen file has an extension: accept
2340         //  - if not we add the default TeX extension: accept if it exists else reject
2341         QString ext = fi.completeSuffix();
2342         if ( ! ext.isEmpty() ) {
2343             return;
2344         }
2345 
2346         filename += m_ki->extensions()->latexDocumentDefault();
2347         if (QFileInfo::exists(filename)) {
2348             return;
2349         }
2350     }
2351 
2352     //ok, we have a project and an existing file
2353     KILE_DEBUG_MAIN << "\tadd file: " << filename;
2354     m_ki->viewManager()->updateStructure(false);
2355 
2356     QUrl url;
2357     url.setPath(filename);
2358     addToProject(project, url);
2359 }
2360 
2361 void Manager::cleanupDocumentInfoForProjectItems(KileDocument::Info *info)
2362 {
2363     QList<KileProjectItem*> itemsList = itemsFor(info);
2364     for(QList<KileProjectItem*>::iterator it = itemsList.begin(); it != itemsList.end(); ++it) {
2365         (*it)->setInfo(Q_NULLPTR);
2366     }
2367 }
2368 
2369 void Manager::createProgressDialog()
2370 {
2371     //TODO this is a dangerous dialog and should be removed in the long-term:
2372     // the dialog disables all close events unless all files are loaded,
2373     // thus if there is a loading error, the only way to abort loading gracefully is to
2374     // terminate the application
2375     m_progressDialog = new KileWidget::ProgressDialog(m_ki->mainWindow());
2376     QLabel *label = new QLabel(m_progressDialog);
2377     label->setText(i18n("Opening Project..."));
2378     m_progressDialog->setLabel(label);
2379     m_progressDialog->setModal(true);
2380     m_progressDialog->setLabelText(i18n("Scanning project files..."));
2381     m_progressDialog->setAutoClose(true);
2382     m_progressDialog->setMinimumDuration(2000);
2383     m_progressDialog->hide();
2384 }
2385 
2386 void Manager::loadDocumentAndViewSettings(KileDocument::TextInfo *textInfo)
2387 {
2388     KTextEditor::Document *document = textInfo->getDoc();
2389     if(!document) {
2390         return;
2391     }
2392 
2393     KConfigGroup configGroup = configGroupForDocumentSettings(document);
2394     if(!configGroup.exists()) {
2395         return;
2396     }
2397 
2398     document->readSessionConfig(configGroup, QSet<QString>() << "SkipEncoding" << "SkipUrl");
2399     {
2400         LaTeXInfo *latexInfo = dynamic_cast<LaTeXInfo*>(textInfo);
2401         if(latexInfo) {
2402             KileTool::LivePreviewManager::readLivePreviewStatusSettings(configGroup, latexInfo);
2403         }
2404     }
2405 
2406     {
2407         LaTeXOutputHandler *h = dynamic_cast<LaTeXOutputHandler*>(textInfo);
2408         if(h) {
2409             h->readBibliographyBackendSettings(configGroup);
2410         }
2411     }
2412 
2413     QList<KTextEditor::View*> viewList = document->views();
2414     int i = 0;
2415     for(QList<KTextEditor::View*>::iterator it = viewList.begin(); it != viewList.end(); ++it) {
2416         KTextEditor::View *view = *it;
2417         configGroup = configGroupForViewSettings(document, i);
2418         view->readSessionConfig(configGroup);
2419         ++i;
2420     }
2421 
2422 }
2423 
2424 void Manager::saveDocumentAndViewSettings(KileDocument::TextInfo *textInfo)
2425 {
2426     KTextEditor::Document *document = textInfo->getDoc();
2427     if(!document) {
2428         return;
2429     }
2430 
2431     KConfigGroup configGroup = configGroupForDocumentSettings(document);
2432 
2433     QUrl url = document->url();
2434     url.setPassword(""); // we don't want the password to appear in the configuration file
2435     deleteDocumentAndViewSettingsGroups(url);
2436 
2437     document->writeSessionConfig(configGroup, QSet<QString>() << "SkipEncoding" << "SkipUrl");
2438     {
2439         LaTeXInfo *latexInfo = dynamic_cast<LaTeXInfo*>(textInfo);
2440         if(latexInfo) {
2441             KileTool::LivePreviewManager::writeLivePreviewStatusSettings(configGroup, latexInfo);
2442         }
2443     }
2444 
2445     {
2446         LaTeXOutputHandler *h = dynamic_cast<LaTeXOutputHandler*>(textInfo);
2447         if(h) {
2448             h->writeBibliographyBackendSettings(configGroup);
2449         }
2450     }
2451 
2452     QList<KTextEditor::View*> viewList = document->views();
2453     int i = 0;
2454     for(QList<KTextEditor::View*>::iterator it = viewList.begin(); it != viewList.end(); ++it) {
2455         configGroup = configGroupForViewSettings(document, i);
2456         (*it)->writeSessionConfig(configGroup);
2457         ++i;
2458     }
2459     // finally remove the config groups for the oldest documents that exceed MAX_NUMBER_OF_STORED_SETTINGS
2460     configGroup = KSharedConfig::openConfig()->group("Session Settings");
2461     QList<QUrl> urlList = QUrl::fromStringList(configGroup.readEntry("Saved Documents", QStringList()));
2462     urlList.removeAll(url);
2463     urlList.push_front(url);
2464     // remove excess elements
2465     if(urlList.length() > MAX_NUMBER_OF_STORED_SETTINGS) {
2466         int excessNumber = urlList.length() - MAX_NUMBER_OF_STORED_SETTINGS;
2467         for(; excessNumber > 0; --excessNumber) {
2468             QUrl removeUrl = urlList.takeLast();
2469             deleteDocumentAndViewSettingsGroups(removeUrl);
2470         }
2471     }
2472     configGroup.writeEntry("Documents", url);
2473     configGroup.writeEntry("Saved Documents", QUrl::toStringList(urlList));
2474 }
2475 
2476 KConfigGroup Manager::configGroupForDocumentSettings(KTextEditor::Document *doc) const
2477 {
2478     return KSharedConfig::openConfig()->group(configGroupNameForDocumentSettings(doc->url()));
2479 }
2480 
2481 QString Manager::configGroupNameForDocumentSettings(const QUrl &url) const
2482 {
2483     QUrl url2 = url;
2484     url2.setPassword("");
2485     return "Document-Settings,URL=" + url2.url();
2486 }
2487 
2488 KConfigGroup Manager::configGroupForViewSettings(KTextEditor::Document *doc, int viewIndex) const
2489 {
2490     return KSharedConfig::openConfig()->group(configGroupNameForViewSettings(doc->url(), viewIndex));
2491 }
2492 
2493 QString Manager::configGroupNameForViewSettings(const QUrl &url, int viewIndex) const
2494 {
2495     QUrl url2 = url;
2496     url2.setPassword("");
2497     return "View-Settings,View=" + QString::number(viewIndex) + ",URL=" + url2.url();
2498 }
2499 
2500 void Manager::deleteDocumentAndViewSettingsGroups(const QUrl &url)
2501 {
2502     QString urlString = url.url();
2503     const QStringList groupList = KSharedConfig::openConfig()->groupList();
2504     for (const auto& groupName : groupList) {
2505         if(!KSharedConfig::openConfig()->hasGroup(groupName)) { // 'groupName' might have been deleted
2506             continue;                                       // work around bug 384039
2507         }
2508         if(groupName.startsWith(QLatin1String("Document-Settings"))
2509                 || groupName.startsWith(QLatin1String("View-Settings"))) {
2510             int urlIndex = groupName.indexOf("URL=");
2511             if(urlIndex >= 0 && groupName.mid(urlIndex + 4) == urlString) {
2512                 KSharedConfig::openConfig()->deleteGroup(groupName);
2513             }
2514         }
2515     }
2516 }
2517 
2518 QStringList Manager::loadTextURLContents(const QUrl &url, const QString& encoding)
2519 {
2520     QTemporaryFile *temporaryFile = Q_NULLPTR;
2521     QString localFileName;
2522     if(url.isLocalFile()) {
2523         localFileName = url.path();
2524     }
2525     else { // only use KIO when we have to
2526         temporaryFile = new QTemporaryFile();
2527         if(!temporaryFile->open()) {
2528             KILE_DEBUG_MAIN << "Cannot create temporary file for" << url;
2529             delete temporaryFile;
2530             return QStringList();
2531         }
2532         localFileName = temporaryFile->fileName();
2533         auto downloadJob = KIO::file_copy(url, QUrl::fromLocalFile(localFileName), 0600, KIO::Overwrite);
2534         KJobWidgets::setWindow(downloadJob, m_ki->mainWindow());
2535         // FIXME: 'exec' should not be used!
2536         if (!downloadJob->exec()) {
2537             KILE_DEBUG_MAIN << "Cannot download resource: " << url;
2538             KILE_DEBUG_MAIN << downloadJob->errorString();
2539             delete temporaryFile;
2540             return QStringList();
2541         }
2542     }
2543 
2544     QFile localFile(localFileName);
2545 
2546     if (!localFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
2547         KILE_DEBUG_MAIN << "Cannot open source file: " << localFileName;
2548         delete temporaryFile;
2549         return QStringList();
2550     }
2551 
2552     QStringList res;
2553     QTextStream stream(&localFile);
2554     if(!encoding.isEmpty()) {
2555         stream.setCodec(encoding.toLatin1());
2556     }
2557     while(!stream.atEnd()) {
2558         res.append(stream.readLine());
2559     }
2560     delete temporaryFile;
2561     return res;
2562 }
2563 
2564 }