File indexing completed on 2024-12-01 05:13:00
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"