You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kate/app/katedocmanager.cpp

611 lines
15 KiB

/* 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(f))
return false;
md5.hexDigest(result);
f.close();
}
else
return false;
return true;
}