diff --git a/messagelist/core/messageitem.cpp b/messagelist/core/messageitem.cpp index 52b7775050..73cf0dda69 100644 --- a/messagelist/core/messageitem.cpp +++ b/messagelist/core/messageitem.cpp @@ -1,754 +1,763 @@ /****************************************************************************** * * Copyright 2008 Szymon Tomasz Stefanek * * 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 "messageitem.h" #include "messageitem_p.h" #include "theme.h" #include +#include #include #include #include #include #include using namespace MessageList::Core; K_GLOBAL_STATIC( TagCache, s_tagCache ) class MessageItem::Tag::Private { public: Private() :mPriority( 0 ) //Initialize it { - } + QPixmap mPixmap; QString mName; QString mId; ///< The unique id of this tag QColor mTextColor; QColor mBackgroundColor; QFont mFont; int mPriority; }; MessageItem::Tag::Tag( const QPixmap &pix, const QString &tagName, const QString &tagId ) : d( new Private ) { d->mPixmap = pix; d->mName = tagName; d->mId = tagId; } MessageItem::Tag::~Tag() { delete d; } QPixmap MessageItem::Tag::pixmap() const { return d->mPixmap; } QString MessageItem::Tag::name() const { return d->mName; } QString MessageItem::Tag::id() const { return d->mId; } QColor MessageItem::Tag::textColor() const { return d->mTextColor; } QColor MessageItem::Tag::backgroundColor() const { return d->mBackgroundColor; } QFont MessageItem::Tag::font() const { return d->mFont; } int MessageItem::Tag::priority() const { return d->mPriority; } void MessageItem::Tag::setTextColor( const QColor &textColor ) { d->mTextColor = textColor; } void MessageItem::Tag::setBackgroundColor( const QColor &backgroundColor ) { d->mBackgroundColor = backgroundColor; } void MessageItem::Tag::setFont( const QFont &font ) { d->mFont = font; } void MessageItem::Tag::setPriority( int priority ) { d->mPriority = priority; } class MessageItemPrivateSettings { public: QColor mColorUnreadMessage; QColor mColorImportantMessage; QColor mColorToDoMessage; QFont mFont; QFont mFontUnreadMessage; QFont mFontImportantMessage; QFont mFontToDoMessage; QString mFontKey; QString mFontUnreadMessageKey; QString mFontImportantMessageKey; QString mFontToDoMessageKey; }; K_GLOBAL_STATIC(MessageItemPrivateSettings, s_settings) MessageItemPrivate::MessageItemPrivate( MessageItem* qq ) : ItemPrivate( qq ), mThreadingStatus( MessageItem::ParentMissing ), mEncryptionState( MessageItem::NotEncrypted ), mSignatureState( MessageItem::NotSigned ), mAboutToBeRemoved( false ), mSubjectIsPrefixed( false ), mTagList( 0 ) { } MessageItemPrivate::~MessageItemPrivate() { s_tagCache->cancelRequest(this); invalidateTagCache(); } void MessageItemPrivate::invalidateTagCache() { if ( mTagList ) { qDeleteAll( *mTagList ); delete mTagList; mTagList = 0; } } void MessageItemPrivate::invalidateAnnotationCache() { //FIXME NOTES_ON_EMAIL } const MessageItem::Tag* MessageItemPrivate::bestTag() const { const MessageItem::Tag *best = 0; foreach( const MessageItem::Tag* tag, getTagList() ) { if ( !best || tag->priority() < best->priority() ) best = tag; } return best; } void MessageItemPrivate::fillTagList(const Akonadi::Tag::List &taglist) { Q_ASSERT( !mTagList ); mTagList = new QList; // TODO: The tag pointers here could be shared between all items, there really is no point in // creating them for each item that has tags //Priority sort this and make bestTag more efficient foreach( const Akonadi::Tag &tag, taglist ) { QString symbol = QLatin1String( "mail-tagged" ); Akonadi::TagAttribute *attr = tag.attribute(); if (attr) { if (!attr->iconName().isEmpty()) { symbol = attr->iconName(); } } MessageItem::Tag *messageListTag = new MessageItem::Tag( SmallIcon( symbol ), tag.name(), tag.url().url() ); if (attr) { messageListTag->setTextColor( attr->textColor() ); messageListTag->setBackgroundColor( attr->backgroundColor() ); if (!attr->font().isEmpty()) { QFont font; if (font.fromString( attr->font() )) { messageListTag->setFont( font ); } } if (attr->priority() != -1) { messageListTag->setPriority( attr->priority() ); } else { messageListTag->setPriority( 0xFFFF ); } } mTagList->append( messageListTag ); } } QList MessageItemPrivate::getTagList() const { if ( !mTagList ) { s_tagCache->retrieveTags(mAkonadiItem.tags(), const_cast(this)); return QList(); } return *mTagList; } bool MessageItemPrivate::tagListInitialized() const { return mTagList != 0; } MessageItem::MessageItem() : Item( Message, new MessageItemPrivate( this ) ), ModelInvariantIndex() { } MessageItem::MessageItem ( MessageItemPrivate* dd ) : Item ( Message, dd ), ModelInvariantIndex() { } MessageItem::~MessageItem() { } QList< MessageItem::Tag * > MessageItem::tagList() const { Q_D( const MessageItem ); return d->getTagList(); } bool MessageItem::hasAnnotation() const { Q_D( const MessageItem ); //FIXME NOTES_ON_EMAIL - return false; + return !d->mAkonadiItem.relations().isEmpty(); } -QString MessageItem::annotation() const +Akonadi::Item MessageItem::annotation() const { Q_D( const MessageItem ); //FIXME NOTES_ON_EMAIL - return QString(); -} + if ( hasAnnotation() ) { + Akonadi::Relation relation; + foreach( const Akonadi::Relation &r, d->mAkonadiItem.relations() ) { + if ( r.type() == Akonadi::Relation::GENERIC ) { + relation = r; + break; + } + } -void MessageItem::editAnnotation() -{ - Q_D( MessageItem ); - //FIXME: NOTES_ON_EMAIL + if ( relation.isValid() ) { + return relation.right(); + } + } + + return Akonadi::Item(); } const MessageItem::Tag * MessageItemPrivate::findTagInternal( const QString &szTagId ) const { foreach( const MessageItem::Tag *tag, getTagList() ) { if ( tag->id() == szTagId ) return tag; } return 0; } const MessageItem::Tag *MessageItem::findTag( const QString &szTagId ) const { Q_D( const MessageItem ); return d->findTagInternal( szTagId ); } QString MessageItem::tagListDescription() const { QString ret; foreach( const Tag *tag, tagList() ) { if ( !ret.isEmpty() ) ret += QLatin1String( ", " ); ret += tag->name(); } return ret; } void MessageItem::invalidateTagCache() { Q_D( MessageItem ); d->invalidateTagCache(); } void MessageItem::invalidateAnnotationCache() { Q_D( MessageItem ); //FIXME: NOTES_ON_EMAIL d->invalidateAnnotationCache(); } QColor MessageItem::textColor() const { Q_D( const MessageItem ); const Tag *bestTag = d->bestTag(); if ( bestTag != 0 && bestTag->textColor().isValid() ) { return bestTag->textColor(); } QColor clr; Akonadi::MessageStatus messageStatus = status(); if ( !messageStatus.isRead() ) { clr = s_settings->mColorUnreadMessage; } else if ( messageStatus.isImportant() ) { clr = s_settings->mColorImportantMessage; } else if ( messageStatus.isToAct() ) { clr = s_settings->mColorToDoMessage; } return clr; } QColor MessageItem::backgroundColor() const { Q_D( const MessageItem ); const Tag *bestTag = d->bestTag(); if ( bestTag ) { return bestTag->backgroundColor(); } else { return QColor(); } } QFont MessageItem::font() const { Q_D( const MessageItem ); // for performance reasons we don't want font retrieval to trigger // full tags loading, as the font is used for geometry calculation // and thus this method called for each item if ( d->tagListInitialized() ) { const Tag *bestTag = d->bestTag(); if ( bestTag && bestTag->font() != QFont() ) { return bestTag->font(); } } QFont font; // from KDE3: "important" overrides "new" overrides "unread" overrides "todo" Akonadi::MessageStatus messageStatus = status(); if ( messageStatus.isImportant() ) { font = s_settings->mFontImportantMessage; } else if ( !messageStatus.isRead() ) { font = s_settings->mFontUnreadMessage; } else if ( messageStatus.isToAct() ) { font = s_settings->mFontToDoMessage; } else { font = s_settings->mFont; } return font; } QString MessageItem::fontKey() const { Q_D( const MessageItem ); // for performance reasons we don't want font retrieval to trigger // full tags loading, as the font is used for geometry calculation // and thus this method called for each item if ( d->tagListInitialized() ) { const Tag *bestTag = d->bestTag(); if ( bestTag && bestTag->font() != QFont() ) { return bestTag->font().key(); } } // from KDE3: "important" overrides "new" overrides "unread" overrides "todo" Akonadi::MessageStatus messageStatus = status(); if ( messageStatus.isImportant() ) { return s_settings->mFontImportantMessageKey; } else if ( !messageStatus.isRead() ) { return s_settings->mFontUnreadMessageKey; } else if ( messageStatus.isToAct() ) { return s_settings->mFontToDoMessageKey; } else { return s_settings->mFontKey; } } MessageItem::SignatureState MessageItem::signatureState() const { Q_D( const MessageItem ); return d->mSignatureState; } void MessageItem::setSignatureState( SignatureState state ) { Q_D( MessageItem ); d->mSignatureState = state; } MessageItem::EncryptionState MessageItem::encryptionState() const { Q_D( const MessageItem ); return d->mEncryptionState; } void MessageItem::setEncryptionState( EncryptionState state ) { Q_D( MessageItem ); d->mEncryptionState = state; } QByteArray MessageItem::messageIdMD5() const { Q_D( const MessageItem ); return d->mMessageIdMD5; } void MessageItem::setMessageIdMD5( const QByteArray &md5 ) { Q_D( MessageItem ); d->mMessageIdMD5 = md5; } QByteArray MessageItem::inReplyToIdMD5() const { Q_D( const MessageItem ); return d->mInReplyToIdMD5; } void MessageItem::setInReplyToIdMD5( const QByteArray& md5 ) { Q_D( MessageItem ); d->mInReplyToIdMD5 = md5; } QByteArray MessageItem::referencesIdMD5() const { Q_D( const MessageItem ); return d->mReferencesIdMD5; } void MessageItem::setReferencesIdMD5( const QByteArray& md5 ) { Q_D( MessageItem ); d->mReferencesIdMD5 = md5; } void MessageItem::setSubjectIsPrefixed( bool subjectIsPrefixed ) { Q_D( MessageItem ); d->mSubjectIsPrefixed = subjectIsPrefixed; } bool MessageItem::subjectIsPrefixed() const { Q_D( const MessageItem ); return d->mSubjectIsPrefixed; } QByteArray MessageItem::strippedSubjectMD5() const { Q_D( const MessageItem ); return d->mStrippedSubjectMD5; } void MessageItem::setStrippedSubjectMD5( const QByteArray& md5 ) { Q_D( MessageItem ); d->mStrippedSubjectMD5 = md5; } bool MessageItem::aboutToBeRemoved() const { Q_D( const MessageItem ); return d->mAboutToBeRemoved; } void MessageItem::setAboutToBeRemoved( bool aboutToBeRemoved ) { Q_D( MessageItem ); d->mAboutToBeRemoved = aboutToBeRemoved; } MessageItem::ThreadingStatus MessageItem::threadingStatus() const { Q_D( const MessageItem ); return d->mThreadingStatus; } void MessageItem::setThreadingStatus( ThreadingStatus threadingStatus ) { Q_D( MessageItem ); d->mThreadingStatus = threadingStatus; } unsigned long MessageItem::uniqueId() const { Q_D( const MessageItem ); return d->mAkonadiItem.id(); } Akonadi::Item MessageList::Core::MessageItem::akonadiItem() const { Q_D( const MessageItem ); return d->mAkonadiItem; } void MessageList::Core::MessageItem::setAkonadiItem(const Akonadi::Item& item) { Q_D( MessageItem ); d->mAkonadiItem = item; } MessageItem * MessageItem::topmostMessage() { if ( !parent() ) return this; if ( parent()->type() == Item::Message ) return static_cast< MessageItem * >( parent() )->topmostMessage(); return this; } QString MessageItem::accessibleTextForField(Theme::ContentItem::Type field) { switch (field) { case Theme::ContentItem::Subject: return d_ptr->mSubject; case Theme::ContentItem::Sender: return d_ptr->mSender; case Theme::ContentItem::Receiver: return d_ptr->mReceiver; case Theme::ContentItem::SenderOrReceiver: return senderOrReceiver(); case Theme::ContentItem::Date: return formattedDate(); case Theme::ContentItem::Size: return formattedSize(); case Theme::ContentItem::RepliedStateIcon: return status().isReplied() ? i18nc( "Status of an item", "Replied" ) : QString(); case Theme::ContentItem::ReadStateIcon: return status().isRead() ? i18nc( "Status of an item", "Read" ) : i18nc( "Status of an item", "Unread" ); case Theme::ContentItem::CombinedReadRepliedStateIcon: return accessibleTextForField( Theme::ContentItem::ReadStateIcon ) + accessibleTextForField( Theme::ContentItem::RepliedStateIcon ); default: return QString(); } } QString MessageItem::accessibleText( const Theme* theme, int columnIndex ) { QStringList rowsTexts; Q_FOREACH( Theme::Row *row, theme->column(columnIndex)->messageRows() ) { QStringList leftStrings; QStringList rightStrings; Q_FOREACH( Theme::ContentItem *contentItem, row->leftItems() ) { leftStrings.append( accessibleTextForField( contentItem->type() ) ); } Q_FOREACH( Theme::ContentItem *contentItem, row->rightItems() ) { rightStrings.insert( rightStrings.begin(), accessibleTextForField( contentItem->type() ) ); } rowsTexts.append( ( leftStrings + rightStrings ).join( QLatin1String( " " ) ) ); } return rowsTexts.join( QLatin1String(" ") ); } void MessageItem::subTreeToList( QList< MessageItem * > &list ) { list.append( this ); QList< Item * > * childList = childItems(); if ( !childList ) return; QList< Item * >::ConstIterator end( childList->constEnd() ); for ( QList< Item * >::ConstIterator it = childList->constBegin(); it != end; ++it ) { Q_ASSERT( ( *it )->type() == Item::Message ); static_cast< MessageItem * >( *it )->subTreeToList( list ); } } void MessageItem::setUnreadMessageColor( const QColor &color ) { s_settings->mColorUnreadMessage = color; } void MessageItem::setImportantMessageColor( const QColor &color ) { s_settings->mColorImportantMessage = color; } void MessageItem::setToDoMessageColor( const QColor &color ) { s_settings->mColorToDoMessage = color; } void MessageItem::setGeneralFont( const QFont &font ) { s_settings->mFont = font; s_settings->mFontKey = font.key(); } void MessageItem::setUnreadMessageFont( const QFont &font ) { s_settings->mFontUnreadMessage = font; s_settings->mFontUnreadMessageKey = font.key(); } void MessageItem::setImportantMessageFont( const QFont &font ) { s_settings->mFontImportantMessage = font; s_settings->mFontImportantMessageKey = font.key(); } void MessageItem::setToDoMessageFont( const QFont &font ) { s_settings->mFontToDoMessage = font; s_settings->mFontToDoMessageKey = font.key(); } FakeItemPrivate::FakeItemPrivate( FakeItem *qq ) : MessageItemPrivate( qq ) { } FakeItem::FakeItem() : MessageItem( new FakeItemPrivate( this ) ) { } FakeItem::~FakeItem() { } QList< MessageItem::Tag * > FakeItem::tagList() const { Q_D( const FakeItem ); return d->mFakeTags; } void FakeItem::setFakeTags( const QList< MessageItem::Tag* > &tagList ) { Q_D( FakeItem ); d->mFakeTags = tagList; } bool FakeItem::hasAnnotation() const { return true; } TagCache::TagCache() :QObject(), mMonitor(new Akonadi::Monitor(this)) { mCache.setMaxCost(100); mMonitor->setTypeMonitored(Akonadi::Monitor::Tags); mMonitor->tagFetchScope().fetchAttribute(); connect(mMonitor, SIGNAL(tagAdded(Akonadi::Tag)), this, SLOT(onTagAdded(Akonadi::Tag))); connect(mMonitor, SIGNAL(tagRemoved(Akonadi::Tag)), this, SLOT(onTagRemoved(Akonadi::Tag))); connect(mMonitor, SIGNAL(tagChanged(Akonadi::Tag)), this, SLOT(onTagChanged(Akonadi::Tag))); } void TagCache::onTagAdded(const Akonadi::Tag &tag) { mCache.insert(tag.id(), new Akonadi::Tag(tag)); } void TagCache::onTagChanged(const Akonadi::Tag &tag) { mCache.remove(tag.id()); } void TagCache::onTagRemoved(const Akonadi::Tag &tag) { mCache.remove(tag.id()); } void TagCache::retrieveTags(const Akonadi::Tag::List &tags, MessageItemPrivate *m) { //Retrieval is in progress if (mRequests.key(m)) { return; } Akonadi::Tag::List toFetch; Akonadi::Tag::List available; Q_FOREACH( const Akonadi::Tag &tag, tags ) { if (mCache.contains(tag.id())) { available << *mCache.object(tag.id()); } else { toFetch << tag; } } //Because fillTagList expects to be called once we either fetch all or none if (!toFetch.isEmpty()) { Akonadi::TagFetchJob *tagFetchJob = new Akonadi::TagFetchJob(tags, this); tagFetchJob->fetchScope().fetchAttribute(); connect(tagFetchJob, SIGNAL(result(KJob*)), this, SLOT(onTagsFetched(KJob*))); mRequests.insert(tagFetchJob, m); } else { m->fillTagList(available); } } void TagCache::cancelRequest(MessageItemPrivate *m) { const QList keys = mRequests.keys(m); Q_FOREACH( KJob *job, keys ) { mRequests.remove(job); } } void TagCache::onTagsFetched(KJob *job) { if (job->error()) { kWarning() << "Failed to fetch tags: " << job->errorString(); return; } Akonadi::TagFetchJob *fetchJob = static_cast(job); Q_FOREACH( const Akonadi::Tag &tag, fetchJob->tags() ) { mCache.insert(tag.id(), new Akonadi::Tag(tag)); } MessageItemPrivate *m = mRequests.take(fetchJob); if (m) { m->fillTagList(fetchJob->tags()); } } diff --git a/messagelist/core/messageitem.h b/messagelist/core/messageitem.h index e3f96daf63..48b4161a1a 100644 --- a/messagelist/core/messageitem.h +++ b/messagelist/core/messageitem.h @@ -1,238 +1,235 @@ /****************************************************************************** * * Copyright 2008 Szymon Tomasz Stefanek * * 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. * *******************************************************************************/ #ifndef __MESSAGELIST_CORE_MESSAGEITEM_H__ #define __MESSAGELIST_CORE_MESSAGEITEM_H__ #include #include #include #include #include #include #include "theme.h" namespace Akonadi { class Item; } namespace MessageList { namespace Core { class MessageItemPrivate; class MESSAGELIST_EXPORT MessageItem : public Item, public ModelInvariantIndex { public: class MESSAGELIST_EXPORT Tag { public: explicit Tag( const QPixmap &pix, const QString &tagName, const QString &tagId ); ~Tag(); QPixmap pixmap() const; QString name() const; QString id() const; QColor textColor() const; QColor backgroundColor() const; QFont font() const; int priority() const; void setTextColor( const QColor &textColor ); void setBackgroundColor( const QColor &backgroundColor ); void setFont( const QFont &font ); void setPriority( int priority ); private: class Private; Private * const d; }; enum ThreadingStatus { PerfectParentFound, ///< this message found a perfect parent to attach to ImperfectParentFound, ///< this message found an imperfect parent to attach to (might be fixed later) ParentMissing, ///< this message might belong to a thread but its parent is actually missing NonThreadable ///< this message does not look as being threadable }; enum EncryptionState { NotEncrypted, PartiallyEncrypted, FullyEncrypted, EncryptionStateUnknown }; enum SignatureState { NotSigned, PartiallySigned, FullySigned, SignatureStateUnknown }; explicit MessageItem(); virtual ~MessageItem(); public: /// Returns the list of tags for this item. virtual QList< Tag * > tagList() const; /// Returns true if this message has an annotation. virtual bool hasAnnotation() const; /// Returns the annotation of the message, given that hasAnnotation() is true - QString annotation() const; - - /// Shows a dialog to edit or delete the annotation - void editAnnotation(); + Akonadi::Item annotation() const; /** - * Returns Tag associated to this message that has the specified id or 0 - * if no such tag exists. mTagList will be 0 in 99% of the cases. - */ + * Returns Tag associated to this message that has the specified id or 0 + * if no such tag exists. mTagList will be 0 in 99% of the cases. + */ const Tag * findTag( const QString &szTagId ) const; QString tagListDescription() const; /// Deletes all cached tags. The next time someone asks this item for the tags, they are /// fetched again void invalidateTagCache(); /// Same as invalidateTagCache(), only for the annotation void invalidateAnnotationCache(); QColor textColor() const; QColor backgroundColor() const; QFont font() const; QString fontKey() const; SignatureState signatureState() const; void setSignatureState( SignatureState state ); EncryptionState encryptionState() const; void setEncryptionState( EncryptionState state ); QByteArray messageIdMD5() const; void setMessageIdMD5( const QByteArray &md5 ); QByteArray inReplyToIdMD5() const; void setInReplyToIdMD5( const QByteArray &md5 ); QByteArray referencesIdMD5() const; void setReferencesIdMD5( const QByteArray &md5 ); void setSubjectIsPrefixed( bool subjectIsPrefixed ); bool subjectIsPrefixed() const; QByteArray strippedSubjectMD5() const; void setStrippedSubjectMD5( const QByteArray &md5 ); bool aboutToBeRemoved() const; void setAboutToBeRemoved( bool aboutToBeRemoved ); ThreadingStatus threadingStatus() const; void setThreadingStatus( ThreadingStatus threadingStatus ); unsigned long uniqueId() const; Akonadi::Item akonadiItem() const; void setAkonadiItem( const Akonadi::Item &item ); MessageItem * topmostMessage(); QString accessibleText( const MessageList::Core::Theme* theme, int columnIndex ); /** * Appends the whole subtree originating at this item * to the specified list. This item is included! */ void subTreeToList( QList< MessageItem * > &list ); // // Colors and fonts shared by all message items. // textColor() and font() will take the message status into account and return // one of these. // Call these setters only once when reading the colors from the config file. // static void setUnreadMessageColor( const QColor &color ); static void setImportantMessageColor( const QColor &color ); static void setToDoMessageColor( const QColor &color ); static void setGeneralFont( const QFont &font ); static void setUnreadMessageFont( const QFont &font ); static void setImportantMessageFont( const QFont &font ); static void setToDoMessageFont( const QFont &font ); protected: explicit MessageItem( MessageItemPrivate* dd ); private: QString accessibleTextForField( Theme::ContentItem::Type field ); Q_DECLARE_PRIVATE( MessageItem ) }; class FakeItemPrivate; /// A message item that can have a fake tag list and a fake annotation class MESSAGELIST_EXPORT FakeItem : public MessageItem { public: explicit FakeItem(); ~FakeItem(); /// Reimplemented to return the fake tag list virtual QList< Tag * > tagList() const; /// Sets a list of fake tags for this item void setFakeTags( const QList< Tag* > &tagList ); /// Reimplemented to always return true virtual bool hasAnnotation() const; private: Q_DECLARE_PRIVATE( FakeItem ) }; } // namespace Core } // namespace MessageList #endif //!__MESSAGELIST_CORE_MESSAGEITEM_H__