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