File indexing completed on 2024-04-21 05:43:55

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "docmanager.h"
0012 #include "docmanageriface.h"
0013 #include "electronics/circuitdocument.h"
0014 #include "flowcodedocument.h"
0015 #include "iteminterface.h"
0016 #include "itemselector.h"
0017 #include "ktechlab.h"
0018 #include "mechanicsdocument.h"
0019 #include "textdocument.h"
0020 #include "textview.h"
0021 #include "viewcontainer.h"
0022 
0023 #include <KLocalizedString>
0024 #include <KMessageBox>
0025 #include <KXMLGUIFactory>
0026 
0027 #include <QAction>
0028 #include <QFile>
0029 #include <QTabWidget>
0030 
0031 #include <cassert>
0032 
0033 #include <ktlconfig.h>
0034 
0035 DocManager *DocManager::m_pSelf = nullptr;
0036 
0037 DocManager *DocManager::self()
0038 {
0039     if (!m_pSelf)
0040         m_pSelf = new DocManager();
0041 
0042     return m_pSelf;
0043 }
0044 
0045 DocManager::DocManager()
0046     : QObject(KTechlab::self())
0047 {
0048     p_focusedView = nullptr;
0049     m_countCircuit = 0;
0050     m_countFlowCode = 0;
0051     m_countMechanics = 0;
0052     m_countOther = 0;
0053     p_connectedDocument = nullptr;
0054     m_nextDocumentID = 1;
0055     m_pIface = new DocManagerIface(this);
0056 }
0057 
0058 DocManager::~DocManager()
0059 {
0060     delete m_pIface;
0061 }
0062 
0063 bool DocManager::closeAll()
0064 {
0065     while (!m_documentList.isEmpty()) {
0066         Document *document = m_documentList.first();
0067         if (document->fileClose()) {
0068             m_documentList.removeAll(document);
0069             removeDocumentAssociations(document);
0070         } else
0071             return false;
0072     }
0073     return true;
0074 }
0075 
0076 void DocManager::gotoTextLine(const QUrl &url, int line)
0077 {
0078     TextDocument *doc = dynamic_cast<TextDocument *>(openURL(url));
0079     TextView *tv = doc ? doc->textView() : nullptr;
0080 
0081     if (!tv)
0082         return;
0083 
0084     tv->gotoLine(line);
0085     tv->setFocus();
0086 }
0087 
0088 Document *DocManager::openURL(const QUrl &url, ViewArea *viewArea)
0089 {
0090     if (url.isEmpty())
0091         return nullptr;
0092 
0093     if (url.isLocalFile()) {
0094         QFile file(url.toLocalFile());
0095         if (file.open(QIODevice::ReadOnly) == false) {
0096             KMessageBox::error(nullptr, i18n("Could not open '%1'", file.fileName()));
0097             return nullptr;
0098         }
0099         file.close();
0100     }
0101 
0102     // If the currently active view area is empty, and we were not given a view area
0103     // to open into, then use the empty view area
0104     if (!viewArea) {
0105         ViewContainer *currentVC = static_cast<ViewContainer *>(KTechlab::self()->tabWidget()->currentWidget());
0106         if (currentVC) {
0107             ViewArea *va = currentVC->viewArea(currentVC->activeViewArea());
0108             if (!va->view())
0109                 viewArea = va;
0110         }
0111     }
0112 
0113     // If the document is already open, and a specific view area hasn't been
0114     // specified, then just return that document - otherwise, create a new
0115     // view in the viewarea
0116     Document *document = findDocument(url);
0117     if (document) {
0118         if (viewArea)
0119             createNewView(document, viewArea);
0120         else
0121             giveDocumentFocus(document, viewArea);
0122         return document;
0123     }
0124 
0125     QString fileName = url.fileName();
0126     QString extension = fileName.right(fileName.length() - fileName.lastIndexOf('.'));
0127 
0128     if (extension == ".circuit")
0129         return openCircuitFile(url, viewArea);
0130     else if (extension == ".flowcode")
0131         return openFlowCodeFile(url, viewArea);
0132     else if (extension == ".mechanics")
0133         return openMechanicsFile(url, viewArea);
0134     else
0135         return openTextFile(url, viewArea);
0136 }
0137 
0138 Document *DocManager::getFocusedDocument() const
0139 {
0140     Document *doc = p_focusedView ? p_focusedView->document() : nullptr;
0141     return (doc && !doc->isDeleted()) ? doc : nullptr;
0142 }
0143 
0144 void DocManager::giveDocumentFocus(Document *toFocus, ViewArea *viewAreaForNew)
0145 {
0146     if (!toFocus)
0147         return;
0148 
0149     if (View *activeView = toFocus->activeView()) {
0150         // KTechlab::self()->tabWidget()->showPage( activeView->viewContainer() ); // 2018.12.01
0151         KTechlab::self()->tabWidget()->setCurrentIndex(KTechlab::self()->tabWidget()->indexOf(activeView->viewContainer()));
0152     }
0153 
0154     else if (viewAreaForNew)
0155         createNewView(toFocus, viewAreaForNew);
0156 }
0157 
0158 QString DocManager::untitledName(int type)
0159 {
0160     QString name;
0161     switch (type) {
0162     case Document::dt_circuit: {
0163         if (m_countCircuit > 1)
0164             name = i18n("Untitled (Circuit %1)", QString::number(m_countCircuit));
0165         else
0166             name = i18n("Untitled (Circuit)");
0167         m_countCircuit++;
0168         break;
0169     }
0170     case Document::dt_flowcode: {
0171         if (m_countFlowCode > 1)
0172             name = i18n("Untitled (FlowCode %1)", QString::number(m_countFlowCode));
0173         else
0174             name = i18n("Untitled (FlowCode)");
0175         m_countFlowCode++;
0176         break;
0177     }
0178     case Document::dt_mechanics: {
0179         if (m_countMechanics > 1)
0180             name = i18n("Untitled (Mechanics %1)", QString::number(m_countMechanics));
0181         else
0182             name = i18n("Untitled (Mechanics)");
0183         m_countMechanics++;
0184         break;
0185     }
0186     default: {
0187         if (m_countOther > 1)
0188             name = i18n("Untitled (%1)", QString::number(m_countOther));
0189         else
0190             name = i18n("Untitled");
0191         m_countOther++;
0192         break;
0193     }
0194     }
0195     return name;
0196 }
0197 
0198 Document *DocManager::findDocument(const QUrl &url) const
0199 {
0200     // First, look in the associated documents
0201     if (m_associatedDocuments.contains(url))
0202         return m_associatedDocuments[url];
0203 
0204     // Not found, so look in the known documents
0205     const DocumentList::const_iterator end = m_documentList.end();
0206     for (DocumentList::const_iterator it = m_documentList.begin(); it != end; ++it) {
0207         if ((*it)->url() == url)
0208             return *it;
0209     }
0210 
0211     return nullptr;
0212 }
0213 
0214 void DocManager::associateDocument(const QUrl &url, Document *document)
0215 {
0216     if (!document)
0217         return;
0218 
0219     m_associatedDocuments[url] = document;
0220 }
0221 
0222 void DocManager::removeDocumentAssociations(Document *document)
0223 {
0224     bool doneErase;
0225     do {
0226         doneErase = false;
0227         const URLDocumentMap::iterator end = m_associatedDocuments.end();
0228         for (URLDocumentMap::iterator it = m_associatedDocuments.begin(); it != end; ++it) {
0229             if (it.value() == document) {
0230                 doneErase = true;
0231                 m_associatedDocuments.erase(it);
0232                 break;
0233             }
0234         }
0235     } while (doneErase);
0236 }
0237 
0238 void DocManager::handleNewDocument(Document *document, ViewArea *viewArea)
0239 {
0240     if (!document || m_documentList.contains(document))
0241         return;
0242 
0243     m_documentList.append(document);
0244     document->setDCOPID(m_nextDocumentID++);
0245 
0246     connect(document, &Document::modifiedStateChanged, KTechlab::self(), &KTechlab::slotDocModifiedChanged);
0247     connect(document, &Document::fileNameChanged, KTechlab::self(), &KTechlab::slotDocModifiedChanged);
0248     connect(document, &Document::fileNameChanged, KTechlab::self(), &KTechlab::addRecentFile);
0249     connect(document, &Document::destroyed, this, &DocManager::documentDestroyed);
0250     connect(document, &Document::viewFocused, this, &DocManager::slotViewFocused);
0251     connect(document, &Document::viewUnfocused, this, &DocManager::slotViewUnfocused);
0252 
0253     createNewView(document, viewArea);
0254 }
0255 
0256 View *DocManager::createNewView(Document *document, ViewArea *viewArea)
0257 {
0258     if (!document)
0259         return nullptr;
0260 
0261     View *view = nullptr;
0262 
0263     if (viewArea)
0264         view = document->createView(viewArea->viewContainer(), viewArea->id());
0265 
0266     else {
0267         ViewContainer *viewContainer = new ViewContainer(document->caption());
0268         view = document->createView(viewContainer, 0);
0269         KTechlab::self()->addWindow(viewContainer);
0270     }
0271 
0272     return view;
0273 }
0274 
0275 void DocManager::documentDestroyed(QObject *obj)
0276 {
0277     Document *doc = static_cast<Document *>(obj);
0278     m_documentList.removeAll(doc);
0279     removeDocumentAssociations(doc);
0280     disableContextActions();
0281 }
0282 
0283 void DocManager::slotViewFocused(View *view)
0284 {
0285     ViewContainer *vc = static_cast<ViewContainer *>(KTechlab::self()->tabWidget()->currentWidget());
0286     if (!vc)
0287         view = nullptr;
0288 
0289     if (!view)
0290         return;
0291 
0292     // This function can get called with a view that is not in the current view
0293     // container (such as when the user right clicks and then the popup is
0294     // destroyed - not too sure why, but this is the easiest way to fix it).
0295     if (view->viewContainer() != vc)
0296         view = vc->activeView();
0297 
0298     if (!view || static_cast<View *>(p_focusedView) == view)
0299         return;
0300 
0301     if (p_focusedView)
0302         slotViewUnfocused();
0303 
0304     p_focusedView = view;
0305 
0306     if (TextView *textView = dynamic_cast<TextView *>(static_cast<View *>(p_focusedView)))
0307         KTechlab::self()->factory()->addClient(textView->kateView());
0308     else
0309         KTechlab::self()->factory()->addClient(p_focusedView);
0310 
0311     Document *document = view->document();
0312 
0313     connect(document, &Document::undoRedoStateChanged, KTechlab::self(), &KTechlab::slotDocUndoRedoChanged);
0314     p_connectedDocument = document;
0315 
0316     if (document->type() == Document::dt_circuit || document->type() == Document::dt_flowcode || document->type() == Document::dt_mechanics) {
0317         ItemDocument *cvb = static_cast<ItemDocument *>(view->document());
0318         ItemInterface::self()->slotItemDocumentChanged(cvb);
0319     }
0320 
0321     KTechlab::self()->slotDocUndoRedoChanged();
0322     KTechlab::self()->slotDocModifiedChanged();
0323     KTechlab::self()->requestUpdateCaptions();
0324 }
0325 
0326 void DocManager::slotViewUnfocused()
0327 {
0328     if (!KTechlab::self())
0329         return;
0330 
0331     KTechlab::self()->removeGUIClients();
0332     disableContextActions();
0333 
0334     if (!p_focusedView)
0335         return;
0336 
0337     if (p_connectedDocument) {
0338         disconnect(p_connectedDocument, &Document::undoRedoStateChanged, KTechlab::self(), &KTechlab::slotDocUndoRedoChanged);
0339         p_connectedDocument = nullptr;
0340     }
0341 
0342     ItemInterface::self()->slotItemDocumentChanged(nullptr);
0343     p_focusedView = nullptr;
0344 
0345     //  KTechlab::self()->setCaption( 0 );
0346     KTechlab::self()->requestUpdateCaptions();
0347 }
0348 
0349 void DocManager::disableContextActions()
0350 {
0351     KTechlab *ktl = KTechlab::self();
0352     if (!ktl)
0353         return;
0354 
0355     ktl->actionByName("file_save")->setEnabled(false);
0356     ktl->actionByName("file_save_as")->setEnabled(false);
0357     ktl->actionByName("file_close")->setEnabled(false);
0358     ktl->actionByName("file_print")->setEnabled(false);
0359     ktl->actionByName("edit_undo")->setEnabled(false);
0360     ktl->actionByName("edit_redo")->setEnabled(false);
0361     ktl->actionByName("edit_cut")->setEnabled(false);
0362     ktl->actionByName("edit_copy")->setEnabled(false);
0363     ktl->actionByName("edit_paste")->setEnabled(false);
0364     ktl->actionByName("view_split_leftright")->setEnabled(false);
0365     ktl->actionByName("view_split_topbottom")->setEnabled(false);
0366 }
0367 
0368 TextDocument *DocManager::createTextDocument()
0369 {
0370     TextDocument *document = TextDocument::constructTextDocument(untitledName(Document::dt_text));
0371     handleNewDocument(document);
0372     return document;
0373 }
0374 
0375 CircuitDocument *DocManager::createCircuitDocument()
0376 {
0377     CircuitDocument *document = new CircuitDocument(untitledName(Document::dt_circuit));
0378     handleNewDocument(document);
0379     if (KTLConfig::raiseItemSelectors())
0380         KTechlab::self()->showToolView(KTechlab::self()->toolView(ComponentSelector::toolViewIdentifier()));
0381     return document;
0382 }
0383 
0384 FlowCodeDocument *DocManager::createFlowCodeDocument()
0385 {
0386     FlowCodeDocument *document = new FlowCodeDocument(untitledName(Document::dt_flowcode));
0387     handleNewDocument(document);
0388     if (KTLConfig::raiseItemSelectors())
0389         KTechlab::self()->showToolView(KTechlab::self()->toolView(FlowPartSelector::toolViewIdentifier()));
0390     return document;
0391 }
0392 
0393 MechanicsDocument *DocManager::createMechanicsDocument()
0394 {
0395     MechanicsDocument *document = new MechanicsDocument(untitledName(Document::dt_mechanics));
0396     handleNewDocument(document);
0397     if (KTLConfig::raiseItemSelectors())
0398         KTechlab::self()->showToolView(KTechlab::self()->toolView(MechanicsSelector::toolViewIdentifier()));
0399     return document;
0400 }
0401 
0402 CircuitDocument *DocManager::openCircuitFile(const QUrl &url, ViewArea *viewArea)
0403 {
0404     CircuitDocument *document = new CircuitDocument(url.fileName());
0405 
0406     if (!document->openURL(url)) {
0407         KMessageBox::error(nullptr, i18n("Could not open Circuit file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile)));
0408         document->deleteLater();
0409         return nullptr;
0410     }
0411 
0412     handleNewDocument(document, viewArea);
0413     emit fileOpened(url);
0414     return document;
0415 }
0416 
0417 FlowCodeDocument *DocManager::openFlowCodeFile(const QUrl &url, ViewArea *viewArea)
0418 {
0419     FlowCodeDocument *document = new FlowCodeDocument(url.fileName());
0420 
0421     if (!document->openURL(url)) {
0422         KMessageBox::error(nullptr, i18n("Could not open FlowCode file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile)));
0423         document->deleteLater();
0424         return nullptr;
0425     }
0426 
0427     handleNewDocument(document, viewArea);
0428     emit fileOpened(url);
0429     return document;
0430 }
0431 
0432 MechanicsDocument *DocManager::openMechanicsFile(const QUrl &url, ViewArea *viewArea)
0433 {
0434     MechanicsDocument *document = new MechanicsDocument(url.fileName());
0435 
0436     if (!document->openURL(url)) {
0437         KMessageBox::error(nullptr, i18n("Could not open Mechanics file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile)));
0438         document->deleteLater();
0439         return nullptr;
0440     }
0441 
0442     handleNewDocument(document, viewArea);
0443     emit fileOpened(url);
0444     return document;
0445 }
0446 
0447 TextDocument *DocManager::openTextFile(const QUrl &url, ViewArea *viewArea)
0448 {
0449     TextDocument *document = TextDocument::constructTextDocument(url.fileName());
0450 
0451     if (!document)
0452         return nullptr;
0453 
0454     if (!document->openURL(url)) {
0455         KMessageBox::error(nullptr, i18n("Could not open text file \"%1\"", url.toDisplayString(QUrl::PreferLocalFile)));
0456         document->deleteLater();
0457         return nullptr;
0458     }
0459 
0460     handleNewDocument(document, viewArea);
0461     emit fileOpened(url);
0462     return document;
0463 }
0464 
0465 #include "moc_docmanager.cpp"