Logo Search packages:      
Sourcecode: mailody version File versions  Download package

messageview.cpp

/*
   Copyright (C) 2006-2007 KovoKs <info@kovoks.nl>
   Copyright (C) 2007 Frode M. Døving <frode@lnix.net>

   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.

   This program 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
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include <kdebug.h>
#include <kapplication.h>
#include <kaboutdata.h>
#include <ktabwidget.h>
#include <kactivelabel.h>
#include <kabc/stdaddressbook.h>
#include <kabc/addresseelist.h>
#include <kfiledialog.h>
#include <kurllabel.h>
#include <kio/netaccess.h>
#include <krun.h>
#include <kiconloader.h>
#include <kprinter.h>
#include <kglobalsettings.h>
#include <kmessagebox.h>
#include <khtmlview.h>
#include <dom/html_element.h>

#include <qfontmetrics.h>
#include <qsimplerichtext.h>
#include <qlabel.h>
#include <qclipboard.h>
#include <qimage.h>
#include <qpopupmenu.h>
#include <qcolor.h>
#include <qcursor.h>
#include <qlayout.h>
#include <qvbox.h>
#include <qpainter.h>
#include <qpaintdevicemetrics.h>

#include "global.h"
#include "messagedata.h"
#include "addresslabel.h"
#include "addressbook.h"
#include "linklocator.h"
#include "messageview.h"

namespace Mailody {

00060 TotalView::TotalView( QWidget* parent)
    : QWidget(parent),
    m_msg(0)
{
    QGridLayout* layout = new QGridLayout( this, 1, 1, 0, 0);

    m_headers = new HeaderView(this);
    layout->addWidget( m_headers, 0, 0 );

    m_body    = new MessageView(this);
    layout->addWidget( m_body->widget(), 1, 0 );

    connect(m_body, SIGNAL(attachment(const QString&, const QString&)),
            m_headers, SLOT(addattachment(const QString&, const QString&)));
    connect(m_body, SIGNAL(update()), m_headers, SLOT(updateHeaders()));
}

00077 TotalView::~TotalView()
{
}

00081 void TotalView::setMsg(MessageData* msg)
{
    msg->setBlocked(true);   // prevent deletion
    if (m_msg && m_msg != msg)
        m_msg->setBlocked(false);   // set previous message free

    m_msg = msg;
    if (m_msg)
    {
        m_headers->setMsg( msg );
        m_body->setMsg( msg );
    }

}

00096 void TotalView::clearView()
{
    m_headers->clearView();
    m_body->clearView();
}

00102 void TotalView::save( const QString& filename)
{
    QFile file( filename );
    QTextStream stream( &file );
    stream.setEncoding( QTextStream::UnicodeUTF8 );
    file.open( IO_WriteOnly );
    stream << m_msg->raw().stripWhiteSpace();
    file.close();
}

00112 void TotalView::print()
{
    QSimpleRichText header (i18n("Printed by Mailody v%1 at %2")
            .arg(kapp->aboutData()->version(),
                 KGlobal::locale()->formatDateTime(
                         QDateTime::currentDateTime())),
            QFont("times",8));

    QString msgheadersdata;
    msgheadersdata += "<table border=1 cellspacing=0 "
                      "cellpadding=0 width=100%>";
    msgheadersdata += "<tr><td colspan=2 bgcolor=\"#dddddd\"><b><h2>"
                      + msg()->subject() + "<h2></b></td>";
    msgheadersdata += "</tr>";
    msgheadersdata += "<tr><td align=right><b>" + i18n("From:") + "</b></td>";
    msgheadersdata += "    <td width=100%>" + msg()->sender_full(true)
                      + "</td>";
    msgheadersdata += "</tr>";
    msgheadersdata += "<tr><td align=right><b>" + i18n("To:") + "</b></td>";
    msgheadersdata += "    <td>" + msg()->to() + "</td>";
    msgheadersdata += "</tr>";
    if (!msg()->cc().isEmpty())
    {
        msgheadersdata += "<tr><td align=right><b>" + i18n("Cc:")
                          + "</b></td>";
        msgheadersdata += "    <td>" + msg()->cc() + "</td>";
        msgheadersdata += "</tr>";
    }
    msgheadersdata += "<tr><td align=right><b>" + i18n("Date:") + "</b></td>";
    msgheadersdata += "    <td>" + msg()->vDate() + "</td>";
    msgheadersdata += "</tr></table>";
    QSimpleRichText msgheaders(msgheadersdata,  QFont("times",9));

    KHTMLPart* newPart = new KHTMLPart(this);
    newPart->setJScriptEnabled(false);
    newPart->setJavaEnabled(false);
    newPart->setMetaRefreshEnabled(false);
    newPart->setPluginsEnabled(false);
    newPart->setOnlyLocalReferences(!m_body->externalImage());
    m_body->fixedfont()
            ? newPart->setStandardFont(KGlobalSettings::fixedFont().family())
            : newPart->setStandardFont( KGlobalSettings::generalFont().family());

    newPart->begin();
    newPart->write(msgheadersdata);
    newPart->write("<br><br>");
    newPart->write(msg()->body());
    newPart->end();
    newPart->view()->print();
    delete newPart;
}

// --------------------- Headers -------------------------------

00166 HeaderView::HeaderView( QWidget* parent)
    : QScrollView( parent ),
    m_currentMessage(0)
{
    hide();
    QHBox* big_box = new QHBox(viewport());
    addChild(big_box);

    //setFrameStyle(QFrame::Sunken);
    setLineWidth(0);
    setHScrollBarMode(QScrollView::AlwaysOff);
    m_label = new AddressLabel(big_box);
    m_foto = new KActiveLabel(big_box);

    connect(m_label, SIGNAL(leftClicked(const QString&)),
                SLOT(slotLeftMouseClick(const QString&)));
    connect(m_label, SIGNAL(rightClicked(const QString&)),
                SLOT(slotRightMouseClick(const QString&)));

}

00187 HeaderView::~HeaderView()
{
}

00191 void HeaderView::setMsg(MessageData* msg)
{
    if (m_currentMessage != msg)
    {
        aMap.clear();
        m_currentMessage = msg;
        setContentsPos(0,0);

        if (msg && isHidden())
            show(); // can be hidden by favorite website / middle click.
    }
}

void HeaderView::addattachment(const QString& filename, const QString& file)
{
    aMap[filename.mid(4)] = file;
}

void HeaderView::updateHeaders()
{
    if (!m_currentMessage)
        return;

       // copied from KMail...
    QString photoURL;
    int photoWidth = 60;
    int photoHeight = 60;
    bool hasPhoto=false;

    KABC::AddresseeList addresses =
        Addressbook::instance()->findByEmail(m_currentMessage->sender_email());
    if (addresses.count()==1)
    {

        // Next part is an example of how the on hover show more info.
        // as it needs to do the same for all addresses in the headers
        // I need to think about this more (it will require an address
        // book search for every address for example. Remove next line when
        // and uncomment to see it.
        /*

        QString tip;
        PhoneNumber::List list = addresses[0].phoneNumbers();
        QValueList<PhoneNumber>::Iterator it=list.begin();
        for(; it!=list.end(); ++it)
        {
            tip.append((*it).label() + ": " + (*it).number() );
            tip.append("\n");
            kdDebug() << tip << endl;
        }

        if (!tip.isEmpty())
        {
            m_sender_data->setUseTips(true);
            m_sender_data->setTipText(tip);
        }
        else
            m_sender_data->setUseTips(false);

        */

        if (addresses[0].photo().isIntern())
        {
            QImage photo = addresses[0].photo().data();
            if ( !photo.isNull() )
            {
                photoWidth = photo.width();
                photoHeight = photo.height();
                // scale below 60, otherwise it can get way too large
                if ( photoHeight > 60 ) {
                    double ratio = ( double )photoHeight / ( double )photoWidth;
                    photoHeight = 60;
                    photoWidth = (int)( 60 / ratio );
                    photo = photo.smoothScale( photoWidth, photoHeight );
                }

                QMimeSourceFactory::defaultFactory()->
                        setImage( "UID"+addresses[0].uid(), photo );

                m_foto->setText(
                        QString("<img src=\"%1\" width=\"%2\" height=\"%3\">")
                                .arg( "UID"+addresses[0].uid() )
                                .arg( photoWidth )
                                .arg( photoHeight ));
                hasPhoto = true;
            }
        }
    }
    else
        m_foto->setText(QString::null);
    // --- end

    //------------- the rest
    using namespace LinkLocatorNS;
    const int flags = LinkLocator::PreserveSpaces |
                    LinkLocator::LinkLocator::HighlightText |
                    LinkLocator::LinkLocator::UseParagraph |
                    LinkLocator::LinkLocator::IgnoreUrls;

    QString text = "<table style=\"margin-right: 20px;\" ";
    text.append("cellpadding=0 cellspacing=0 width=\"100%\">");
    text.append("<tr><td colspan=2 width=\"100%\">"
            + LinkLocator::convertToHtml(m_currentMessage->subject()
                                .stripWhiteSpace(), flags)
            + "</td></tr>");

    QString t = "<a href=\"email:"
            + LinkLocator::convertToHtml(m_currentMessage->sender_full(false),
                                         flags) + "\">"
            + LinkLocator::convertToHtml(m_currentMessage->sender_full(true)
                                .stripWhiteSpace(), flags) + "</a>";
    text.append("<tr><td valign=top align=right>" + i18n("From") + ": </td>");
    if (m_currentMessage->userAgent().startsWith("Mailody"))
        text.append("<td valign=top width=100%><font color=\"#22cc22\">"
                + t + "<td></tr>\n");
    else
        text.append("<td valign=top width=100%>" + t + "</td></tr>");

    QStringList* to = m_currentMessage->to_list();
    QString toList;
    if ((*to).count()>0)
    {
        QStringList::Iterator it=to->begin();
        for(; it!=to->end(); ++it)
        {
            if (!toList.isEmpty())
                toList.append(", ");
                toList.append("<a href=\"email:"
                    + LinkLocator::convertToHtml((*it).stripWhiteSpace(), flags)
                    + "\">"
                    + LinkLocator::convertToHtml((*it).stripWhiteSpace(), flags)
                    + "</a>");
        }
        text.append("<tr><td align=right valign=top>" + i18n("To") + ": </td>");
        text.append("<td valign=top>" + toList + "</td></tr>");
    }

    QStringList* cc = m_currentMessage->cc_list();
    if ((*cc).count()>0)
    {
        QString t;
        QStringList::Iterator it=cc->begin();
        for(; it!=cc->end(); ++it)
        {
            if (!t.isEmpty())
                t.append(", ");
            t.append("<a href=\"email:"
                    + LinkLocator::convertToHtml((*it).stripWhiteSpace(), flags)
                    + "\">"
                    + LinkLocator::convertToHtml((*it).stripWhiteSpace(), flags)
                    + "</a>");

        }
        text.append("<tr><td valign=top align=right>"
                + i18n("Cc")+": </td>");
        text.append("<td valign=top>" + t + "</td></tr>");
    }

    QString resentFrom = m_currentMessage->resentFrom().stripWhiteSpace();
    if (!resentFrom.isEmpty())
    {
        QString t = "<a href=\"email:"
                + LinkLocator::convertToHtml(resentFrom, flags) +"\">"
                + LinkLocator::convertToHtml(resentFrom, flags) + "</a>";

        QString t2;
        QString resentTo = m_currentMessage->resentTo().stripWhiteSpace();
        if (resentTo.isEmpty())
            t2 = toList;
        else
            t2 = "<a href=\"email:"
                + LinkLocator::convertToHtml(resentTo, flags) +"\">"
                + LinkLocator::convertToHtml(resentTo, flags) + "</a>";

        text.append("<tr><td valign=top align=right><nobr>" +
                i18n("Resent")+": </nobr></td>");
        text.append("<td valign=top>"
                + i18n("resent by from-address(1) to to-address(2)",
                       "by %1 to %2").arg(t,t2) + "</td></tr>");
    }

    if (aMap.count() > 0)
    {
        QString t;
        QMap<QString,QString>::Iterator ita;
        for (ita = aMap.begin(); ita != aMap.end(); ++ita)
        {
            if (!t.isEmpty())
                t.append(", ");
            t.append("<a href=\"attachment:"+ita.key()+"\">" +
                    QStyleSheet::escape(ita.key().stripWhiteSpace()) + "</a>");

        }
        text.append("<tr><td align=right valign=top>"
                + i18n("Attachments")+": </td>");
        text.append("<td valign=top align=left>" + t + "</td></tr>");
    }

    text.append("</table>");

    // Set the complete text.
    m_label->setText(text);
    if (!hasPhoto)
        m_label->setDefaultWidth(viewport()->width()-5);
    else
        m_label->setDefaultWidth(viewport()->width()-60);

    // find out the height
    m_label->adjustSize();    // this call is crucial to get the correct height
    int h = m_label->height()+1;
    if (h > 100)
        h = 100;
    setFixedHeight(h);
}

00406 void HeaderView::clearView()
{
    m_label->setText("<table width=\"100%\" height=\"100%\">"
            "<tr height=\"100%\"><td width=\"100%\">&nbsp;</td></tr></table>");
    m_foto->setText(QString::null);
    hide();
}

void HeaderView::slotLeftMouseClick(const QString& to)
{
    if (!m_currentMessage)
        return;

    if (to.startsWith("email:"))
    {
        QString box;
        if (m_currentMessage)
            box = m_currentMessage->mb();
        else
            box = "INBOX";

        emit openComposer( to.mid(6), QString::null, box );
    }
    else if (to.startsWith("attachment:"))
    {
        QString attachment = to.mid(11);
        QString save = KFileDialog::getSaveFileName(attachment);
        if (save.isEmpty())
            return;
        KIO::NetAccess::copy( aMap[attachment], save, this );
    }
}

void HeaderView::slotRightMouseClick(const QString& to)
{
    if (to.startsWith("email:"))
    {
        QString address = to.mid(6);

        QPopupMenu* p = new QPopupMenu(this);
        int newmsg = p->insertItem(KGlobal::iconLoader()->loadIcon(
                    "email",KIcon::Small),
                    i18n("New Message to") + "...");
        int add = p->insertItem(KGlobal::iconLoader()->loadIcon(
                    "kaddressbook",KIcon::Small),
                    i18n("Add to KDE Addressbook") + "...");
        p->insertSeparator();
        int copy = p->insertItem(KGlobal::iconLoader()->loadIcon(
                    "editcopy",KIcon::Small), i18n("Copy"));
        int choice = p->exec(QCursor::pos());

        if (choice == add)
            Addressbook::instance()->add(address);
        else if (choice == newmsg)
            slotLeftMouseClick(to);
        else if (choice == copy)
        {
            QClipboard *cb = QApplication::clipboard();
            cb->setText(address, QClipboard::Clipboard);
        }
    }
    else if (to.startsWith("attachment:"))
    {
        QString attachment = to.mid(11);

        QPopupMenu* p = new QPopupMenu(this);
        int open = p->insertItem(i18n("Open"));
        int openwith = p->insertItem(i18n("Open with..."));
        int save = p->insertItem(i18n("Save..."));
        int saveAll = p->insertItem(i18n("Save all..."));
        int choice = p->exec(QCursor::pos());

        KURL url = KURL(aMap[attachment]);
        if (choice == open)
            new KRun( url, this );
        else if (choice == openwith)
            KRun::displayOpenWithDialog(KURL::List::List(url));
        else if (choice == save)
            slotLeftMouseClick(to);
        else if (choice == saveAll)
        {
            QString save = KFileDialog::getExistingDirectory(
                    ":saveAttachmentsFolder", this);
            if (save.isEmpty())
                return;

            QMap<QString,QString>::Iterator ita;
            for (ita = aMap.begin(); ita != aMap.end(); ++ita)
            {
                QString filename = save+"/"+ita.key();

                // now we see if a file with that name already exists.
                KURL dst;
                dst.setPath(filename);
                bool exists = KIO::NetAccess::exists(dst, false, this);
                int i = 0;
                while (exists)
                {
                    // it exists, so find the extention, add a digit and try
                    // again.
                    int extAt = ita.key().findRev('.');
                    if (extAt == -1)
                        extAt = ita.key().length();

                    filename = save + '/' + ita.key().left(extAt)
                            + '_' + QString::number(i) + ita.key().mid(extAt);

                    dst.setPath(filename);
                    exists = KIO::NetAccess::exists(dst, false, this);
                    i++;
                }
                kdDebug() << "copy: " << ita.data() << "->" << filename
                        << endl;

                KIO::NetAccess::copy( ita.data(), filename, this );
            }

        }
    }
}

// --------------------- Message -------------------------------

00529 MessageView::MessageView( QWidget* parent )
    : KHTMLPart( parent ),
    m_currentMessage(0),
    m_bodyType(MessageData::Plain),
    m_fixedFont(false),
    m_externalImage(false),
    m_openDirectly(false)
{
    setOnlyLocalReferences(true);
    setJScriptEnabled(false);
    setJavaEnabled(false);
    setMetaRefreshEnabled(false);
    setPluginsEnabled(false);

    kapp->config()->setGroup("Messageview");
    setZoomFactor(kapp->config()->readNumEntry("zoomlevel", 80));


    connect(this, SIGNAL(popupMenu(const QString&, const QPoint&)),
            SLOT(slotPopupMenu(const QString&, const QPoint&)));
    connect(this, SIGNAL(setWindowCaption (const QString&)),
            SLOT(slotSetCaption (const QString&)));
}

00553 MessageView::~MessageView()
{
    kapp->config()->setGroup("Messageview");
    kapp->config()->writeEntry("zoomlevel", zoomFactor());
    kapp->config()->sync();
}

void MessageView::slotSetCaption(const QString& title)
{
    emit pageTitle(title);
}

00565 void MessageView::setMsg(MessageData* msg)
{
    if (msg != m_currentMessage)
    {
        // disconnect to prevent multiple connections!
        if (m_currentMessage)
        {
            disconnect(m_currentMessage, SIGNAL(messageData(const MessageData*)),
                this, SLOT(slotShowMsg(const MessageData*)));
            disconnect(m_currentMessage, SIGNAL(addContent(const MessageData*,
                                            const QString&, const QString&)),
                this, SLOT(slotAddContent(const MessageData*, const QString&,
                                          const QString&)));
        }

        m_currentMessage = msg;

        connect(m_currentMessage, SIGNAL(messageData(const MessageData*)),
                this, SLOT(slotShowMsg(const MessageData*)));
        connect(m_currentMessage, SIGNAL(addContent(const MessageData*,
                                            const QString&, const QString&)),
                this, SLOT(slotAddContent(const MessageData*, const QString&,
                                          const QString&)));

        m_currentMessage->requestBody( m_bodyType );
        m_openDirectly = false;
    }
}


void MessageView::slotShowMsg(const MessageData* msg)
{
    if (msg != m_currentMessage)
    {
        kdDebug() << "Message is gone...." << endl;
        return;
    }

    emit update();
    static_cast<QScrollView *>(widget())->setContentsPos(0,0);

    begin();
    write( m_currentMessage->body().isEmpty() ? "&nbsp;" :
            m_currentMessage->body());
    end();
}

00612 void MessageView::setHTML( bool i )
{
    i ? m_bodyType = MessageData::HTML : m_bodyType = MessageData::Plain;
    if (m_currentMessage)
            m_currentMessage->requestBody( m_bodyType );
}


00620 void MessageView::setViewSource()
{
    m_bodyType = MessageData::Source;

    if (m_currentMessage)
        m_currentMessage->requestBody( m_bodyType );
}

00628 void MessageView::setFixedFont( bool i )
{
    m_fixedFont = i;
    i ? setStandardFont(KGlobalSettings::fixedFont().family())
        : setStandardFont( KGlobalSettings::generalFont().family() );

    // there is no update() call afaik
    if (m_currentMessage)
    {
        begin();
        write(m_currentMessage->body());
        end();
    }
}

00643 void MessageView::setExternalImage( bool i, bool screenupdate )
{
    m_externalImage = i;
    setOnlyLocalReferences(!i);

    // there is no update() call afaik
    if (m_currentMessage && screenupdate)
    {
        begin();
        write(m_currentMessage->body());
        end();
    }
}

00657 void MessageView::setOpenLinksDirectly( bool i )
{
    m_openDirectly = i;
}

void MessageView::slotAddContent(const MessageData* msg,
                                    const QString& filename,
                                    const QString& file)
{
    if (msg != m_currentMessage)
    {
        kdDebug() << "Message is gone...." << endl;
        return;
    }
    QMimeSourceFactory::defaultFactory()->setImage( filename, QImage(file) );
    emit attachment(filename,file);
}

void MessageView::urlSelected (const QString &url, int button, int,
                               const QString&, KParts::URLArgs)
{
    // if the text of the link contains http:// or www., match that against the
    // actual url. If that does not contain it, we might have a case of
    // phishing. I know it's not waterproof, but it's a start.
    QString linkText = DOM::HTMLElement(nodeUnderMouse()).innerText().string();
    QString urlHost  = KURL(url).host();
    if ((linkText.find("http") > -1 || linkText.find("www") > -1) &&
         linkText.find(urlHost) == -1)
    {
        kdDebug() << "PHISHING alert" << endl;
        int i = KMessageBox::warningYesNo( 0,
               i18n("You have clicked on an link which might not indicate "
                       "correctly where you are really going to. Please check "
                       "if you really want to a page on this server:\n\n"
                       "%1\n\nDo you want to go there?").arg(urlHost),
                i18n("Phishing alert"));
        if (i == KMessageBox::No)
            return;
    }

    if (url.startsWith("mailto:"))
    {
        QString box;
        if (m_currentMessage)
            box = m_currentMessage->mb();
        else
            box = "INBOX";

        emit openComposer(url.mid(7), QString::null, box);
    }
    else
    {
        if (button == MidButton)
            emit openInTab( url );
        else if (m_openDirectly)
            openURL( url );
        else
            kapp->invokeBrowser( url );
    }
}

void MessageView::slotPopupMenu(const QString& url, const QPoint& point)
{
    QPopupMenu* p = new QPopupMenu(0);

    int add    = -2;
    int copy   = -2;
    int newmsg = -2;
    int newurl = -2;
    int newtab = -2;

    if (url.startsWith("mailto:"))
    {
        newmsg = p->insertItem(KGlobal::iconLoader()->loadIcon(
                "email",KIcon::Small),
                i18n("New Message to") + "...");
        add = p->insertItem(KGlobal::iconLoader()->loadIcon(
                "kaddressbook",KIcon::Small),
                i18n("Add to KDE Addressbook") + "...");
    }
    else if (!url.isNull())
    {
        newurl = p->insertItem(i18n("Open"));
        newtab = p->insertItem(i18n("Open in New Tab"));
    }

    p->insertSeparator();

    if ( hasSelection() || !url.isNull() )
    {
        copy = p->insertItem(KGlobal::iconLoader()->loadIcon(
                "editcopy",KIcon::Small), i18n("Copy"));
        p->insertSeparator();
    }
    int zoomin = p->insertItem(KGlobal::iconLoader()->loadIcon(
            "viewmag+",KIcon::Small),i18n("Zoom in"));
    int zoomout = p->insertItem(KGlobal::iconLoader()->loadIcon(
            "viewmag-",KIcon::Small),i18n("Zoom out"));
    int choice = p->exec(point);

    if (choice == copy)
    {
        QClipboard *cb = QApplication::clipboard();

        if (hasSelection())
        {
            cb->setText(selectedText(), QClipboard::Clipboard);
        }
        else if (url.startsWith("mailto:"))
            cb->setText(url.mid(7), QClipboard::Clipboard);
        else
            cb->setText(url, QClipboard::Clipboard);
    }
    else if (choice == newmsg)
    {
        QString box;
        if (m_currentMessage)
            box = m_currentMessage->mb();
        else
            box = "INBOX";
        emit openComposer(url.mid(7), QString::null, box);
    }
    else if (choice == newurl)
        kapp->invokeBrowser(url);
    else if (choice == add)
    {
        Addressbook::instance()->add(url.mid(7));
    }
    else if (choice == newtab)
        emit openInTab(url);
    else if (choice == zoomin)
        setZoomFactor(zoomFactor()+10);
    else if (choice == zoomout)
        setZoomFactor(zoomFactor()-10);

    delete p;
}

00795 void MessageView::clearView()
{
    begin();
    write(  "&nbsp;" );
    end();
}

}

#include "messageview.moc"

Generated by  Doxygen 1.6.0   Back to index