src/hbwidgets/editors/hbsmileyengine_p.cpp
changeset 1 f7ac710697a9
child 7 923ff622b8b9
equal deleted inserted replaced
0:16d8024aca5e 1:f7ac710697a9
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbWidgets module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 
       
    26 #include "hbsmileyengine_p.h"
       
    27 #include "hbsmileyengine_p_p.h"
       
    28 #include <hbiconanimator.h>
       
    29 #include <hbicon.h>
       
    30 #include <QTextDocument>
       
    31 #include <QFontMetricsF>
       
    32 #include <QChar>
       
    33 #include <QPainter>
       
    34 
       
    35 HbSmileyTheme HbSmileyEnginePrivate::mDefaultTheme = HbSmileyTheme();
       
    36 
       
    37 QSizeF HbIconTextObject::intrinsicSize(QTextDocument *doc, int posInDocument,
       
    38                                     const QTextFormat &format)
       
    39 {
       
    40     Q_UNUSED(doc)
       
    41     Q_UNUSED(posInDocument)
       
    42 
       
    43     HbSmileyDataType smiley = qVariantValue<HbSmileyDataType>(format.property(HbSmileyData));
       
    44     HbIconAnimator *animator = smiley.animator;
       
    45     Q_ASSERT(animator);
       
    46 
       
    47     QFont f = static_cast<const QTextCharFormat&>(format).font();
       
    48     QFontMetricsF fm(f);
       
    49 
       
    50     // TODO: optimize this
       
    51     HbIcon icon = animator->icon();
       
    52     qreal iconHeight = fm.height() * smiley.scaleFactor;
       
    53 
       
    54     if(icon.height() != iconHeight) {
       
    55         icon.setHeight(iconHeight);
       
    56         animator->setIcon(icon);
       
    57     }
       
    58     // We are going to draw the smiley on the descent line not on the baseline
       
    59     // therefore the size needs to be adjusted accordingly.
       
    60     return (icon.size()-QSizeF(0,fm.descent()));
       
    61 }
       
    62 
       
    63 void HbIconTextObject::drawObject(QPainter *painter, const QRectF &rect,
       
    64                                QTextDocument *doc, int posInDocument,
       
    65                                const QTextFormat &format)
       
    66 {
       
    67     Q_UNUSED(doc)
       
    68     Q_UNUSED(posInDocument)
       
    69 
       
    70     HbSmileyDataType smiley = qVariantValue<HbSmileyDataType>(format.property(HbSmileyData));
       
    71 
       
    72     QFont f = static_cast<const QTextCharFormat&>(format).font();
       
    73     QFontMetricsF fm(f);
       
    74 
       
    75     // Draw smiley on descent line
       
    76     QTransform matrix = painter->transform();
       
    77     painter->translate(0,fm.descent()-1);
       
    78 
       
    79     HbIconAnimator *animator = smiley.animator;
       
    80     animator->paint(painter,rect);
       
    81     painter->setTransform(matrix);
       
    82 }
       
    83 
       
    84 HbSmileyEnginePrivate::HbSmileyEnginePrivate()
       
    85     :mDocument(0),
       
    86      mEdited(true),
       
    87      mIconTextObject(new HbIconTextObject),
       
    88      mScaleFactor(1)
       
    89 {
       
    90     q_ptr = 0;
       
    91 }
       
    92 
       
    93 HbSmileyEnginePrivate::~HbSmileyEnginePrivate()
       
    94 {
       
    95     cleanUp();
       
    96     delete mIconTextObject;
       
    97 }
       
    98 
       
    99 
       
   100 void HbSmileyEnginePrivate::init()
       
   101 {
       
   102     // Load default smiley theme
       
   103     if(mDefaultTheme.isNull()) {
       
   104         HbSmileyEnginePrivate::mDefaultTheme.load(":smileys/smileys_theme.sml");
       
   105         //
       
   106         //TODO: uncomment the lines below if animation definition file is provided for
       
   107         //      default smiley theme.
       
   108         //HbIconAnimationManager* m = HbIconAnimationManager::global();
       
   109         //m->addDefinitionFile(":smileys/smileys_animations.xml");
       
   110     }
       
   111     mSmileyTheme = HbSmileyEnginePrivate::mDefaultTheme;
       
   112 }
       
   113 
       
   114 void HbSmileyEnginePrivate::cleanUp()
       
   115 {
       
   116     QList<QTextCursor*> cursors = mCursorToAnimator.keys();
       
   117     qDeleteAll(cursors);
       
   118 
       
   119     QList<HbIconAnimator*> animators = mAnimatorToCursors.keys();
       
   120     qDeleteAll(animators);
       
   121 
       
   122     mAnimatorToCursors.clear();
       
   123     mCursorToAnimator.clear();
       
   124     mSmileyAnimator.clear();
       
   125 }
       
   126 
       
   127 void HbSmileyEnginePrivate::setDocument(QTextDocument *doc)
       
   128 {
       
   129     Q_Q(HbSmileyEngine);
       
   130     mDocument = doc;
       
   131     mDocument->documentLayout()->registerHandler(HbIconTextFormat, mIconTextObject);
       
   132     q->connect(mDocument, SIGNAL(contentsChange(int,int,int)), q, SLOT(_q_documentContentsChanged(int,int,int)));
       
   133     cleanUp();
       
   134 }
       
   135 
       
   136 void HbSmileyEnginePrivate::insertSmiley( QTextCursor cursor, const QString& name)
       
   137 {
       
   138     QTextCharFormat hbiconFormat;
       
   139     QTextCursor *tmpCursor = new QTextCursor(cursor);
       
   140     hbiconFormat.setObjectType(HbIconTextFormat);
       
   141 
       
   142     HbSmileyDataType smiley;
       
   143     HbIconAnimator *animator = lookupAnimator(name);
       
   144     smiley.cursor = tmpCursor;
       
   145     smiley.animator = animator;
       
   146     smiley.scaleFactor = mScaleFactor;
       
   147 
       
   148     hbiconFormat.setProperty(HbIconTextObject::HbSmileyData, qVariantFromValue(smiley));
       
   149 
       
   150     mEdited = false;
       
   151     tmpCursor->insertText(QString(QChar::ObjectReplacementCharacter), hbiconFormat);
       
   152     mEdited = true;
       
   153     tmpCursor->setPosition(tmpCursor->position()-1);
       
   154     mAnimatorToCursors[animator] << tmpCursor;
       
   155     mCursorToAnimator[tmpCursor] = animator;
       
   156 }
       
   157 
       
   158 void HbSmileyEnginePrivate::insertSmileys( QTextCursor cursor, bool insertOne)
       
   159 {
       
   160     QString regexpStr;
       
   161     foreach (QString pattern, mSmileyTheme.patterns()) {
       
   162         regexpStr += QRegExp::escape(pattern) + "|";
       
   163     }
       
   164     regexpStr.remove(regexpStr.count()-1, 1);
       
   165 
       
   166     QRegExp rx(regexpStr);
       
   167     cursor = mDocument->find(rx, cursor);
       
   168     while ( !cursor.isNull()){
       
   169         insertSmiley(cursor, mSmileyTheme.smiley(cursor.selectedText()));
       
   170         if (insertOne) {
       
   171             break;
       
   172         }
       
   173         cursor = mDocument->find(rx, cursor);
       
   174     }
       
   175 }
       
   176 
       
   177 
       
   178 HbIconAnimator* HbSmileyEnginePrivate::lookupAnimator(const QString& name)
       
   179 {
       
   180     Q_Q(HbSmileyEngine);
       
   181     HbIconAnimator *animator = mSmileyAnimator.value(name);
       
   182 
       
   183     // Init icon if it doesn't already exits
       
   184     if (!animator) {
       
   185         HbIconAnimator *newAnimator = new HbIconAnimator();
       
   186         animator = newAnimator;
       
   187         HbIcon icon = HbIcon(name);
       
   188         icon.setFlags(HbIcon::NoAutoStartAnimation);
       
   189         animator->setIcon(icon);
       
   190         q->connect(animator,SIGNAL(animationProgressed()),q,SLOT(_q_animationProgressed()));
       
   191         animator->startAnimation();
       
   192         mSmileyAnimator[name] = animator;
       
   193     }
       
   194 
       
   195     return animator;
       
   196 }
       
   197 
       
   198 
       
   199 bool HbSmileyEnginePrivate::isCursorValid(QTextCursor* cursor) const
       
   200 {
       
   201     bool ret = true;
       
   202 
       
   203     if (cursor) {
       
   204         if (mDocument->characterAt(cursor->position()) != QChar::ObjectReplacementCharacter) {
       
   205             ret = false;
       
   206          } else {
       
   207              QTextCursor tmpCursor(*cursor);
       
   208              tmpCursor.setPosition(tmpCursor.position()+1);
       
   209              QTextCharFormat format = tmpCursor.charFormat();
       
   210 
       
   211              HbSmileyDataType smiley = qVariantValue<HbSmileyDataType>(format.property(HbIconTextObject::HbSmileyData));
       
   212              if (cursor != smiley.cursor) {
       
   213                 ret = false;
       
   214              }
       
   215          }
       
   216      } else {
       
   217         ret = false;
       
   218      }
       
   219     return ret;
       
   220 }
       
   221 
       
   222 void HbSmileyEnginePrivate::convertToText(QTextDocument *copyDoc) const
       
   223 {
       
   224     QList<QTextCursor> cursors;
       
   225     // copy the cursors to copy document so that the positions get automatically updated
       
   226     foreach(QTextCursor *cursor, mCursorToAnimator.keys()) {
       
   227         if(isCursorValid(cursor)) {
       
   228             QTextCursor copyCursor(copyDoc);
       
   229             copyCursor.setPosition(cursor->position());
       
   230             cursors << copyCursor;
       
   231         }
       
   232     }
       
   233 
       
   234     foreach(QTextCursor copyCursor, cursors) {
       
   235         copyCursor.setPosition(copyCursor.position()+1, QTextCursor::KeepAnchor);
       
   236         QTextFormat format = copyCursor.charFormat();
       
   237         HbSmileyDataType smiley = qVariantValue<HbSmileyDataType>(format.property(HbIconTextObject::HbSmileyData));
       
   238         HbIconAnimator *animator = smiley.animator;
       
   239         Q_ASSERT(animator);
       
   240 
       
   241         QString pattern = mSmileyTheme.patterns(mSmileyAnimator.key(animator)).first();
       
   242         copyCursor.deleteChar();
       
   243         copyCursor.insertText(pattern);
       
   244     }
       
   245 }
       
   246 
       
   247 void HbSmileyEnginePrivate::_q_animationProgressed()
       
   248 {
       
   249     Q_Q(HbSmileyEngine);
       
   250     HbIconAnimator *animator = qobject_cast<HbIconAnimator *>(q->sender());
       
   251     Q_ASSERT(animator);
       
   252 
       
   253     foreach(QTextCursor *cursor, mAnimatorToCursors.value(animator)) {
       
   254         QTextCursor tmpCursor(*cursor);
       
   255         tmpCursor.setPosition(tmpCursor.position()+1, QTextCursor::KeepAnchor);
       
   256 
       
   257         // update a bogus property, which will trigger a paint
       
   258         QTextCharFormat format;
       
   259         format.setProperty(HbIconTextObject::HbSmileyData+1, QString("Dummy"));
       
   260         mEdited = false;
       
   261         tmpCursor.mergeCharFormat(format);
       
   262         mEdited = true;
       
   263     }
       
   264 }
       
   265 
       
   266 void HbSmileyEnginePrivate::_q_documentContentsChanged(int position, int charsRemoved, int charsAdded)
       
   267 {
       
   268     Q_UNUSED(position);
       
   269     Q_UNUSED(charsRemoved);
       
   270     Q_UNUSED(charsAdded);
       
   271 
       
   272     if(charsRemoved > 0 && mEdited) {
       
   273         foreach (QTextCursor* cursor, mCursorToAnimator.keys()) {          
       
   274             if (!isCursorValid(cursor)) {
       
   275                 HbIconAnimator * animator = mCursorToAnimator.value(cursor);
       
   276                 mCursorToAnimator.remove(cursor);
       
   277                 QList<QTextCursor*> & cursorList = mAnimatorToCursors[animator];
       
   278                 cursorList.removeFirst();
       
   279                 if (!cursorList.count()) {
       
   280                     mAnimatorToCursors.remove(animator);
       
   281                     mSmileyAnimator.remove(mSmileyAnimator.key(animator));
       
   282                     animator->deleteLater();
       
   283                 }
       
   284                 delete cursor;
       
   285             }
       
   286         }
       
   287     }
       
   288 }
       
   289 
       
   290 
       
   291 HbSmileyEngine::HbSmileyEngine(QObject *parent)
       
   292     :QObject(parent),
       
   293     d_ptr(new HbSmileyEnginePrivate)
       
   294 
       
   295 {
       
   296     Q_D(HbSmileyEngine);
       
   297     d->q_ptr = this;
       
   298     d->init();
       
   299 }
       
   300 
       
   301 HbSmileyEngine::HbSmileyEngine(HbSmileyEnginePrivate &dd, QObject *parent)
       
   302     :QObject(parent),
       
   303     d_ptr(&dd)
       
   304 {
       
   305     Q_D(HbSmileyEngine);
       
   306     d->q_ptr = this;
       
   307     d->init();
       
   308 }
       
   309 
       
   310 HbSmileyEngine::~HbSmileyEngine()
       
   311 {
       
   312     delete d_ptr;
       
   313 }
       
   314 
       
   315 void HbSmileyEngine::setDocument(QTextDocument *doc)
       
   316 {
       
   317     Q_D(HbSmileyEngine);
       
   318     d->setDocument(doc);
       
   319 }
       
   320 
       
   321 void HbSmileyEngine::setTheme(const HbSmileyTheme& theme)
       
   322 {
       
   323     Q_D(HbSmileyEngine);
       
   324     d->mSmileyTheme = theme;
       
   325 }
       
   326 
       
   327 HbSmileyTheme HbSmileyEngine::theme() const
       
   328 {
       
   329     Q_D(const HbSmileyEngine);
       
   330     return d->mSmileyTheme;
       
   331 }
       
   332 
       
   333 HbSmileyTheme HbSmileyEngine::defaultTheme() const
       
   334 {
       
   335     return HbSmileyEnginePrivate::mDefaultTheme;
       
   336 }
       
   337 
       
   338 
       
   339 QString HbSmileyEngine::toPlainText() const
       
   340 {
       
   341     Q_D(const HbSmileyEngine);
       
   342     QTextDocument *copyDoc = d->mDocument->clone();
       
   343     d->convertToText(copyDoc);
       
   344     QString plainText = copyDoc->toPlainText();
       
   345     delete copyDoc;
       
   346     return plainText;
       
   347 }
       
   348 
       
   349 QString HbSmileyEngine::toHtml() const
       
   350 {
       
   351     Q_D(const HbSmileyEngine);
       
   352     QTextDocument *copyDoc = d->mDocument->clone();
       
   353     d->convertToText(copyDoc);
       
   354     QString htmlString = copyDoc->toHtml();
       
   355     return htmlString;
       
   356 }
       
   357 
       
   358 void HbSmileyEngine::setSmileyScaleFactor(qreal scaleFactor)
       
   359 {
       
   360     Q_D(HbSmileyEngine);
       
   361     if (d->mScaleFactor != scaleFactor) {
       
   362         // Note: it is assumed that the scale factor is not changed on the fly hence we do not need to redraw the smileyes.
       
   363         d->mScaleFactor = scaleFactor;
       
   364     }
       
   365 }
       
   366 
       
   367 void HbSmileyEngine::startAnimation()
       
   368 {
       
   369     Q_D(HbSmileyEngine);
       
   370     foreach (HbIconAnimator *animator, d->mAnimatorToCursors.keys()) {
       
   371         animator->startAnimation();
       
   372     }
       
   373 }
       
   374 
       
   375 void HbSmileyEngine::stopAnimation()
       
   376 {
       
   377     Q_D(HbSmileyEngine);
       
   378     foreach (HbIconAnimator *animator, d->mAnimatorToCursors.keys()) {
       
   379         animator->stopAnimation();
       
   380     }
       
   381 }
       
   382 
       
   383 void HbSmileyEngine::insertSmileys()
       
   384 {
       
   385     Q_D(HbSmileyEngine);
       
   386 
       
   387     QTextCursor cursor(d->mDocument);
       
   388     d->insertSmileys(cursor);
       
   389 }
       
   390 
       
   391 void HbSmileyEngine::insertSmileys(const QTextCursor& cursor)
       
   392 {
       
   393     Q_D(HbSmileyEngine);
       
   394     d->insertSmileys(cursor);
       
   395 }
       
   396 
       
   397 void HbSmileyEngine::insertSmiley(const QTextCursor& cursor)
       
   398 {
       
   399     Q_D(HbSmileyEngine);
       
   400     d->insertSmileys(cursor,true);
       
   401 }
       
   402 
       
   403 #include "moc_hbsmileyengine_p.cpp"