src/hbwidgets/editors/hbsmileyengine_p.cpp
changeset 1 f7ac710697a9
child 7 923ff622b8b9
--- /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 <hbiconanimator.h>
+#include <hbicon.h>
+#include <QTextDocument>
+#include <QFontMetricsF>
+#include <QChar>
+#include <QPainter>
+
+HbSmileyTheme HbSmileyEnginePrivate::mDefaultTheme = HbSmileyTheme();
+
+QSizeF HbIconTextObject::intrinsicSize(QTextDocument *doc, int posInDocument,
+                                    const QTextFormat &format)
+{
+    Q_UNUSED(doc)
+    Q_UNUSED(posInDocument)
+
+    HbSmileyDataType smiley = qVariantValue<HbSmileyDataType>(format.property(HbSmileyData));
+    HbIconAnimator *animator = smiley.animator;
+    Q_ASSERT(animator);
+
+    QFont f = static_cast<const QTextCharFormat&>(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<HbSmileyDataType>(format.property(HbSmileyData));
+
+    QFont f = static_cast<const QTextCharFormat&>(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<QTextCursor*> cursors = mCursorToAnimator.keys();
+    qDeleteAll(cursors);
+
+    QList<HbIconAnimator*> 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<HbSmileyDataType>(format.property(HbIconTextObject::HbSmileyData));
+             if (cursor != smiley.cursor) {
+                ret = false;
+             }
+         }
+     } else {
+        ret = false;
+     }
+    return ret;
+}
+
+void HbSmileyEnginePrivate::convertToText(QTextDocument *copyDoc) const
+{
+    QList<QTextCursor> 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<HbSmileyDataType>(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<HbIconAnimator *>(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<QTextCursor*> & 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"