/* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katedocmanager.h" #include "katedocmanager.moc" #include "kateapp.h" #include "katemainwindow.h" #include "kateviewmanager.h" #include "katedocmanageriface.h" #include "kateexternaltools.h" #include "kateviewspacecontainer.h" #include <kate/view.h> #include <tdetexteditor/encodinginterface.h> #include <tdeparts/factory.h> #include <tdelocale.h> #include <kdebug.h> #include <tdeconfig.h> #include <klibloader.h> #include <kmdcodec.h> #include <tdemessagebox.h> #include <kencodingfiledialog.h> #include <tdeio/job.h> #include <twin.h> #include <tqdatetime.h> #include <tqtextcodec.h> #include <tqprogressdialog.h> KateDocManager::KateDocManager (TQObject *parent) : TQObject (parent) , m_saveMetaInfos(true) , m_daysMetaInfos(0) { m_factory = (KParts::Factory *) KLibLoader::self()->factory ("libkatepart"); m_documentManager = new Kate::DocumentManager (this); m_docList.setAutoDelete(true); m_docDict.setAutoDelete(false); m_docInfos.setAutoDelete(true); m_dcop = new KateDocManagerDCOPIface (this); m_metaInfos = new TDEConfig("metainfos", false, false, "appdata"); createDoc (); } KateDocManager::~KateDocManager () { // save config if (!m_docList.isEmpty()) m_docList.at(0)->writeConfig(KateApp::self()->config()); if (m_saveMetaInfos) { // saving meta-infos when file is saved is not enough, we need to do it once more at the end for (Kate::Document *doc = m_docList.first(); doc; doc = m_docList.next()) saveMetaInfos(doc); // purge saved filesessions if (m_daysMetaInfos > 0) { TQStringList groups = m_metaInfos->groupList(); TQDateTime *def = new TQDateTime(TQDate(1970, 1, 1)); for (TQStringList::Iterator it = groups.begin(); it != groups.end(); ++it) { m_metaInfos->setGroup(*it); TQDateTime last = m_metaInfos->readDateTimeEntry("Time", def); if (last.daysTo(TQDateTime::currentDateTime()) > m_daysMetaInfos) m_metaInfos->deleteGroup(*it); } delete def; } } delete m_dcop; delete m_metaInfos; } KateDocManager *KateDocManager::self () { return KateApp::self()->documentManager (); } Kate::Document *KateDocManager::createDoc () { KTextEditor::Document *doc = (KTextEditor::Document *) m_factory->createPart (0, "", this, "", "KTextEditor::Document"); m_docList.append((Kate::Document *)doc); m_docDict.insert (doc->documentNumber(), (Kate::Document *)doc); m_docInfos.insert (doc, new KateDocumentInfo ()); if (m_docList.count() < 2) ((Kate::Document *)doc)->readConfig(KateApp::self()->config()); emit documentCreated ((Kate::Document *)doc); emit m_documentManager->documentCreated ((Kate::Document *)doc); connect(doc,TQT_SIGNAL(modifiedOnDisc(Kate::Document *, bool, unsigned char)),this,TQT_SLOT(slotModifiedOnDisc(Kate::Document *, bool, unsigned char))); return (Kate::Document *)doc; } void KateDocManager::deleteDoc (Kate::Document *doc) { uint id = doc->documentNumber(); uint activeId = 0; if (m_currentDoc) activeId = m_currentDoc->documentNumber (); if (m_docList.count() < 2) doc->writeConfig(KateApp::self()->config()); m_docInfos.remove (doc); m_docDict.remove (id); m_docList.remove (doc); emit documentDeleted (id); emit m_documentManager->documentDeleted (id); // ohh, current doc was deleted if (activeId == id) { // special case of documentChanged, no longer any doc here ! m_currentDoc = 0; emit documentChanged (); emit m_documentManager->documentChanged (); } } Kate::Document *KateDocManager::document (uint n) { return m_docList.at(n); } Kate::Document *KateDocManager::activeDocument () { return m_currentDoc; } void KateDocManager::setActiveDocument (Kate::Document *doc) { if (!doc) return; if (m_currentDoc && (m_currentDoc->documentNumber() == doc->documentNumber())) return; m_currentDoc = doc; emit documentChanged (); emit m_documentManager->documentChanged (); } Kate::Document *KateDocManager::firstDocument () { return m_docList.first(); } Kate::Document *KateDocManager::nextDocument () { return m_docList.next(); } Kate::Document *KateDocManager::documentWithID (uint id) { return m_docDict[id]; } const KateDocumentInfo *KateDocManager::documentInfo (Kate::Document *doc) { return m_docInfos[doc]; } int KateDocManager::findDocument (Kate::Document *doc) { return m_docList.find (doc); } uint KateDocManager::documents () { return m_docList.count (); } int KateDocManager::findDocument ( KURL url ) { TQPtrListIterator<Kate::Document> it(m_docList); for (; it.current(); ++it) { if ( it.current()->url() == url) return it.current()->documentNumber(); } return -1; } Kate::Document *KateDocManager::findDocumentByUrl( KURL url ) { for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) { if ( it.current()->url() == url) return it.current(); } return 0L; } bool KateDocManager::isOpen(KURL url) { // return just if we found some document with this url return findDocumentByUrl (url) != 0; } Kate::Document *KateDocManager::openURL (const KURL& url,const TQString &encoding, uint *id, bool isTempFile) { // special handling if still only the first initial doc is there if (!documentList().isEmpty() && (documentList().count() == 1) && (!documentList().at(0)->isModified() && documentList().at(0)->url().isEmpty())) { Kate::Document* doc = documentList().getFirst(); doc->setEncoding(encoding); if (!loadMetaInfos(doc, url)) doc->openURL (url); if (id) *id=doc->documentNumber(); if ( isTempFile && !url.isEmpty() && url.isLocalFile() ) { TQFileInfo fi( url.path() ); if ( fi.exists() ) { m_tempFiles[ doc->documentNumber() ] = qMakePair(url, fi.lastModified()); kdDebug(13001)<<"temporary file will be deleted after use unless modified: "<<url.prettyURL()<<endl; } } connect(doc, TQT_SIGNAL(modStateChanged(Kate::Document *)), this, TQT_SLOT(slotModChanged(Kate::Document *))); emit initialDocumentReplaced(); return doc; } Kate::Document *doc = findDocumentByUrl (url); if ( !doc ) { doc = (Kate::Document *)createDoc (); doc->setEncoding(encoding.isNull() ? Kate::Document::defaultEncoding() : encoding); if (!loadMetaInfos(doc, url)) doc->openURL (url); } if (id) *id=doc->documentNumber(); if ( isTempFile && !url.isEmpty() && url.isLocalFile() ) { TQFileInfo fi( url.path() ); if ( fi.exists() ) { m_tempFiles[ doc->documentNumber() ] = qMakePair(url, fi.lastModified()); kdDebug(13001)<<"temporary file will be deleted after use unless modified: "<<url.prettyURL()<<endl; } } return doc; } bool KateDocManager::closeDocument(class Kate::Document *doc,bool closeURL) { if (!doc) return false; saveMetaInfos(doc); if (closeURL) if (!doc->closeURL()) return false; TQPtrList<Kate::View> closeList; uint documentNumber = doc->documentNumber(); for (uint i=0; i < KateApp::self()->mainWindows (); i++ ) { KateApp::self()->mainWindow(i)->viewManager()->closeViews(documentNumber); } if ( closeURL && m_tempFiles.contains( documentNumber ) ) { TQFileInfo fi( m_tempFiles[ documentNumber ].first.path() ); if ( fi.lastModified() <= m_tempFiles[ documentNumber ].second /*|| KMessageBox::questionYesNo( KateApp::self()->activeMainWindow(), i18n("The supposedly temporary file %1 has been modified. " "Do you want to delete it anyway?").arg(m_tempFiles[ documentNumber ].first.prettyURL()), i18n("Delete File?") ) == KMessageBox::Yes*/ ) { TDEIO::del( m_tempFiles[ documentNumber ].first, false, false ); kdDebug(13001)<<"Deleted temporary file "<<m_tempFiles[ documentNumber ].first<<endl; m_tempFiles.remove( documentNumber ); } else kdWarning(13001)<<"The supposedly temporary file "<<m_tempFiles[ documentNumber ].first.prettyURL()<<" have been modified since loaded, and has not been deleted."<<endl; } deleteDoc (doc); // never ever empty the whole document list if (m_docList.isEmpty()) createDoc (); return true; } bool KateDocManager::closeDocument(uint n) { return closeDocument(document(n)); } bool KateDocManager::closeDocumentWithID(uint id) { return closeDocument(documentWithID(id)); } bool KateDocManager::closeAllDocuments(bool closeURL) { bool res = true; TQPtrList<Kate::Document> docs = m_docList; for (uint i=0; i < KateApp::self()->mainWindows (); i++ ) { KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(true); } while (!docs.isEmpty() && res) if (! closeDocument(docs.at(0),closeURL) ) res = false; else docs.remove ((uint)0); for (uint i=0; i < KateApp::self()->mainWindows (); i++ ) { KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(false); for (uint s=0; s < KateApp::self()->mainWindow(i)->viewManager()->containers()->count(); s++) KateApp::self()->mainWindow(i)->viewManager()->containers()->at(s)->activateView (m_docList.at(0)->documentNumber()); } return res; } TQPtrList<Kate::Document> KateDocManager::modifiedDocumentList() { TQPtrList<Kate::Document> modified; for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) { Kate::Document *doc = it.current(); if (doc->isModified()) { modified.append(doc); } } return modified; } bool KateDocManager::queryCloseDocuments(KateMainWindow *w) { uint docCount = m_docList.count(); for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) { Kate::Document *doc = it.current(); if (doc->url().isEmpty() && doc->isModified()) { int msgres=KMessageBox::warningYesNoCancel( w, i18n("<p>The document '%1' has been modified, but not saved." "<p>Do you want to save your changes or discard them?").arg( doc->docName() ), i18n("Close Document"), KStdGuiItem::save(), KStdGuiItem::discard() ); if (msgres==KMessageBox::Cancel) return false; if (msgres==KMessageBox::Yes) { KEncodingFileDialog::Result r=KEncodingFileDialog::getSaveURLAndEncoding( KTextEditor::encodingInterface(doc)->encoding(),TQString::null,TQString::null,w,i18n("Save As")); doc->setEncoding( r.encoding ); if (!r.URLs.isEmpty()) { KURL tmp = r.URLs.first(); if ( !doc->saveAs( tmp ) ) return false; } else return false; } } else { if (!doc->queryClose()) return false; } } // document count changed while queryClose, abort and notify user if (m_docList.count() > docCount) { KMessageBox::information (w, i18n ("New file opened while trying to close Kate, closing aborted."), i18n ("Closing Aborted")); return false; } return true; } void KateDocManager::saveAll() { for (TQPtrListIterator<Kate::Document> it(m_docList); it.current(); ++it) if ( it.current()->isModified() && it.current()->views().count() ) ((Kate::View*)it.current()->views().first())->save(); } void KateDocManager::saveDocumentList (TDEConfig* config) { TQString prevGrp=config->group(); config->setGroup ("Open Documents"); TQString grp = config->group(); config->writeEntry ("Count", m_docList.count()); int i=0; for ( Kate::Document *doc = m_docList.first(); doc; doc = m_docList.next() ) { long docListPos = doc->documentListPosition(); config->setGroup(TQString("Document %1").arg((docListPos<0)?i:docListPos)); doc->writeSessionConfig(config); config->setGroup(grp); i++; } config->setGroup(prevGrp); } void KateDocManager::restoreDocumentList (TDEConfig* config) { TQString prevGrp=config->group(); config->setGroup ("Open Documents"); TQString grp = config->group(); unsigned int count = config->readUnsignedNumEntry("Count", 0); if (count == 0) { config->setGroup(prevGrp); return; } TQProgressDialog *pd = new TQProgressDialog( i18n("Reopening files from the last session..."), TQString::null, count, 0, "openprog"); KWin::setOnDesktop(pd->winId(), KWin::currentDesktop()); pd->setCaption (KateApp::self()->makeStdCaption(i18n("Starting Up"))); bool first = true; for (unsigned int i=0; i < count; i++) { config->setGroup(TQString("Document %1").arg(i)); Kate::Document *doc = 0; if (first) { first = false; doc = document (0); } else doc = createDoc (); doc->readSessionConfig(config); config->setGroup (grp); pd->setProgress(pd->progress()+1); KateApp::self()->processEvents(); } delete pd; config->setGroup(prevGrp); } void KateDocManager::slotModifiedOnDisc (Kate::Document *doc, bool b, unsigned char reason) { if (m_docInfos[doc]) { m_docInfos[doc]->modifiedOnDisc = b; m_docInfos[doc]->modifiedOnDiscReason = reason; } } void KateDocManager::slotModChanged(Kate::Document *doc) { saveMetaInfos(doc); } /** * Load file and file' meta-informations iif the MD5 didn't change since last time. */ bool KateDocManager::loadMetaInfos(Kate::Document *doc, const KURL &url) { if (!m_saveMetaInfos) return false; if (!m_metaInfos->hasGroup(url.prettyURL())) return false; TQCString md5; bool ok = true; if (computeUrlMD5(url, md5)) { m_metaInfos->setGroup(url.prettyURL()); TQString old_md5 = m_metaInfos->readEntry("MD5"); if ((const char *)md5 == old_md5) doc->readSessionConfig(m_metaInfos); else { m_metaInfos->deleteGroup(url.prettyURL()); ok = false; } m_metaInfos->sync(); } return ok && doc->url() == url; } /** * Save file' meta-informations iif doc is in 'unmodified' state */ void KateDocManager::saveMetaInfos(Kate::Document *doc) { TQCString md5; if (!m_saveMetaInfos) return; if (doc->isModified()) { // kdDebug (13020) << "DOC MODIFIED: no meta data saved" << endl; return; } if (computeUrlMD5(doc->url(), md5)) { m_metaInfos->setGroup(doc->url().prettyURL()); doc->writeSessionConfig(m_metaInfos); m_metaInfos->writeEntry("MD5", (const char *)md5); m_metaInfos->writeEntry("Time", TQDateTime::currentDateTime()); m_metaInfos->sync(); } } bool KateDocManager::computeUrlMD5(const KURL &url, TQCString &result) { TQFile f(url.path()); if (f.open(IO_ReadOnly)) { KMD5 md5; if (!md5.update(TQT_TQIODEVICE_OBJECT(f))) return false; md5.hexDigest(result); f.close(); } else return false; return true; } // kate: space-indent on; indent-width 2; replace-tabs on;