diff -r 16d8024aca5e -r f7ac710697a9 src/hbwidgets/editors/hbsmileyengine_p.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hbwidgets/editors/hbsmileyengine_p.cpp Mon May 03 12:48:33 2010 +0300 @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (developer.feedback@nokia.com) +** +** This file is part of the HbWidgets module of the UI Extensions for Mobile. +** +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at developer.feedback@nokia.com. +** +****************************************************************************/ + +#include "hbsmileyengine_p.h" +#include "hbsmileyengine_p_p.h" +#include +#include +#include +#include +#include +#include + +HbSmileyTheme HbSmileyEnginePrivate::mDefaultTheme = HbSmileyTheme(); + +QSizeF HbIconTextObject::intrinsicSize(QTextDocument *doc, int posInDocument, + const QTextFormat &format) +{ + Q_UNUSED(doc) + Q_UNUSED(posInDocument) + + HbSmileyDataType smiley = qVariantValue(format.property(HbSmileyData)); + HbIconAnimator *animator = smiley.animator; + Q_ASSERT(animator); + + QFont f = static_cast(format).font(); + QFontMetricsF fm(f); + + // TODO: optimize this + HbIcon icon = animator->icon(); + qreal iconHeight = fm.height() * smiley.scaleFactor; + + if(icon.height() != iconHeight) { + icon.setHeight(iconHeight); + animator->setIcon(icon); + } + // We are going to draw the smiley on the descent line not on the baseline + // therefore the size needs to be adjusted accordingly. + return (icon.size()-QSizeF(0,fm.descent())); +} + +void HbIconTextObject::drawObject(QPainter *painter, const QRectF &rect, + QTextDocument *doc, int posInDocument, + const QTextFormat &format) +{ + Q_UNUSED(doc) + Q_UNUSED(posInDocument) + + HbSmileyDataType smiley = qVariantValue(format.property(HbSmileyData)); + + QFont f = static_cast(format).font(); + QFontMetricsF fm(f); + + // Draw smiley on descent line + QTransform matrix = painter->transform(); + painter->translate(0,fm.descent()-1); + + HbIconAnimator *animator = smiley.animator; + animator->paint(painter,rect); + painter->setTransform(matrix); +} + +HbSmileyEnginePrivate::HbSmileyEnginePrivate() + :mDocument(0), + mEdited(true), + mIconTextObject(new HbIconTextObject), + mScaleFactor(1) +{ + q_ptr = 0; +} + +HbSmileyEnginePrivate::~HbSmileyEnginePrivate() +{ + cleanUp(); + delete mIconTextObject; +} + + +void HbSmileyEnginePrivate::init() +{ + // Load default smiley theme + if(mDefaultTheme.isNull()) { + HbSmileyEnginePrivate::mDefaultTheme.load(":smileys/smileys_theme.sml"); + // + //TODO: uncomment the lines below if animation definition file is provided for + // default smiley theme. + //HbIconAnimationManager* m = HbIconAnimationManager::global(); + //m->addDefinitionFile(":smileys/smileys_animations.xml"); + } + mSmileyTheme = HbSmileyEnginePrivate::mDefaultTheme; +} + +void HbSmileyEnginePrivate::cleanUp() +{ + QList cursors = mCursorToAnimator.keys(); + qDeleteAll(cursors); + + QList animators = mAnimatorToCursors.keys(); + qDeleteAll(animators); + + mAnimatorToCursors.clear(); + mCursorToAnimator.clear(); + mSmileyAnimator.clear(); +} + +void HbSmileyEnginePrivate::setDocument(QTextDocument *doc) +{ + Q_Q(HbSmileyEngine); + mDocument = doc; + mDocument->documentLayout()->registerHandler(HbIconTextFormat, mIconTextObject); + q->connect(mDocument, SIGNAL(contentsChange(int,int,int)), q, SLOT(_q_documentContentsChanged(int,int,int))); + cleanUp(); +} + +void HbSmileyEnginePrivate::insertSmiley( QTextCursor cursor, const QString& name) +{ + QTextCharFormat hbiconFormat; + QTextCursor *tmpCursor = new QTextCursor(cursor); + hbiconFormat.setObjectType(HbIconTextFormat); + + HbSmileyDataType smiley; + HbIconAnimator *animator = lookupAnimator(name); + smiley.cursor = tmpCursor; + smiley.animator = animator; + smiley.scaleFactor = mScaleFactor; + + hbiconFormat.setProperty(HbIconTextObject::HbSmileyData, qVariantFromValue(smiley)); + + mEdited = false; + tmpCursor->insertText(QString(QChar::ObjectReplacementCharacter), hbiconFormat); + mEdited = true; + tmpCursor->setPosition(tmpCursor->position()-1); + mAnimatorToCursors[animator] << tmpCursor; + mCursorToAnimator[tmpCursor] = animator; +} + +void HbSmileyEnginePrivate::insertSmileys( QTextCursor cursor, bool insertOne) +{ + QString regexpStr; + foreach (QString pattern, mSmileyTheme.patterns()) { + regexpStr += QRegExp::escape(pattern) + "|"; + } + regexpStr.remove(regexpStr.count()-1, 1); + + QRegExp rx(regexpStr); + cursor = mDocument->find(rx, cursor); + while ( !cursor.isNull()){ + insertSmiley(cursor, mSmileyTheme.smiley(cursor.selectedText())); + if (insertOne) { + break; + } + cursor = mDocument->find(rx, cursor); + } +} + + +HbIconAnimator* HbSmileyEnginePrivate::lookupAnimator(const QString& name) +{ + Q_Q(HbSmileyEngine); + HbIconAnimator *animator = mSmileyAnimator.value(name); + + // Init icon if it doesn't already exits + if (!animator) { + HbIconAnimator *newAnimator = new HbIconAnimator(); + animator = newAnimator; + HbIcon icon = HbIcon(name); + icon.setFlags(HbIcon::NoAutoStartAnimation); + animator->setIcon(icon); + q->connect(animator,SIGNAL(animationProgressed()),q,SLOT(_q_animationProgressed())); + animator->startAnimation(); + mSmileyAnimator[name] = animator; + } + + return animator; +} + + +bool HbSmileyEnginePrivate::isCursorValid(QTextCursor* cursor) const +{ + bool ret = true; + + if (cursor) { + if (mDocument->characterAt(cursor->position()) != QChar::ObjectReplacementCharacter) { + ret = false; + } else { + QTextCursor tmpCursor(*cursor); + tmpCursor.setPosition(tmpCursor.position()+1); + QTextCharFormat format = tmpCursor.charFormat(); + + HbSmileyDataType smiley = qVariantValue(format.property(HbIconTextObject::HbSmileyData)); + if (cursor != smiley.cursor) { + ret = false; + } + } + } else { + ret = false; + } + return ret; +} + +void HbSmileyEnginePrivate::convertToText(QTextDocument *copyDoc) const +{ + QList cursors; + // copy the cursors to copy document so that the positions get automatically updated + foreach(QTextCursor *cursor, mCursorToAnimator.keys()) { + if(isCursorValid(cursor)) { + QTextCursor copyCursor(copyDoc); + copyCursor.setPosition(cursor->position()); + cursors << copyCursor; + } + } + + foreach(QTextCursor copyCursor, cursors) { + copyCursor.setPosition(copyCursor.position()+1, QTextCursor::KeepAnchor); + QTextFormat format = copyCursor.charFormat(); + HbSmileyDataType smiley = qVariantValue(format.property(HbIconTextObject::HbSmileyData)); + HbIconAnimator *animator = smiley.animator; + Q_ASSERT(animator); + + QString pattern = mSmileyTheme.patterns(mSmileyAnimator.key(animator)).first(); + copyCursor.deleteChar(); + copyCursor.insertText(pattern); + } +} + +void HbSmileyEnginePrivate::_q_animationProgressed() +{ + Q_Q(HbSmileyEngine); + HbIconAnimator *animator = qobject_cast(q->sender()); + Q_ASSERT(animator); + + foreach(QTextCursor *cursor, mAnimatorToCursors.value(animator)) { + QTextCursor tmpCursor(*cursor); + tmpCursor.setPosition(tmpCursor.position()+1, QTextCursor::KeepAnchor); + + // update a bogus property, which will trigger a paint + QTextCharFormat format; + format.setProperty(HbIconTextObject::HbSmileyData+1, QString("Dummy")); + mEdited = false; + tmpCursor.mergeCharFormat(format); + mEdited = true; + } +} + +void HbSmileyEnginePrivate::_q_documentContentsChanged(int position, int charsRemoved, int charsAdded) +{ + Q_UNUSED(position); + Q_UNUSED(charsRemoved); + Q_UNUSED(charsAdded); + + if(charsRemoved > 0 && mEdited) { + foreach (QTextCursor* cursor, mCursorToAnimator.keys()) { + if (!isCursorValid(cursor)) { + HbIconAnimator * animator = mCursorToAnimator.value(cursor); + mCursorToAnimator.remove(cursor); + QList & cursorList = mAnimatorToCursors[animator]; + cursorList.removeFirst(); + if (!cursorList.count()) { + mAnimatorToCursors.remove(animator); + mSmileyAnimator.remove(mSmileyAnimator.key(animator)); + animator->deleteLater(); + } + delete cursor; + } + } + } +} + + +HbSmileyEngine::HbSmileyEngine(QObject *parent) + :QObject(parent), + d_ptr(new HbSmileyEnginePrivate) + +{ + Q_D(HbSmileyEngine); + d->q_ptr = this; + d->init(); +} + +HbSmileyEngine::HbSmileyEngine(HbSmileyEnginePrivate &dd, QObject *parent) + :QObject(parent), + d_ptr(&dd) +{ + Q_D(HbSmileyEngine); + d->q_ptr = this; + d->init(); +} + +HbSmileyEngine::~HbSmileyEngine() +{ + delete d_ptr; +} + +void HbSmileyEngine::setDocument(QTextDocument *doc) +{ + Q_D(HbSmileyEngine); + d->setDocument(doc); +} + +void HbSmileyEngine::setTheme(const HbSmileyTheme& theme) +{ + Q_D(HbSmileyEngine); + d->mSmileyTheme = theme; +} + +HbSmileyTheme HbSmileyEngine::theme() const +{ + Q_D(const HbSmileyEngine); + return d->mSmileyTheme; +} + +HbSmileyTheme HbSmileyEngine::defaultTheme() const +{ + return HbSmileyEnginePrivate::mDefaultTheme; +} + + +QString HbSmileyEngine::toPlainText() const +{ + Q_D(const HbSmileyEngine); + QTextDocument *copyDoc = d->mDocument->clone(); + d->convertToText(copyDoc); + QString plainText = copyDoc->toPlainText(); + delete copyDoc; + return plainText; +} + +QString HbSmileyEngine::toHtml() const +{ + Q_D(const HbSmileyEngine); + QTextDocument *copyDoc = d->mDocument->clone(); + d->convertToText(copyDoc); + QString htmlString = copyDoc->toHtml(); + return htmlString; +} + +void HbSmileyEngine::setSmileyScaleFactor(qreal scaleFactor) +{ + Q_D(HbSmileyEngine); + if (d->mScaleFactor != scaleFactor) { + // Note: it is assumed that the scale factor is not changed on the fly hence we do not need to redraw the smileyes. + d->mScaleFactor = scaleFactor; + } +} + +void HbSmileyEngine::startAnimation() +{ + Q_D(HbSmileyEngine); + foreach (HbIconAnimator *animator, d->mAnimatorToCursors.keys()) { + animator->startAnimation(); + } +} + +void HbSmileyEngine::stopAnimation() +{ + Q_D(HbSmileyEngine); + foreach (HbIconAnimator *animator, d->mAnimatorToCursors.keys()) { + animator->stopAnimation(); + } +} + +void HbSmileyEngine::insertSmileys() +{ + Q_D(HbSmileyEngine); + + QTextCursor cursor(d->mDocument); + d->insertSmileys(cursor); +} + +void HbSmileyEngine::insertSmileys(const QTextCursor& cursor) +{ + Q_D(HbSmileyEngine); + d->insertSmileys(cursor); +} + +void HbSmileyEngine::insertSmiley(const QTextCursor& cursor) +{ + Q_D(HbSmileyEngine); + d->insertSmileys(cursor,true); +} + +#include "moc_hbsmileyengine_p.cpp"