|
|
|
/*
|
|
|
|
historylogger.cpp
|
|
|
|
|
|
|
|
Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
|
|
|
|
|
|
|
|
Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
|
|
|
|
|
|
|
|
*************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
*************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "historylogger.h"
|
|
|
|
#include "historyconfig.h"
|
|
|
|
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
#include <tqdom.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <ksavefile.h>
|
|
|
|
|
|
|
|
#include "kopeteglobal.h"
|
|
|
|
#include "kopetecontact.h"
|
|
|
|
#include "kopeteprotocol.h"
|
|
|
|
#include "kopeteaccount.h"
|
|
|
|
#include "kopetemetacontact.h"
|
|
|
|
#include "kopetechatsession.h"
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
HistoryLogger::HistoryLogger( Kopete::MetaContact *m, TQObject *parent, const char *name )
|
|
|
|
: TQObject(parent, name)
|
|
|
|
{
|
|
|
|
m_saveTimer=0L;
|
|
|
|
m_saveTimerTime=0;
|
|
|
|
m_metaContact=m;
|
|
|
|
m_hideOutgoing=false;
|
|
|
|
m_cachedMonth=-1;
|
|
|
|
m_realMonth=TQDate::currentDate().month();
|
|
|
|
m_oldSens=Default;
|
|
|
|
|
|
|
|
//the contact may be destroyed, for example, if the contact changes its metacontact
|
|
|
|
connect(m_metaContact , TQT_SIGNAL(destroyed(TQObject *)) , this , TQT_SLOT(slotMCDeleted()));
|
|
|
|
|
|
|
|
setPositionToLast();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HistoryLogger::HistoryLogger( Kopete::Contact *c, TQObject *parent, const char *name )
|
|
|
|
: TQObject(parent, name)
|
|
|
|
{
|
|
|
|
m_saveTimer=0L;
|
|
|
|
m_saveTimerTime=0;
|
|
|
|
m_cachedMonth=-1;
|
|
|
|
m_metaContact=c->metaContact();
|
|
|
|
m_hideOutgoing=false;
|
|
|
|
m_realMonth=TQDate::currentDate().month();
|
|
|
|
m_oldSens=Default;
|
|
|
|
|
|
|
|
//the contact may be destroyed, for example, if the contact changes its metacontact
|
|
|
|
connect(m_metaContact , TQT_SIGNAL(destroyed(TQObject *)) , this , TQT_SLOT(slotMCDeleted()));
|
|
|
|
|
|
|
|
setPositionToLast();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HistoryLogger::~HistoryLogger()
|
|
|
|
{
|
|
|
|
if(m_saveTimer && m_saveTimer->isActive())
|
|
|
|
saveToDisk();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HistoryLogger::setPositionToLast()
|
|
|
|
{
|
|
|
|
setCurrentMonth(0);
|
|
|
|
m_oldSens = AntiChronological;
|
|
|
|
m_oldMonth=0;
|
|
|
|
m_oldElements.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HistoryLogger::setPositionToFirst()
|
|
|
|
{
|
|
|
|
setCurrentMonth( getFirstMonth() );
|
|
|
|
m_oldSens = Chronological;
|
|
|
|
m_oldMonth=m_currentMonth;
|
|
|
|
m_oldElements.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HistoryLogger::setCurrentMonth(int month)
|
|
|
|
{
|
|
|
|
m_currentMonth = month;
|
|
|
|
m_currentElements.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQDomDocument HistoryLogger::getDocument(const Kopete::Contact *c, unsigned int month , bool canLoad , bool* contain)
|
|
|
|
{
|
|
|
|
if(m_realMonth!=TQDate::currentDate().month())
|
|
|
|
{ //We changed month, our indice are not correct anymore, clean memory.
|
|
|
|
// or we will see what i called "the 31 midnight bug"(TM) :-) -Olivier
|
|
|
|
m_documents.clear();
|
|
|
|
m_cachedMonth=-1;
|
|
|
|
m_currentMonth++; //Not usre it's ok, but should work;
|
|
|
|
m_oldMonth++; // idem
|
|
|
|
m_realMonth=TQDate::currentDate().month();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!m_metaContact)
|
|
|
|
{ //this may happen if the contact has been moved, and the MC deleted
|
|
|
|
if(c && c->metaContact())
|
|
|
|
m_metaContact=c->metaContact();
|
|
|
|
else
|
|
|
|
return TQDomDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!m_metaContact->contacts().contains(c))
|
|
|
|
{
|
|
|
|
if(contain)
|
|
|
|
*contain=false;
|
|
|
|
return TQDomDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQMap<unsigned int , TQDomDocument> documents = m_documents[c];
|
|
|
|
if (documents.contains(month))
|
|
|
|
return documents[month];
|
|
|
|
|
|
|
|
|
|
|
|
TQDomDocument doc = getDocument(c, TQDate::currentDate().addMonths(0-month), canLoad, contain);
|
|
|
|
|
|
|
|
documents.insert(month, doc);
|
|
|
|
m_documents[c]=documents;
|
|
|
|
|
|
|
|
return doc;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDomDocument HistoryLogger::getDocument(const Kopete::Contact *c, const TQDate date , bool canLoad , bool* contain)
|
|
|
|
{
|
|
|
|
if(!m_metaContact)
|
|
|
|
{ //this may happen if the contact has been moved, and the MC deleted
|
|
|
|
if(c && c->metaContact())
|
|
|
|
m_metaContact=c->metaContact();
|
|
|
|
else
|
|
|
|
return TQDomDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!m_metaContact->contacts().contains(c))
|
|
|
|
{
|
|
|
|
if(contain)
|
|
|
|
*contain=false;
|
|
|
|
return TQDomDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!canLoad)
|
|
|
|
{
|
|
|
|
if(contain)
|
|
|
|
*contain=false;
|
|
|
|
return TQDomDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString FileName = getFileName(c, date);
|
|
|
|
|
|
|
|
TQDomDocument doc( "Kopete-History" );
|
|
|
|
|
|
|
|
TQFile file( FileName );
|
|
|
|
if ( !file.open( IO_ReadOnly ) )
|
|
|
|
{
|
|
|
|
if(contain)
|
|
|
|
*contain=false;
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
if ( !doc.setContent( &file ) )
|
|
|
|
{
|
|
|
|
file.close();
|
|
|
|
if(contain)
|
|
|
|
*contain=false;
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
if(contain)
|
|
|
|
*contain=true;
|
|
|
|
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HistoryLogger::appendMessage( const Kopete::Message &msg , const Kopete::Contact *ct )
|
|
|
|
{
|
|
|
|
if(!msg.from())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If no contact are given: If the manager is availiable, use the manager's
|
|
|
|
// first contact (the channel on irc, or the other contact for others protocols
|
|
|
|
const Kopete::Contact *c = ct;
|
|
|
|
if(!c && msg.manager() )
|
|
|
|
{
|
|
|
|
TQPtrList<Kopete::Contact> mb=msg.manager()->members() ;
|
|
|
|
c = mb.first();
|
|
|
|
}
|
|
|
|
if(!c) //If the contact is still not initialized, use the message author.
|
|
|
|
c = msg.direction()==Kopete::Message::Outbound ? msg.to().first() : msg.from() ;
|
|
|
|
|
|
|
|
|
|
|
|
if(!m_metaContact)
|
|
|
|
{ //this may happen if the contact has been moved, and the MC deleted
|
|
|
|
if(c && c->metaContact())
|
|
|
|
m_metaContact=c->metaContact();
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!c || !m_metaContact->contacts().contains(c) )
|
|
|
|
{
|
|
|
|
/*TQPtrList<Kopete::Contact> contacts= m_metaContact->contacts();
|
|
|
|
TQPtrListIterator<Kopete::Contact> it( contacts );
|
|
|
|
for( ; it.current(); ++it )
|
|
|
|
{
|
|
|
|
if( (*it)->protocol()->pluginId() == msg.from()->protocol()->pluginId() )
|
|
|
|
{
|
|
|
|
c=*it;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
//if(!c)
|
|
|
|
|
|
|
|
kdWarning(14310) << k_funcinfo << "No contact found in this metacontact to" <<
|
|
|
|
" append in the history" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDomDocument doc=getDocument(c,0);
|
|
|
|
TQDomElement docElem = doc.documentElement();
|
|
|
|
|
|
|
|
if(docElem.isNull())
|
|
|
|
{
|
|
|
|
docElem= doc.createElement( "kopete-history" );
|
|
|
|
docElem.setAttribute ( "version" , "0.9" );
|
|
|
|
doc.appendChild( docElem );
|
|
|
|
TQDomElement headElem = doc.createElement( "head" );
|
|
|
|
docElem.appendChild( headElem );
|
|
|
|
TQDomElement dateElem = doc.createElement( "date" );
|
|
|
|
dateElem.setAttribute( "year", TQString::number(TQDate::currentDate().year()) );
|
|
|
|
dateElem.setAttribute( "month", TQString::number(TQDate::currentDate().month()) );
|
|
|
|
headElem.appendChild(dateElem);
|
|
|
|
TQDomElement myselfElem = doc.createElement( "contact" );
|
|
|
|
myselfElem.setAttribute( "type", "myself" );
|
|
|
|
myselfElem.setAttribute( "contactId", c->account()->myself()->contactId() );
|
|
|
|
headElem.appendChild(myselfElem);
|
|
|
|
TQDomElement contactElem = doc.createElement( "contact" );
|
|
|
|
contactElem.setAttribute( "contactId", c->contactId() );
|
|
|
|
headElem.appendChild(contactElem);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDomElement msgElem = doc.createElement( "msg" );
|
|
|
|
msgElem.setAttribute( "in", msg.direction()==Kopete::Message::Outbound ? "0" : "1" );
|
|
|
|
msgElem.setAttribute( "from", msg.from()->contactId() );
|
|
|
|
msgElem.setAttribute( "nick", msg.from()->property( Kopete::Global::Properties::self()->nickName() ).value().toString() ); //do we have to set this?
|
|
|
|
msgElem.setAttribute( "time", msg.timestamp().toString("d h:m:s") );
|
|
|
|
|
|
|
|
TQDomText msgNode = doc.createTextNode( msg.plainBody() );
|
|
|
|
docElem.appendChild( msgElem );
|
|
|
|
msgElem.appendChild( msgNode );
|
|
|
|
|
|
|
|
|
|
|
|
// I'm temporizing the save.
|
|
|
|
// On hight-traffic channel, saving can take lots of CPU. (because the file is big)
|
|
|
|
// So i wait a time proportional to the time needed to save..
|
|
|
|
|
|
|
|
const TQString filename=getFileName(c,TQDate::currentDate());
|
|
|
|
if(!m_toSaveFileName.isEmpty() && m_toSaveFileName != filename)
|
|
|
|
{ //that mean the contact or the month has changed, save it now.
|
|
|
|
saveToDisk();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_toSaveFileName=filename;
|
|
|
|
m_toSaveDocument=doc;
|
|
|
|
|
|
|
|
if(!m_saveTimer)
|
|
|
|
{
|
|
|
|
m_saveTimer=new TQTimer(this);
|
|
|
|
connect( m_saveTimer, TQT_SIGNAL( timeout() ) , this, TQT_SLOT(saveToDisk()) );
|
|
|
|
}
|
|
|
|
if(!m_saveTimer->isActive())
|
|
|
|
m_saveTimer->start( m_saveTimerTime, true /*singleshot*/ );
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryLogger::saveToDisk()
|
|
|
|
{
|
|
|
|
if(m_saveTimer)
|
|
|
|
m_saveTimer->stop();
|
|
|
|
if(m_toSaveFileName.isEmpty() || m_toSaveDocument.isNull())
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQTime t;
|
|
|
|
t.start(); //mesure the time needed to save.
|
|
|
|
|
|
|
|
KSaveFile file( m_toSaveFileName );
|
|
|
|
if( file.status() == 0 )
|
|
|
|
{
|
|
|
|
TQTextStream *stream = file.textStream();
|
|
|
|
//stream->setEncoding( TQTextStream::UnicodeUTF8 ); //???? oui ou non?
|
|
|
|
m_toSaveDocument.save( *stream, 1 );
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
m_saveTimerTime=TQMIN(t.elapsed()*1000, 300000);
|
|
|
|
//a time 1000 times supperior to the time needed to save. but with a upper limit of 5 minutes
|
|
|
|
//on a my machine, (2.4Ghz, but old HD) it should take about 10 ms to save the file.
|
|
|
|
// So that would mean save every 10 seconds, which seems to be ok.
|
|
|
|
// But it may take 500 ms if the file to save becomes too big (1Mb).
|
|
|
|
kdDebug(14310) << k_funcinfo << m_toSaveFileName << " saved in " << t.elapsed() << " ms " <<endl ;
|
|
|
|
|
|
|
|
m_toSaveFileName=TQString();
|
|
|
|
m_toSaveDocument=TQDomDocument();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
kdError(14310) << k_funcinfo << "impossible to save the history file " << m_toSaveFileName << endl;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueList<Kopete::Message> HistoryLogger::readMessages(TQDate date)
|
|
|
|
{
|
|
|
|
TQRegExp rxTime("(\\d+) (\\d+):(\\d+)($|:)(\\d*)"); //(with a 0.7.x compatibility)
|
|
|
|
TQValueList<Kopete::Message> messages;
|
|
|
|
|
|
|
|
|
|
|
|
TQPtrList<Kopete::Contact> ct=m_metaContact->contacts();
|
|
|
|
TQPtrListIterator<Kopete::Contact> it( ct );
|
|
|
|
|
|
|
|
for( ; it.current(); ++it )
|
|
|
|
{
|
|
|
|
TQDomDocument doc=getDocument(*it,date, true, 0L);
|
|
|
|
TQDomElement docElem = doc.documentElement();
|
|
|
|
TQDomNode n = docElem.firstChild();
|
|
|
|
|
|
|
|
while(!n.isNull())
|
|
|
|
{
|
|
|
|
TQDomElement msgElem2 = n.toElement();
|
|
|
|
if( !msgElem2.isNull() && msgElem2.tagName()=="msg")
|
|
|
|
{
|
|
|
|
rxTime.search(msgElem2.attribute("time"));
|
|
|
|
TQDateTime dt( TQDate(date.year() , date.month() , rxTime.cap(1).toUInt()), TQTime( rxTime.cap(2).toUInt() , rxTime.cap(3).toUInt(), rxTime.cap(5).toUInt() ) );
|
|
|
|
|
|
|
|
if (dt.date() != date)
|
|
|
|
{
|
|
|
|
n = n.nextSibling();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Kopete::Message::MessageDirection dir = (msgElem2.attribute("in") == "1") ?
|
|
|
|
Kopete::Message::Inbound : Kopete::Message::Outbound;
|
|
|
|
|
|
|
|
if(!m_hideOutgoing || dir != Kopete::Message::Outbound)
|
|
|
|
{ //parse only if we don't hide it
|
|
|
|
|
|
|
|
TQString f=msgElem2.attribute("from" );
|
|
|
|
const Kopete::Contact *from=f.isNull()? 0L : (*it)->account()->contacts()[f];
|
|
|
|
|
|
|
|
if(!from)
|
|
|
|
from= dir==Kopete::Message::Inbound ? (*it) : (*it)->account()->myself();
|
|
|
|
|
|
|
|
Kopete::ContactPtrList to;
|
|
|
|
to.append( dir==Kopete::Message::Inbound ? (*it)->account()->myself() : *it );
|
|
|
|
|
|
|
|
Kopete::Message msg(dt, from, to, msgElem2.text(), dir);
|
|
|
|
msg.setBody( TQString::fromLatin1("<span title=\"%1\">%2</span>")
|
|
|
|
.arg( dt.toString(Qt::LocalDate), msg.escapedBody() ),
|
|
|
|
Kopete::Message::RichText);
|
|
|
|
|
|
|
|
|
|
|
|
// We insert it at the good place, given its date
|
|
|
|
TQValueListIterator<Kopete::Message> msgIt;
|
|
|
|
|
|
|
|
for (msgIt = messages.begin(); msgIt != messages.end(); ++msgIt)
|
|
|
|
{
|
|
|
|
if ((*msgIt).timestamp() > msg.timestamp())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
messages.insert(msgIt, msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n = n.nextSibling();
|
|
|
|
} // end while on messages
|
|
|
|
|
|
|
|
}
|
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueList<Kopete::Message> HistoryLogger::readMessages(unsigned int lines,
|
|
|
|
const Kopete::Contact *c, Sens sens, bool reverseOrder, bool colorize)
|
|
|
|
{
|
|
|
|
//TQDate dd = TQDate::currentDate().addMonths(0-m_currentMonth);
|
|
|
|
|
|
|
|
TQValueList<Kopete::Message> messages;
|
|
|
|
|
|
|
|
// A regexp useful for this function
|
|
|
|
TQRegExp rxTime("(\\d+) (\\d+):(\\d+)($|:)(\\d*)"); //(with a 0.7.x compatibility)
|
|
|
|
|
|
|
|
if(!m_metaContact)
|
|
|
|
{ //this may happen if the contact has been moved, and the MC deleted
|
|
|
|
if(c && c->metaContact())
|
|
|
|
m_metaContact=c->metaContact();
|
|
|
|
else
|
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(c && !m_metaContact->contacts().contains(c) )
|
|
|
|
return messages;
|
|
|
|
|
|
|
|
if(sens ==0 ) //if no sens are selected, just continue in the previous sens
|
|
|
|
sens = m_oldSens ;
|
|
|
|
if( m_oldSens != 0 && sens != m_oldSens )
|
|
|
|
{ //we changed our sens! so retrieve the old position to fly in the other way
|
|
|
|
m_currentElements= m_oldElements;
|
|
|
|
m_currentMonth=m_oldMonth;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_oldElements=m_currentElements;
|
|
|
|
m_oldMonth=m_currentMonth;
|
|
|
|
}
|
|
|
|
m_oldSens=sens;
|
|
|
|
|
|
|
|
//getting the color for messages:
|
|
|
|
TQColor fgColor = HistoryConfig::history_color();
|
|
|
|
|
|
|
|
//Hello guest!
|
|
|
|
|
|
|
|
//there are two algoritms:
|
|
|
|
// - if a contact is given, or the metacontact contain only one contact, just read the history.
|
|
|
|
// - else, merge the history
|
|
|
|
|
|
|
|
//the merging algoritm is the following:
|
|
|
|
// we see what contact we have to read first, and we look at the firt date before another contact
|
|
|
|
// has a message with a bigger date.
|
|
|
|
|
|
|
|
TQDateTime timeLimit;
|
|
|
|
const Kopete::Contact *currentContact=c;
|
|
|
|
if(!c && m_metaContact->contacts().count()==1)
|
|
|
|
currentContact=m_metaContact->contacts().first();
|
|
|
|
else if(!c && m_metaContact->contacts().count()== 0)
|
|
|
|
{
|
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
|
|
|
while(messages.count() < lines)
|
|
|
|
{
|
|
|
|
timeLimit=TQDateTime();
|
|
|
|
TQDomElement msgElem; //here is the message element
|
|
|
|
TQDateTime timestamp; //and the timestamp of this message
|
|
|
|
|
|
|
|
if(!c && m_metaContact->contacts().count()>1)
|
|
|
|
{ //we have to merge the differents subcontact history
|
|
|
|
TQPtrList<Kopete::Contact> ct=m_metaContact->contacts();
|
|
|
|
TQPtrListIterator<Kopete::Contact> it( ct );
|
|
|
|
for( ; it.current(); ++it )
|
|
|
|
{ //we loop over each contact. we are searching the contact with the next message with the smallest date,
|
|
|
|
// it will becomes our current contact, and the contact with the mext message with the second smallest
|
|
|
|
// date, this date will bocomes the limit.
|
|
|
|
|
|
|
|
TQDomNode n;
|
|
|
|
if(m_currentElements.contains(*it))
|
|
|
|
n=m_currentElements[*it];
|
|
|
|
else //there is not yet "next message" register, so we will take the first (for the current month)
|
|
|
|
{
|
|
|
|
TQDomDocument doc=getDocument(*it,m_currentMonth);
|
|
|
|
TQDomElement docElem = doc.documentElement();
|
|
|
|
n= (sens==Chronological)?docElem.firstChild() : docElem.lastChild();
|
|
|
|
|
|
|
|
//i can't drop the root element
|
|
|
|
workaround.append(docElem);
|
|
|
|
}
|
|
|
|
while(!n.isNull())
|
|
|
|
{
|
|
|
|
TQDomElement msgElem2 = n.toElement();
|
|
|
|
if( !msgElem2.isNull() && msgElem2.tagName()=="msg")
|
|
|
|
{
|
|
|
|
rxTime.search(msgElem2.attribute("time"));
|
|
|
|
TQDate d=TQDate::currentDate().addMonths(0-m_currentMonth);
|
|
|
|
TQDateTime dt( TQDate(d.year() , d.month() , rxTime.cap(1).toUInt()), TQTime( rxTime.cap(2).toUInt() , rxTime.cap(3).toUInt(), rxTime.cap(5).toUInt() ) );
|
|
|
|
if(!timestamp.isValid() || ((sens==Chronological )? dt < timestamp : dt > timestamp) )
|
|
|
|
{
|
|
|
|
timeLimit=timestamp;
|
|
|
|
timestamp=dt;
|
|
|
|
msgElem=msgElem2;
|
|
|
|
currentContact=*it;
|
|
|
|
|
|
|
|
}
|
|
|
|
else if(!timeLimit.isValid() || ((sens==Chronological) ? timeLimit > dt : timeLimit < dt) )
|
|
|
|
{
|
|
|
|
timeLimit=dt;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
n=(sens==Chronological)? n.nextSibling() : n.previousSibling();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else //we don't have to merge the history. just take the next item in the contact
|
|
|
|
{
|
|
|
|
if(m_currentElements.contains(currentContact))
|
|
|
|
msgElem=m_currentElements[currentContact];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TQDomDocument doc=getDocument(currentContact,m_currentMonth);
|
|
|
|
TQDomElement docElem = doc.documentElement();
|
|
|
|
TQDomNode n= (sens==Chronological)?docElem.firstChild() : docElem.lastChild();
|
|
|
|
msgElem=TQDomElement();
|
|
|
|
while(!n.isNull()) //continue until we get a msg
|
|
|
|
{
|
|
|
|
msgElem=n.toElement();
|
|
|
|
if( !msgElem.isNull() && msgElem.tagName()=="msg")
|
|
|
|
{
|
|
|
|
m_currentElements[currentContact]=msgElem;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
n=(sens==Chronological)? n.nextSibling() : n.previousSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
//i can't drop the root element
|
|
|
|
workaround.append(docElem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(msgElem.isNull()) //we don't find ANY messages in any contact for this month. so we change the month
|
|
|
|
{
|
|
|
|
if(sens==Chronological)
|
|
|
|
{
|
|
|
|
if(m_currentMonth <= 0)
|
|
|
|
break; //there are no other messages to show. break even if we don't have nb messages
|
|
|
|
setCurrentMonth(m_currentMonth-1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(m_currentMonth >= getFirstMonth(c))
|
|
|
|
break; //we don't have any other messages to show
|
|
|
|
setCurrentMonth(m_currentMonth+1);
|
|
|
|
}
|
|
|
|
continue; //begin the loop from the bottom, and find currentContact and timeLimit again
|
|
|
|
}
|
|
|
|
|
|
|
|
while(
|
|
|
|
(messages.count() < lines) &&
|
|
|
|
!msgElem.isNull() &&
|
|
|
|
(!timestamp.isValid() || !timeLimit.isValid() ||
|
|
|
|
((sens==Chronological) ? timestamp <= timeLimit : timestamp >= timeLimit)
|
|
|
|
))
|
|
|
|
{
|
|
|
|
// break this loop, if we have reached the correct number of messages,
|
|
|
|
// if there are no more messages for this contact, or if we reached
|
|
|
|
// the timeLimit msgElem is the next message, still not parsed, so
|
|
|
|
// we parse it now
|
|
|
|
|
|
|
|
Kopete::Message::MessageDirection dir = (msgElem.attribute("in") == "1") ?
|
|
|
|
Kopete::Message::Inbound : Kopete::Message::Outbound;
|
|
|
|
|
|
|
|
if(!m_hideOutgoing || dir != Kopete::Message::Outbound)
|
|
|
|
{ //parse only if we don't hide it
|
|
|
|
|
|
|
|
if( m_filter.isNull() || ( m_filterRegExp? msgElem.text().contains(TQRegExp(m_filter,m_filterCaseSensitive)) : msgElem.text().contains(m_filter,m_filterCaseSensitive) ))
|
|
|
|
{
|
|
|
|
TQString f=msgElem.attribute("from" );
|
|
|
|
const Kopete::Contact *from=(f.isNull() || !currentContact) ? 0L : currentContact->account()->contacts()[f];
|
|
|
|
|
|
|
|
if(!from)
|
|
|
|
from= dir==Kopete::Message::Inbound ? currentContact : currentContact->account()->myself();
|
|
|
|
|
|
|
|
Kopete::ContactPtrList to;
|
|
|
|
to.append( dir==Kopete::Message::Inbound ? currentContact->account()->myself() : currentContact );
|
|
|
|
|
|
|
|
if(!timestamp.isValid())
|
|
|
|
{
|
|
|
|
//parse timestamp only if it was not already parsed
|
|
|
|
rxTime.search(msgElem.attribute("time"));
|
|
|
|
TQDate d=TQDate::currentDate().addMonths(0-m_currentMonth);
|
|
|
|
timestamp=TQDateTime( TQDate(d.year() , d.month() , rxTime.cap(1).toUInt()), TQTime( rxTime.cap(2).toUInt() , rxTime.cap(3).toUInt() , rxTime.cap(5).toUInt() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
Kopete::Message msg(timestamp, from, to, msgElem.text(), dir);
|
|
|
|
if (colorize)
|
|
|
|
{
|
|
|
|
msg.setBody( TQString::fromLatin1("<span style=\"color:%1\" title=\"%2\">%3</span>")
|
|
|
|
.arg( fgColor.name(), timestamp.toString(Qt::LocalDate), msg.escapedBody() ),
|
|
|
|
Kopete::Message::RichText
|
|
|
|
);
|
|
|
|
msg.setFg( fgColor );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
msg.setBody( TQString::fromLatin1("<span title=\"%1\">%2</span>")
|
|
|
|
.arg( timestamp.toString(Qt::LocalDate), msg.escapedBody() ),
|
|
|
|
Kopete::Message::RichText
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(reverseOrder)
|
|
|
|
messages.prepend(msg);
|
|
|
|
else
|
|
|
|
messages.append(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//here is the point of workaround. If i drop the root element, this crashes
|
|
|
|
//get the next message
|
|
|
|
TQDomNode node = ( (sens==Chronological) ? msgElem.nextSibling() :
|
|
|
|
msgElem.previousSibling() );
|
|
|
|
|
|
|
|
msgElem = TQDomElement(); //n.toElement();
|
|
|
|
while (!node.isNull() && msgElem.isNull())
|
|
|
|
{
|
|
|
|
msgElem = node.toElement();
|
|
|
|
if (!msgElem.isNull())
|
|
|
|
{
|
|
|
|
if (msgElem.tagName() == "msg")
|
|
|
|
{
|
|
|
|
if (!c && (m_metaContact->contacts().count() > 1))
|
|
|
|
{
|
|
|
|
// In case of hideoutgoing messages, it is faster to do
|
|
|
|
// this, so we don't parse the date if it is not needed
|
|
|
|
TQRegExp rx("(\\d+) (\\d+):(\\d+):(\\d+)");
|
|
|
|
rx.search(msgElem.attribute("time"));
|
|
|
|
|
|
|
|
TQDate d = TQDate::currentDate().addMonths(0-m_currentMonth);
|
|
|
|
timestamp = TQDateTime(
|
|
|
|
TQDate(d.year(), d.month(), rx.cap(1).toUInt()),
|
|
|
|
TQTime( rx.cap(2).toUInt(), rx.cap(3).toUInt() ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
timestamp = TQDateTime(); //invalid
|
|
|
|
}
|
|
|
|
else
|
|
|
|
msgElem = TQDomElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
node = (sens == Chronological) ? node.nextSibling() :
|
|
|
|
node.previousSibling();
|
|
|
|
}
|
|
|
|
m_currentElements[currentContact]=msgElem; //this is the next message
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(messages.count() < lines)
|
|
|
|
m_currentElements.clear(); //current elements are null this can't be allowed
|
|
|
|
|
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString HistoryLogger::getFileName(const Kopete::Contact* c, TQDate date)
|
|
|
|
{
|
|
|
|
|
|
|
|
TQString name = c->protocol()->pluginId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) ) +
|
|
|
|
TQString::fromLatin1( "/" ) +
|
|
|
|
c->account()->accountId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) ) +
|
|
|
|
TQString::fromLatin1( "/" ) +
|
|
|
|
c->contactId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) ) +
|
|
|
|
date.toString(".yyyyMM");
|
|
|
|
|
|
|
|
TQString filename=locateLocal( "data", TQString::fromLatin1( "kopete/logs/" ) + name+ TQString::fromLatin1( ".xml" ) ) ;
|
|
|
|
|
|
|
|
//Check if there is a kopete 0.7.x file
|
|
|
|
TQFileInfo fi(filename);
|
|
|
|
if(!fi.exists())
|
|
|
|
{
|
|
|
|
name = c->protocol()->pluginId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) ) +
|
|
|
|
TQString::fromLatin1( "/" ) +
|
|
|
|
c->contactId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) ) +
|
|
|
|
date.toString(".yyyyMM");
|
|
|
|
|
|
|
|
TQString filename2=locateLocal( "data", TQString::fromLatin1( "kopete/logs/" ) + name+ TQString::fromLatin1( ".xml" ) ) ;
|
|
|
|
|
|
|
|
TQFileInfo fi2(filename2);
|
|
|
|
if(fi2.exists())
|
|
|
|
return filename2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return filename;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int HistoryLogger::getFirstMonth(const Kopete::Contact *c)
|
|
|
|
{
|
|
|
|
if(!c)
|
|
|
|
return getFirstMonth();
|
|
|
|
|
|
|
|
TQRegExp rx( "\\.(\\d\\d\\d\\d)(\\d\\d)" );
|
|
|
|
TQFileInfo *fi;
|
|
|
|
|
|
|
|
// BEGIN check if there are Kopete 0.7.x
|
|
|
|
TQDir d1(locateLocal("data",TQString("kopete/logs/")+
|
|
|
|
c->protocol()->pluginId().replace( TQRegExp(TQString::fromLatin1("[./~?*]")),TQString::fromLatin1("-"))
|
|
|
|
));
|
|
|
|
d1.setFilter( TQDir::Files | TQDir::NoSymLinks );
|
|
|
|
d1.setSorting( TQDir::Name );
|
|
|
|
|
|
|
|
const TQFileInfoList *list1 = d1.entryInfoList();
|
|
|
|
TQFileInfoListIterator it1( *list1 );
|
|
|
|
|
|
|
|
while ( (fi = it1.current()) != 0 )
|
|
|
|
{
|
|
|
|
if(fi->fileName().contains(c->contactId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) )))
|
|
|
|
{
|
|
|
|
rx.search(fi->fileName());
|
|
|
|
int result = 12*(TQDate::currentDate().year() - rx.cap(1).toUInt()) +TQDate::currentDate().month() - rx.cap(2).toUInt();
|
|
|
|
|
|
|
|
if(result < 0)
|
|
|
|
{
|
|
|
|
kdWarning(14310) << k_funcinfo << "Kopete only found log file from Kopete 0.7.x made in the future. Check your date!" << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
++it1;
|
|
|
|
}
|
|
|
|
// END of kopete 0.7.x check
|
|
|
|
|
|
|
|
|
|
|
|
TQDir d(locateLocal("data",TQString("kopete/logs/")+
|
|
|
|
c->protocol()->pluginId().replace( TQRegExp(TQString::fromLatin1("[./~?*]")),TQString::fromLatin1("-")) +
|
|
|
|
TQString::fromLatin1( "/" ) +
|
|
|
|
c->account()->accountId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) )
|
|
|
|
));
|
|
|
|
|
|
|
|
d.setFilter( TQDir::Files | TQDir::NoSymLinks );
|
|
|
|
d.setSorting( TQDir::Name );
|
|
|
|
|
|
|
|
const TQFileInfoList *list = d.entryInfoList();
|
|
|
|
TQFileInfoListIterator it( *list );
|
|
|
|
while ( (fi = it.current()) != 0 )
|
|
|
|
{
|
|
|
|
if(fi->fileName().contains(c->contactId().replace( TQRegExp( TQString::fromLatin1( "[./~?*]" ) ), TQString::fromLatin1( "-" ) )))
|
|
|
|
{
|
|
|
|
rx.search(fi->fileName());
|
|
|
|
int result = 12*(TQDate::currentDate().year() - rx.cap(1).toUInt()) +TQDate::currentDate().month() - rx.cap(2).toUInt();
|
|
|
|
if(result < 0)
|
|
|
|
{
|
|
|
|
kdWarning(14310) << k_funcinfo << "Kopete only found log file made in the future. Check your date!" << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int HistoryLogger::getFirstMonth()
|
|
|
|
{
|
|
|
|
if(m_cachedMonth!=-1)
|
|
|
|
return m_cachedMonth;
|
|
|
|
|
|
|
|
if(!m_metaContact)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int m=0;
|
|
|
|
TQPtrList<Kopete::Contact> contacts=m_metaContact->contacts();
|
|
|
|
TQPtrListIterator<Kopete::Contact> it( contacts );
|
|
|
|
for( ; it.current(); ++it )
|
|
|
|
{
|
|
|
|
int m2=getFirstMonth(*it);
|
|
|
|
if(m2>m) m=m2;
|
|
|
|
}
|
|
|
|
m_cachedMonth=m;
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryLogger::setHideOutgoing(bool b)
|
|
|
|
{
|
|
|
|
m_hideOutgoing = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryLogger::slotMCDeleted()
|
|
|
|
{
|
|
|
|
m_metaContact = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryLogger::setFilter(const TQString& filter, bool caseSensitive , bool isRegExp)
|
|
|
|
{
|
|
|
|
m_filter=filter;
|
|
|
|
m_filterCaseSensitive=caseSensitive;
|
|
|
|
m_filterRegExp=isRegExp;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString HistoryLogger::filter() const
|
|
|
|
{
|
|
|
|
return m_filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryLogger::filterCaseSensitive() const
|
|
|
|
{
|
|
|
|
return m_filterCaseSensitive;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HistoryLogger::filterRegExp() const
|
|
|
|
{
|
|
|
|
return m_filterRegExp;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueList<int> HistoryLogger::getDaysForMonth(TQDate date)
|
|
|
|
{
|
|
|
|
TQRegExp rxTime("time=\"(\\d+) \\d+:\\d+(:\\d+)?\""); //(with a 0.7.x compatibility)
|
|
|
|
|
|
|
|
TQValueList<int> dayList;
|
|
|
|
|
|
|
|
TQPtrList<Kopete::Contact> contacts = m_metaContact->contacts();
|
|
|
|
TQPtrListIterator<Kopete::Contact> it(contacts);
|
|
|
|
|
|
|
|
int lastDay=0;
|
|
|
|
for(; it.current(); ++it)
|
|
|
|
{
|
|
|
|
// kdDebug() << getFileName(*it, date) << endl;
|
|
|
|
TQFile file(getFileName(*it, date));
|
|
|
|
if(!file.open(IO_ReadOnly))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
TQTextStream stream(&file);
|
|
|
|
TQString fullText = stream.read();
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
int pos = 0;
|
|
|
|
while( (pos = rxTime.search(fullText, pos)) != -1)
|
|
|
|
{
|
|
|
|
pos += rxTime.matchedLength();
|
|
|
|
int day=rxTime.capturedTexts()[1].toInt();
|
|
|
|
|
|
|
|
if ( day !=lastDay && dayList.find(day) == dayList.end()) // avoid duplicates
|
|
|
|
{
|
|
|
|
dayList.append(rxTime.capturedTexts()[1].toInt());
|
|
|
|
lastDay=day;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dayList;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "historylogger.moc"
|