katedocmanager.cpp
00001 /* This file is part of the KDE project 00002 Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> 00003 Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "katedocmanager.h" 00021 #include "katedocmanager.moc" 00022 #include "kateapp.h" 00023 #include "katemainwindow.h" 00024 #include "kateviewmanager.h" 00025 #include "katedocmanageriface.h" 00026 #include "kateexternaltools.h" 00027 #include "kateviewspacecontainer.h" 00028 00029 #include <kate/view.h> 00030 00031 #include <tdetexteditor/encodinginterface.h> 00032 00033 #include <tdeparts/factory.h> 00034 00035 #include <tdelocale.h> 00036 #include <kdebug.h> 00037 #include <tdeconfig.h> 00038 #include <klibloader.h> 00039 #include <kmdcodec.h> 00040 #include <tdemessagebox.h> 00041 #include <kencodingfiledialog.h> 00042 #include <tdeio/job.h> 00043 #include <twin.h> 00044 00045 #include <tqdatetime.h> 00046 #include <tqtextcodec.h> 00047 #include <tqprogressdialog.h> 00048 00049 KateDocManager::KateDocManager (TQObject *parent) 00050 : TQObject (parent) 00051 , m_saveMetaInfos(true) 00052 , m_daysMetaInfos(0) 00053 { 00054 m_factory = (KParts::Factory *) KLibLoader::self()->factory ("libkatepart"); 00055 00056 m_documentManager = new Kate::DocumentManager (this); 00057 m_docList.setAutoDelete(true); 00058 m_docDict.setAutoDelete(false); 00059 m_docInfos.setAutoDelete(true); 00060 00061 m_dcop = new KateDocManagerDCOPIface (this); 00062 00063 m_metaInfos = new TDEConfig("metainfos", false, false, "appdata"); 00064 00065 createDoc (); 00066 } 00067 00068 KateDocManager::~KateDocManager () 00069 { 00070 // save config 00071 if (!m_docList.isEmpty()) 00072 m_docList.at(0)->writeConfig(KateApp::self()->config()); 00073 00074 if (m_saveMetaInfos) 00075 { 00076 // saving meta-infos when file is saved is not enough, we need to do it once more at the end 00077 for (Kate::Document *doc = m_docList.first(); doc; doc = m_docList.next()) 00078 saveMetaInfos(doc); 00079 00080 // purge saved filesessions 00081 if (m_daysMetaInfos > 0) 00082 { 00083 TQStringList groups = m_metaInfos->groupList(); 00084 TQDateTime *def = new TQDateTime(TQDate(1970, 1, 1)); 00085 for (TQStringList::Iterator it = groups.begin(); it != groups.end(); ++it) 00086 { 00087 m_metaInfos->setGroup(*it); 00088 TQDateTime last = m_metaInfos->readDateTimeEntry("Time", def); 00089 if (last.daysTo(TQDateTime::currentDateTime()) > m_daysMetaInfos) 00090 m_metaInfos->deleteGroup(*it); 00091 } 00092 delete def; 00093 } 00094 } 00095 00096 delete m_dcop; 00097 delete m_metaInfos; 00098 } 00099 00100 KateDocManager *KateDocManager::self () 00101 { 00102 return KateApp::self()->documentManager (); 00103 } 00104 00105 Kate::Document *KateDocManager::createDoc () 00106 { 00107 KTextEditor::Document *doc = (KTextEditor::Document *) m_factory->createPart (0, "", this, "", "KTextEditor::Document"); 00108 00109 m_docList.append((Kate::Document *)doc); 00110 m_docDict.insert (doc->documentNumber(), (Kate::Document *)doc); 00111 m_docInfos.insert (doc, new KateDocumentInfo ()); 00112 00113 if (m_docList.count() < 2) 00114 ((Kate::Document *)doc)->readConfig(KateApp::self()->config()); 00115 00116 emit documentCreated ((Kate::Document *)doc); 00117 emit m_documentManager->documentCreated ((Kate::Document *)doc); 00118 00119 connect(doc,TQT_SIGNAL(modifiedOnDisc(Kate::Document *, bool, unsigned char)),this,TQT_SLOT(slotModifiedOnDisc(Kate::Document *, bool, unsigned char))); 00120 return (Kate::Document *)doc; 00121 } 00122 00123 void KateDocManager::deleteDoc (Kate::Document *doc) 00124 { 00125 uint id = doc->documentNumber(); 00126 uint activeId = 0; 00127 if (m_currentDoc) 00128 activeId = m_currentDoc->documentNumber (); 00129 00130 if (m_docList.count() < 2) 00131 doc->writeConfig(KateApp::self()->config()); 00132 00133 m_docInfos.remove (doc); 00134 m_docDict.remove (id); 00135 m_docList.remove (doc); 00136 00137 emit documentDeleted (id); 00138 emit m_documentManager->documentDeleted (id); 00139 00140 // ohh, current doc was deleted 00141 if (activeId == id) 00142 { 00143 // special case of documentChanged, no longer any doc here ! 00144 m_currentDoc = 0; 00145 00146 emit documentChanged (); 00147 emit m_documentManager->documentChanged (); 00148 } 00149 } 00150 00151 Kate::Document *KateDocManager::document (uint n) 00152 { 00153 return m_docList.at(n); 00154 } 00155 00156 Kate::Document *KateDocManager::activeDocument () 00157 { 00158 return m_currentDoc; 00159 } 00160 00161 void KateDocManager::setActiveDocument (Kate::Document *doc) 00162 { 00163 if (!doc) 00164 return; 00165 00166 if (m_currentDoc && (m_currentDoc->documentNumber() == doc->documentNumber())) 00167 return; 00168 00169 m_currentDoc = doc; 00170 00171 emit documentChanged (); 00172 emit m_documentManager->documentChanged (); 00173 } 00174 00175 Kate::Document *KateDocManager::firstDocument () 00176 { 00177 return m_docList.first(); 00178 } 00179 00180 Kate::Document *KateDocManager::nextDocument () 00181 { 00182 return m_docList.next(); 00183 } 00184 00185 Kate::Document *KateDocManager::documentWithID (uint id) 00186 { 00187 return m_docDict[id]; 00188 } 00189 00190 const KateDocumentInfo *KateDocManager::documentInfo (Kate::Document *doc) 00191 { 00192 return m_docInfos[doc]; 00193 } 00194 00195 int KateDocManager::findDocument (Kate::Document *doc) 00196 { 00197 return m_docList.find (doc); 00198 } 00199 00200 uint KateDocManager::documents () 00201 { 00202 return m_docList.count (); 00203 } 00204 00205 int KateDocManager::findDocument ( KURL url ) 00206 { 00207 TQPtrListIterator<Kate::Document> it(m_docList); 00208 00209 for (; it.current(); ++it) 00210 { 00211 if ( it.current()->url() == url) 00212 return it.current()->documentNumber(); 00213 } 00214 return -1; 00215 } 00216 00217 Kate::Document *KateDocManager::findDocumentByUrl( KURL url ) 00218 { 00219 for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) 00220 { 00221 if ( it.current()->url() == url) 00222 return it.current(); 00223 } 00224 00225 return 0L; 00226 } 00227 00228 bool KateDocManager::isOpen(KURL url) 00229 { 00230 // return just if we found some document with this url 00231 return findDocumentByUrl (url) != 0; 00232 } 00233 00234 Kate::Document *KateDocManager::openURL (const KURL& url,const TQString &encoding, uint *id, bool isTempFile) 00235 { 00236 // special handling if still only the first initial doc is there 00237 if (!documentList().isEmpty() && (documentList().count() == 1) && (!documentList().at(0)->isModified() && documentList().at(0)->url().isEmpty())) 00238 { 00239 Kate::Document* doc = documentList().getFirst(); 00240 00241 doc->setEncoding(encoding); 00242 00243 if (!loadMetaInfos(doc, url)) 00244 doc->openURL (url); 00245 00246 if (id) 00247 *id=doc->documentNumber(); 00248 00249 if ( isTempFile && !url.isEmpty() && url.isLocalFile() ) 00250 { 00251 TQFileInfo fi( url.path() ); 00252 if ( fi.exists() ) 00253 { 00254 m_tempFiles[ doc->documentNumber() ] = qMakePair(url, fi.lastModified()); 00255 kdDebug(13001)<<"temporary file will be deleted after use unless modified: "<<url.prettyURL()<<endl; 00256 } 00257 } 00258 00259 connect(doc, TQT_SIGNAL(modStateChanged(Kate::Document *)), this, TQT_SLOT(slotModChanged(Kate::Document *))); 00260 00261 emit initialDocumentReplaced(); 00262 00263 return doc; 00264 } 00265 00266 Kate::Document *doc = findDocumentByUrl (url); 00267 if ( !doc ) 00268 { 00269 doc = (Kate::Document *)createDoc (); 00270 00271 doc->setEncoding(encoding.isNull() ? Kate::Document::defaultEncoding() : encoding); 00272 00273 if (!loadMetaInfos(doc, url)) 00274 doc->openURL (url); 00275 } 00276 00277 if (id) 00278 *id=doc->documentNumber(); 00279 00280 if ( isTempFile && !url.isEmpty() && url.isLocalFile() ) 00281 { 00282 TQFileInfo fi( url.path() ); 00283 if ( fi.exists() ) 00284 { 00285 m_tempFiles[ doc->documentNumber() ] = qMakePair(url, fi.lastModified()); 00286 kdDebug(13001)<<"temporary file will be deleted after use unless modified: "<<url.prettyURL()<<endl; 00287 } 00288 } 00289 00290 return doc; 00291 } 00292 00293 bool KateDocManager::closeDocument(class Kate::Document *doc,bool closeURL) 00294 { 00295 if (!doc) return false; 00296 00297 saveMetaInfos(doc); 00298 if (closeURL) 00299 if (!doc->closeURL()) return false; 00300 00301 TQPtrList<Kate::View> closeList; 00302 uint documentNumber = doc->documentNumber(); 00303 00304 for (uint i=0; i < KateApp::self()->mainWindows (); i++ ) 00305 { 00306 KateApp::self()->mainWindow(i)->viewManager()->closeViews(documentNumber); 00307 } 00308 00309 if ( closeURL && m_tempFiles.contains( documentNumber ) ) 00310 { 00311 TQFileInfo fi( m_tempFiles[ documentNumber ].first.path() ); 00312 if ( fi.lastModified() <= m_tempFiles[ documentNumber ].second /*|| 00313 KMessageBox::questionYesNo( KateApp::self()->activeMainWindow(), 00314 i18n("The supposedly temporary file %1 has been modified. " 00315 "Do you want to delete it anyway?").arg(m_tempFiles[ documentNumber ].first.prettyURL()), 00316 i18n("Delete File?") ) == KMessageBox::Yes*/ ) 00317 { 00318 TDEIO::del( m_tempFiles[ documentNumber ].first, false, false ); 00319 kdDebug(13001)<<"Deleted temporary file "<<m_tempFiles[ documentNumber ].first<<endl; 00320 m_tempFiles.remove( documentNumber ); 00321 } 00322 else 00323 kdWarning(13001)<<"The supposedly temporary file "<<m_tempFiles[ documentNumber ].first.prettyURL()<<" have been modified since loaded, and has not been deleted."<<endl; 00324 } 00325 00326 deleteDoc (doc); 00327 00328 // never ever empty the whole document list 00329 if (m_docList.isEmpty()) 00330 createDoc (); 00331 00332 return true; 00333 } 00334 00335 bool KateDocManager::closeDocument(uint n) 00336 { 00337 return closeDocument(document(n)); 00338 } 00339 00340 bool KateDocManager::closeDocumentWithID(uint id) 00341 { 00342 return closeDocument(documentWithID(id)); 00343 } 00344 00345 bool KateDocManager::closeAllDocuments(bool closeURL) 00346 { 00347 bool res = true; 00348 00349 TQPtrList<Kate::Document> docs = m_docList; 00350 00351 for (uint i=0; i < KateApp::self()->mainWindows (); i++ ) 00352 { 00353 KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(true); 00354 } 00355 00356 while (!docs.isEmpty() && res) 00357 if (! closeDocument(docs.at(0),closeURL) ) 00358 res = false; 00359 else 00360 docs.remove ((uint)0); 00361 00362 for (uint i=0; i < KateApp::self()->mainWindows (); i++ ) 00363 { 00364 KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(false); 00365 00366 for (uint s=0; s < KateApp::self()->mainWindow(i)->viewManager()->containers()->count(); s++) 00367 KateApp::self()->mainWindow(i)->viewManager()->containers()->at(s)->activateView (m_docList.at(0)->documentNumber()); 00368 } 00369 00370 return res; 00371 } 00372 00373 TQPtrList<Kate::Document> KateDocManager::modifiedDocumentList() { 00374 TQPtrList<Kate::Document> modified; 00375 for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) { 00376 Kate::Document *doc = it.current(); 00377 if (doc->isModified()) { 00378 modified.append(doc); 00379 } 00380 } 00381 return modified; 00382 } 00383 00384 00385 bool KateDocManager::queryCloseDocuments(KateMainWindow *w) 00386 { 00387 uint docCount = m_docList.count(); 00388 for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) 00389 { 00390 Kate::Document *doc = it.current(); 00391 00392 if (doc->url().isEmpty() && doc->isModified()) 00393 { 00394 int msgres=KMessageBox::warningYesNoCancel( w, 00395 i18n("<p>The document '%1' has been modified, but not saved." 00396 "<p>Do you want to save your changes or discard them?").arg( doc->docName() ), 00397 i18n("Close Document"), KStdGuiItem::save(), KStdGuiItem::discard() ); 00398 00399 if (msgres==KMessageBox::Cancel) 00400 return false; 00401 00402 if (msgres==KMessageBox::Yes) 00403 { 00404 KEncodingFileDialog::Result r=KEncodingFileDialog::getSaveURLAndEncoding( 00405 KTextEditor::encodingInterface(doc)->encoding(),TQString::null,TQString::null,w,i18n("Save As")); 00406 00407 doc->setEncoding( r.encoding ); 00408 00409 if (!r.URLs.isEmpty()) 00410 { 00411 KURL tmp = r.URLs.first(); 00412 00413 if ( !doc->saveAs( tmp ) ) 00414 return false; 00415 } 00416 else 00417 return false; 00418 } 00419 } 00420 else 00421 { 00422 if (!doc->queryClose()) 00423 return false; 00424 } 00425 } 00426 00427 // document count changed while queryClose, abort and notify user 00428 if (m_docList.count() > docCount) 00429 { 00430 KMessageBox::information (w, 00431 i18n ("New file opened while trying to close Kate, closing aborted."), 00432 i18n ("Closing Aborted")); 00433 return false; 00434 } 00435 00436 return true; 00437 } 00438 00439 00440 void KateDocManager::saveAll() 00441 { 00442 for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) 00443 if ( it.current()->isModified() && it.current()->views().count() ) 00444 ((Kate::View*)it.current()->views().first())->save(); 00445 } 00446 00447 void KateDocManager::saveDocumentList (TDEConfig* config) 00448 { 00449 TQString prevGrp=config->group(); 00450 config->setGroup ("Open Documents"); 00451 TQString grp = config->group(); 00452 00453 config->writeEntry ("Count", m_docList.count()); 00454 00455 int i=0; 00456 for ( Kate::Document *doc = m_docList.first(); doc; doc = m_docList.next() ) 00457 { 00458 long docListPos = doc->documentListPosition(); 00459 config->setGroup(TQString("Document %1").arg((docListPos<0)?i:docListPos)); 00460 doc->writeSessionConfig(config); 00461 config->setGroup(grp); 00462 00463 i++; 00464 } 00465 00466 config->setGroup(prevGrp); 00467 } 00468 00469 void KateDocManager::restoreDocumentList (TDEConfig* config) 00470 { 00471 TQString prevGrp=config->group(); 00472 config->setGroup ("Open Documents"); 00473 TQString grp = config->group(); 00474 00475 unsigned int count = config->readUnsignedNumEntry("Count", 0); 00476 00477 if (count == 0) 00478 { 00479 config->setGroup(prevGrp); 00480 return; 00481 } 00482 00483 TQProgressDialog *pd = new TQProgressDialog( 00484 i18n("Reopening files from the last session..."), 00485 TQString::null, 00486 count, 00487 0, 00488 "openprog"); 00489 00490 KWin::setOnDesktop(pd->winId(), KWin::currentDesktop()); 00491 pd->setCaption (KateApp::self()->makeStdCaption(i18n("Starting Up"))); 00492 00493 bool first = true; 00494 for (unsigned int i=0; i < count; i++) 00495 { 00496 config->setGroup(TQString("Document %1").arg(i)); 00497 Kate::Document *doc = 0; 00498 00499 if (first) 00500 { 00501 first = false; 00502 doc = document (0); 00503 } 00504 else 00505 doc = createDoc (); 00506 00507 doc->readSessionConfig(config); 00508 config->setGroup (grp); 00509 00510 pd->setProgress(pd->progress()+1); 00511 KateApp::self()->processEvents(); 00512 } 00513 00514 delete pd; 00515 00516 config->setGroup(prevGrp); 00517 } 00518 00519 void KateDocManager::slotModifiedOnDisc (Kate::Document *doc, bool b, unsigned char reason) 00520 { 00521 if (m_docInfos[doc]) 00522 { 00523 m_docInfos[doc]->modifiedOnDisc = b; 00524 m_docInfos[doc]->modifiedOnDiscReason = reason; 00525 } 00526 } 00527 00528 void KateDocManager::slotModChanged(Kate::Document *doc) 00529 { 00530 saveMetaInfos(doc); 00531 } 00532 00536 bool KateDocManager::loadMetaInfos(Kate::Document *doc, const KURL &url) 00537 { 00538 if (!m_saveMetaInfos) 00539 return false; 00540 00541 if (!m_metaInfos->hasGroup(url.prettyURL())) 00542 return false; 00543 00544 TQCString md5; 00545 bool ok = true; 00546 00547 if (computeUrlMD5(url, md5)) 00548 { 00549 m_metaInfos->setGroup(url.prettyURL()); 00550 TQString old_md5 = m_metaInfos->readEntry("MD5"); 00551 00552 if ((const char *)md5 == old_md5) 00553 doc->readSessionConfig(m_metaInfos); 00554 else 00555 { 00556 m_metaInfos->deleteGroup(url.prettyURL()); 00557 ok = false; 00558 } 00559 00560 m_metaInfos->sync(); 00561 } 00562 00563 return ok && doc->url() == url; 00564 } 00565 00569 void KateDocManager::saveMetaInfos(Kate::Document *doc) 00570 { 00571 TQCString md5; 00572 00573 if (!m_saveMetaInfos) 00574 return; 00575 00576 if (doc->isModified()) 00577 { 00578 // kdDebug (13020) << "DOC MODIFIED: no meta data saved" << endl; 00579 return; 00580 } 00581 00582 if (computeUrlMD5(doc->url(), md5)) 00583 { 00584 m_metaInfos->setGroup(doc->url().prettyURL()); 00585 doc->writeSessionConfig(m_metaInfos); 00586 m_metaInfos->writeEntry("MD5", (const char *)md5); 00587 m_metaInfos->writeEntry("Time", TQDateTime::currentDateTime()); 00588 m_metaInfos->sync(); 00589 } 00590 } 00591 00592 bool KateDocManager::computeUrlMD5(const KURL &url, TQCString &result) 00593 { 00594 TQFile f(url.path()); 00595 00596 if (f.open(IO_ReadOnly)) 00597 { 00598 KMD5 md5; 00599 00600 if (!md5.update(TQT_TQIODEVICE_OBJECT(f))) 00601 return false; 00602 00603 md5.hexDigest(result); 00604 f.close(); 00605 } 00606 else 00607 return false; 00608 00609 return true; 00610 } 00611 00612 // kate: space-indent on; indent-width 2; replace-tabs on;