Hi,In this summer of code, i m working on the project, KDE-Conversation
logging framework.
A part of the project is to Modify the history plugin of Kopete such that
it saves its chat in Akonadi.
In the process of modification, we decided that it is a good idea to replace
the QDomDocument with the History object, that represents a chatLog. While
trying to understand the code of the function given below,(as is uses
QDomNode, and Element everywhere), i though it would be a good idea, to get
mylogic clear, about the algorithm that is used in this function.
The function is as follows
QList<Kopete::Message> HistoryLogger::readMessages(int lines,
const Kopete::Contact *c, Sens sens, bool reverseOrder, bool colorize)
{
//QDate dd = QDate::currentDate().addMonths(0-m_currentMonth);
QList<Kopete::Message> messages;
// A regexp useful for this function
QRegExp 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(const_cast<Kopete::Contact*>(c)) )
return messages;
if(sens == Default ) //if no sens are selected, just continue in the
previous sens
sens = m_oldSens ;
if( m_oldSens != Default && 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:
QColor 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.
QDateTime 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=QDateTime();
QDomElement msgElem; //here is the message element
QDateTime timestamp; //and the timestamp of this message
if(!c && m_metaContact->contacts().count()>1)
{ //we have to merge the differents subcontact history
QList<Kopete::Contact*> ct=m_metaContact->contacts();
foreach(Kopete::Contact *contact, ct)
{ //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.
QDomNode n;
if(m_currentElements.contains(contact))
n=m_currentElements[contact];
else //there is not yet "next message"
register, so we will take
the first (for the current month)
{
QDomDocument
doc=getDocument(contact,m_currentMonth);
QDomElement docElem =
doc.documentElement();
n=
(sens==Chronological)?docElem.firstChild() : docElem.lastChild();
//i can't drop the root element
workaround.append(docElem);
}
while(!n.isNull())
{
QDomElement msgElem2 = n.toElement();
if( !msgElem2.isNull() &&
msgElem2.tagName()=="msg")
{
rxTime.indexIn(msgElem2.attribute("time"));
QDate
d=QDate::currentDate().addMonths(0-m_currentMonth);
QDateTime dt( QDate(d.year() ,
d.month() ,
rxTime.cap(1).toUInt()), QTime( 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=contact;
}
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
{
QDomDocument
doc=getDocument(currentContact,m_currentMonth);
QDomElement docElem = doc.documentElement();
QDomNode n=
(sens==Chronological)?docElem.firstChild() :
docElem.lastChild();
msgElem=QDomElement();
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(QRegExp(m_filter,m_filterCaseSensitive)) :
msgElem.text().contains(m_filter,m_filterCaseSensitive) ))
{
Q_ASSERT(currentContact);
QString f=msgElem.attribute("from" );
const Kopete::Contact *from=f.isNull()
? 0L :
currentContact->account()->contacts().value(f);
if( !from )
from = (dir ==
Kopete::Message::Inbound) ? currentContact :
currentContact->account()->myself();
Kopete::ContactPtrList to;
to.append(
dir==Kopete::Message::Inbound ?
currentContact->account()->myself() :
const_cast<Kopete::Contact*>(currentContact) );
if(!timestamp.isValid())
{
//parse timestamp only if it
was not already parsed
rxTime.indexIn(msgElem.attribute("time"));
QDate
d=QDate::currentDate().addMonths(0-m_currentMonth);
timestamp=QDateTime(
QDate(d.year() , d.month() ,
rxTime.cap(1).toUInt()), QTime( rxTime.cap(2).toUInt() ,
rxTime.cap(3).toUInt() , rxTime.cap(5).toUInt() ) );
}
Kopete::Message msg(from, to);
msg.setTimestamp( timestamp );
msg.setDirection( dir );
msg.setPlainBody( msgElem.text() );
if (colorize)
{
msg.setHtmlBody(
QString::fromLatin1("<span style=\"color:%1\"
title=\"%2\">%3</span>")
.arg( fgColor.name(),
timestamp.toString(Qt::LocalDate),
msg.escapedBody() ));
msg.setForegroundColor( fgColor
);
msg.addClass( "history" );
}
else
{
msg.setHtmlBody(
QString::fromLatin1("<span title=\"%1\">%2</span>")
.arg(
timestamp.toString(Qt::LocalDate), msg.escapedBody() ));
}
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
QDomNode node = ( (sens==Chronological) ?
msgElem.nextSibling() :
msgElem.previousSibling() );
msgElem = QDomElement(); //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
QRegExp rx("(\\d+)
(\\d+):(\\d+):(\\d+)");
rx.indexIn(msgElem.attribute("time"));
QDate d =
QDate::currentDate().addMonths(0-m_currentMonth);
timestamp = QDateTime(
QDate(d.year(),
d.month(), rx.cap(1).toUInt()),
QTime(
rx.cap(2).toUInt(), rx.cap(3).toUInt() ) );
}
else
timestamp =
QDateTime(); //invalid
}
else
msgElem = QDomElement();
}
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;
}
After spending a lot of time, in understanding this function,
The algorithm i have understood here is that,
The function has to return a number of lines of chat of a particular
contact.
IF the contact is given then just read the history and return it(easy part)
if the contact is not given, and the metacontact has more than one contact.
merge the history(confusing here),
How merging works is. if there are more than one contact, Find out the
Contact which has the chat with smallest/largest date (depends on the
chronological order). Note the date. Then find out another contact Which has
a message with date just below or above the first chosen contact.
Now return the list of chat messages that lie between these two dates..
Have i understood the logic correctly or have i missed something here ?
--
Please do make some comments on this, they are always helpful. :)
Greetings,
[email protected]
roideuniverse.blogspot.com
_______________________________________________
kopete-devel mailing list
[email protected]
https://mail.kde.org/mailman/listinfo/kopete-devel