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 <ktexteditor/encodinginterface.h> 00032 00033 #include <kparts/factory.h> 00034 00035 #include <klocale.h> 00036 #include <kdebug.h> 00037 #include <kconfig.h> 00038 #include <klibloader.h> 00039 #include <kmdcodec.h> 00040 #include <kmessagebox.h> 00041 #include <kencodingfiledialog.h> 00042 #include <kio/job.h> 00043 #include <kwin.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 KConfig("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 KIO::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 (KConfig* 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 config->setGroup(TQString("Document %1").arg(i)); 00459 doc->writeSessionConfig(config); 00460 config->setGroup(grp); 00461 00462 i++; 00463 } 00464 00465 config->setGroup(prevGrp); 00466 } 00467 00468 void KateDocManager::restoreDocumentList (KConfig* config) 00469 { 00470 TQString prevGrp=config->group(); 00471 config->setGroup ("Open Documents"); 00472 TQString grp = config->group(); 00473 00474 unsigned int count = config->readUnsignedNumEntry("Count", 0); 00475 00476 if (count == 0) 00477 { 00478 config->setGroup(prevGrp); 00479 return; 00480 } 00481 00482 TQProgressDialog *pd = new TQProgressDialog( 00483 i18n("Reopening files from the last session..."), 00484 TQString::null, 00485 count, 00486 0, 00487 "openprog"); 00488 00489 KWin::setOnDesktop(pd->winId(), KWin::currentDesktop()); 00490 pd->setCaption (KateApp::self()->makeStdCaption(i18n("Starting Up"))); 00491 00492 bool first = true; 00493 for (unsigned int i=0; i < count; i++) 00494 { 00495 config->setGroup(TQString("Document %1").arg(i)); 00496 Kate::Document *doc = 0; 00497 00498 if (first) 00499 { 00500 first = false; 00501 doc = document (0); 00502 } 00503 else 00504 doc = createDoc (); 00505 00506 doc->readSessionConfig(config); 00507 config->setGroup (grp); 00508 00509 pd->setProgress(pd->progress()+1); 00510 KateApp::self()->processEvents(); 00511 } 00512 00513 delete pd; 00514 00515 config->setGroup(prevGrp); 00516 } 00517 00518 void KateDocManager::slotModifiedOnDisc (Kate::Document *doc, bool b, unsigned char reason) 00519 { 00520 if (m_docInfos[doc]) 00521 { 00522 m_docInfos[doc]->modifiedOnDisc = b; 00523 m_docInfos[doc]->modifiedOnDiscReason = reason; 00524 } 00525 } 00526 00527 void KateDocManager::slotModChanged(Kate::Document *doc) 00528 { 00529 saveMetaInfos(doc); 00530 } 00531 00535 bool KateDocManager::loadMetaInfos(Kate::Document *doc, const KURL &url) 00536 { 00537 if (!m_saveMetaInfos) 00538 return false; 00539 00540 if (!m_metaInfos->hasGroup(url.prettyURL())) 00541 return false; 00542 00543 TQCString md5; 00544 bool ok = true; 00545 00546 if (computeUrlMD5(url, md5)) 00547 { 00548 m_metaInfos->setGroup(url.prettyURL()); 00549 TQString old_md5 = m_metaInfos->readEntry("MD5"); 00550 00551 if ((const char *)md5 == old_md5) 00552 doc->readSessionConfig(m_metaInfos); 00553 else 00554 { 00555 m_metaInfos->deleteGroup(url.prettyURL()); 00556 ok = false; 00557 } 00558 00559 m_metaInfos->sync(); 00560 } 00561 00562 return ok && doc->url() == url; 00563 } 00564 00568 void KateDocManager::saveMetaInfos(Kate::Document *doc) 00569 { 00570 TQCString md5; 00571 00572 if (!m_saveMetaInfos) 00573 return; 00574 00575 if (doc->isModified()) 00576 { 00577 // kdDebug (13020) << "DOC MODIFIED: no meta data saved" << endl; 00578 return; 00579 } 00580 00581 if (computeUrlMD5(doc->url(), md5)) 00582 { 00583 m_metaInfos->setGroup(doc->url().prettyURL()); 00584 doc->writeSessionConfig(m_metaInfos); 00585 m_metaInfos->writeEntry("MD5", (const char *)md5); 00586 m_metaInfos->writeEntry("Time", TQDateTime::currentDateTime()); 00587 m_metaInfos->sync(); 00588 } 00589 } 00590 00591 bool KateDocManager::computeUrlMD5(const KURL &url, TQCString &result) 00592 { 00593 TQFile f(url.path()); 00594 00595 if (f.open(IO_ReadOnly)) 00596 { 00597 KMD5 md5; 00598 00599 if (!md5.update(TQT_TQIODEVICE_OBJECT(f))) 00600 return false; 00601 00602 md5.hexDigest(result); 00603 f.close(); 00604 } 00605 else 00606 return false; 00607 00608 return true; 00609 } 00610 00611 // kate: space-indent on; indent-width 2; replace-tabs on;