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, ro...@irc.freenode.net roideuniverse.blogspot.com
_______________________________________________ kopete-devel mailing list kopete-devel@kde.org https://mail.kde.org/mailman/listinfo/kopete-devel