src/hbwidgets/editors/hbselectioncontrol_p.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     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 //
       
    27 //  W A R N I N G
       
    28 //  -------------
       
    29 //
       
    30 // This file is not part of the Hb API.  It exists purely as an
       
    31 // implementation detail.  This file may change from version to
       
    32 // version without notice, or even be removed.
       
    33 //
       
    34 // We mean it.
       
    35 //
       
    36 
       
    37 #include "hbselectioncontrol_p.h"
       
    38 #include "hbstyleoption.h"
       
    39 #include "hbeffect.h"
       
    40 #include "hbdialog_p.h"
       
    41 #include "hbabstractedit.h"
       
    42 #include "hbabstractedit_p.h"
       
    43 #include "hbtoucharea.h"
       
    44 
       
    45 
       
    46 #include <QTextCursor>
       
    47 #include <QGraphicsItem>
       
    48 #include <QAbstractTextDocumentLayout>
       
    49 #include <QGraphicsSceneMouseEvent>
       
    50 #include <QBasicTimer>
       
    51 #include <QSizeF>
       
    52 #include <QPointF>
       
    53 
       
    54 #include <hbwidgetfeedback.h>
       
    55 
       
    56 namespace {
       
    57     static const int SNAP_DELAY = 300;
       
    58 }
       
    59 
       
    60 
       
    61 class HbSelectionControlPrivate :public HbDialogPrivate
       
    62 {
       
    63     Q_DECLARE_PUBLIC(HbSelectionControl)
       
    64 
       
    65 public:
       
    66     HbSelectionControlPrivate(HbAbstractEdit *edit);
       
    67     void init();
       
    68     void createPrimitives();
       
    69     void updateHandle(int newHandlePos,
       
    70                       Qt::AlignmentFlag handleAlignment,
       
    71                       QGraphicsItem *handle,
       
    72                       QGraphicsItem *handleTouchArea,
       
    73                       HbStyle::Primitive handlePrimitive);
       
    74     QGraphicsItem * reparent(QGraphicsItem *item);
       
    75     void reparent(QGraphicsItem *item, QGraphicsItem *newParent);
       
    76     void reparentHandles(QGraphicsItem *newParent);
       
    77 
       
    78 public:
       
    79 
       
    80     HbAbstractEdit *mEdit;
       
    81     QPointF mMouseOffset;
       
    82 
       
    83     QGraphicsItem *mSelectionStartHandle;
       
    84     QGraphicsItem *mSelectionEndHandle;
       
    85     HbTouchArea* mSelectionStartTouchArea;
       
    86     HbTouchArea* mSelectionEndTouchArea;
       
    87 
       
    88     HbSelectionControl::HandleType mPressed;
       
    89     bool mHandlesDisabled;
       
    90     bool mPanInProgress;
       
    91     bool mHandlesMoved;
       
    92     QBasicTimer mWordSnapTimer;
       
    93 };
       
    94 
       
    95 
       
    96 HbSelectionControlPrivate::HbSelectionControlPrivate(HbAbstractEdit *edit):
       
    97     mEdit(edit),
       
    98     mSelectionStartHandle(0),
       
    99     mSelectionEndHandle(0),
       
   100     mSelectionStartTouchArea(0),
       
   101     mSelectionEndTouchArea(0),
       
   102     mPressed(HbSelectionControl::HandleType(0)),
       
   103     mHandlesDisabled(true),
       
   104     mPanInProgress(false),
       
   105     mHandlesMoved(false)
       
   106 {
       
   107 }
       
   108 
       
   109 void HbSelectionControlPrivate::init()
       
   110 {
       
   111     Q_Q(HbSelectionControl);
       
   112     createPrimitives();
       
   113 
       
   114     q->setBackgroundItem(HbStyle::P_None);
       
   115     q->setFocusPolicy(Qt::NoFocus);
       
   116     q->setTimeout(HbPopup::NoTimeout);
       
   117     q->setBackgroundFaded(false);
       
   118     q->setVisible(false);
       
   119     q->setDismissPolicy(HbPopup::NoDismiss);
       
   120     q->setModal(false);
       
   121 
       
   122     #ifdef HB_EFFECTS
       
   123     HbEffect::disable(q);
       
   124     #endif    
       
   125 
       
   126     q->setParent(mEdit);
       
   127 
       
   128     // Control will handle all events going to different handlers.
       
   129     q->setHandlesChildEvents(true);
       
   130 
       
   131     QObject::connect(mEdit, SIGNAL(cursorPositionChanged(int, int)), q, SLOT(updatePrimitives()));
       
   132     QObject::connect(mEdit, SIGNAL(selectionChanged(const QTextCursor&, const QTextCursor&)), q, SLOT(updatePrimitives()));
       
   133     QObject::connect(mEdit, SIGNAL(contentsChanged()), q, SLOT(updatePrimitives()));
       
   134 
       
   135     q->updatePrimitives();
       
   136 
       
   137 }
       
   138 
       
   139 void HbSelectionControlPrivate::createPrimitives()
       
   140 {
       
   141     Q_Q(HbSelectionControl);
       
   142     if (!mSelectionStartHandle) {
       
   143         mSelectionStartHandle = mEdit->style()->createPrimitive(HbStyle::P_SelectionControl_selectionstart, q);
       
   144         mSelectionStartHandle->hide();
       
   145     }
       
   146 
       
   147     if (!mSelectionEndHandle) {
       
   148         mSelectionEndHandle = mEdit->style()->createPrimitive(HbStyle::P_SelectionControl_selectionend, q);
       
   149         mSelectionEndHandle->hide();
       
   150     }
       
   151 
       
   152     if (!mSelectionStartTouchArea) {
       
   153         mSelectionStartTouchArea = new HbTouchArea(q);
       
   154         mSelectionStartTouchArea->hide();
       
   155         HbStyle::setItemName(mSelectionStartTouchArea, "handle-toucharea");
       
   156 
       
   157     }
       
   158 
       
   159     if (!mSelectionEndTouchArea) {
       
   160         mSelectionEndTouchArea = new HbTouchArea(q);
       
   161         mSelectionEndTouchArea->hide();
       
   162         HbStyle::setItemName(mSelectionEndTouchArea, "handle-toucharea");
       
   163     }
       
   164 }
       
   165 
       
   166 /*
       
   167    Updates given handle associated with handleTouchArea to place it
       
   168    newHandlePos in the selected text.
       
   169    handlePrimitive identifies handle graphics.
       
   170 */
       
   171 void HbSelectionControlPrivate::updateHandle(int newHandlePos,
       
   172                   Qt::AlignmentFlag handleAlignment,
       
   173                   QGraphicsItem *handle,
       
   174                   QGraphicsItem *handleTouchArea,
       
   175                   HbStyle::Primitive handlePrimitive)
       
   176 {    
       
   177     Q_Q(HbSelectionControl);
       
   178 
       
   179     HbStyleOption option;
       
   180 
       
   181     q->initStyleOption(&option);
       
   182     mEdit->style()->updatePrimitive(handle, handlePrimitive, &option);
       
   183 
       
   184     QRectF rect = mEdit->rectForPosition(newHandlePos,Qt::AlignTop?QTextLine::Leading:QTextLine::Trailing);
       
   185 
       
   186     // Convert rect to handle's parent coordinate system
       
   187     rect = handle->parentItem()->mapRectFromItem(mEdit,rect);
       
   188 
       
   189     // Center handle around center point of rect
       
   190     QRectF boundingRect = handle->boundingRect();
       
   191 
       
   192     boundingRect.moveCenter(rect.center());
       
   193 
       
   194     if (handleAlignment == Qt::AlignTop) {
       
   195        boundingRect.moveBottom(rect.top());
       
   196     } else {
       
   197        boundingRect.moveTop(rect.bottom());
       
   198     }
       
   199 
       
   200     handle->setPos(boundingRect.topLeft());
       
   201 
       
   202     // Center handle touch area around center pos of handle
       
   203     QPointF centerPos = boundingRect.center();
       
   204     boundingRect = handleTouchArea->boundingRect();
       
   205     boundingRect.moveCenter(centerPos);
       
   206     handleTouchArea->setPos(boundingRect.topLeft());
       
   207 
       
   208     if (!mPanInProgress) {
       
   209         QGraphicsItem * newParent = reparent(handle);
       
   210         reparent(handleTouchArea, newParent);
       
   211     }
       
   212 
       
   213     handle->show();
       
   214     handleTouchArea->show() ;
       
   215 }
       
   216 
       
   217 
       
   218 
       
   219 /*
       
   220    Reparents item to q if item's bounding rect intersects mEdit bounding rectangle or otherwise to
       
   221    HbAbstractEditPrivate::d_ptr(d->mEdit)->canvas.
       
   222    Returns new parent.
       
   223 */
       
   224 QGraphicsItem * HbSelectionControlPrivate::reparent(QGraphicsItem *item)
       
   225 {
       
   226     Q_Q(HbSelectionControl);
       
   227 
       
   228     QGraphicsItem *newParent = HbAbstractEditPrivate::d_ptr(mEdit)->canvas;
       
   229 
       
   230     // Convert bounding rect to mEdit's coordinate system
       
   231     QRectF rect = item->boundingRect();
       
   232     rect = item->mapRectToItem(mEdit,rect);
       
   233 
       
   234     if (mEdit->contains(rect.topLeft()) || mEdit->contains(rect.bottomRight())) {
       
   235         newParent = q;
       
   236     }
       
   237 
       
   238     reparent(item, newParent);
       
   239     return newParent;
       
   240 }
       
   241 
       
   242 void HbSelectionControlPrivate::reparent(QGraphicsItem *item, QGraphicsItem *newParent)
       
   243 {
       
   244     if (item && newParent && newParent != item->parentItem()) {
       
   245 
       
   246         // Reparent handle items to newParent
       
   247         QPointF pos = newParent->mapFromItem(item->parentItem(),item->pos());
       
   248 
       
   249         // TODO: This is a workaround for a Qt bug when reparenting from a clipping parent to a
       
   250         //       non-clipping parent
       
   251         item->setParentItem(0);
       
   252         item->setParentItem(newParent);
       
   253         item->setPos(pos);
       
   254     }
       
   255 }
       
   256 
       
   257 void HbSelectionControlPrivate::reparentHandles(QGraphicsItem *newParent)
       
   258 {
       
   259     // Reparent handle items to newParent
       
   260     reparent(mSelectionStartHandle, newParent);
       
   261     reparent(mSelectionStartTouchArea, newParent);
       
   262     reparent(mSelectionEndHandle, newParent);
       
   263     reparent(mSelectionEndTouchArea, newParent);
       
   264 }
       
   265 
       
   266 
       
   267 HbSelectionControl::HbSelectionControl(HbAbstractEdit *edit) :
       
   268     HbPopup(*new HbSelectionControlPrivate(edit),0)
       
   269 
       
   270 {
       
   271     Q_D(HbSelectionControl);
       
   272     d->q_ptr = this;
       
   273     d->init();
       
   274     //TODO: selection control could be singleton per main window
       
   275     //      since only one selection control is used at a time
       
   276 }
       
   277 
       
   278 
       
   279 void HbSelectionControl::updatePrimitives()
       
   280 {
       
   281     Q_D(HbSelectionControl);
       
   282     if (!d->mHandlesDisabled && d->polished) {
       
   283         if (d->mEdit->textCursor().hasSelection() ||
       
   284             (!d->mEdit->textCursor().hasSelection() && (d->mPressed == SelectionStartHandle || d->mPressed == SelectionEndHandle))) {
       
   285 
       
   286             int selStartPos = qMin(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
       
   287             int selEndPos = qMax(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
       
   288 
       
   289             d->updateHandle(selStartPos,Qt::AlignTop,d->mSelectionStartHandle,d->mSelectionStartTouchArea,HbStyle::P_SelectionControl_selectionstart);
       
   290             d->updateHandle(selEndPos,Qt::AlignBottom,d->mSelectionEndHandle,d->mSelectionEndTouchArea,HbStyle::P_SelectionControl_selectionend);
       
   291         }
       
   292         else {
       
   293             d->mSelectionStartHandle->hide();
       
   294             d->mSelectionStartTouchArea->hide() ;
       
   295             d->mSelectionEndHandle->hide();
       
   296             d->mSelectionEndTouchArea->hide() ;
       
   297         }
       
   298     }
       
   299 }
       
   300 
       
   301 void HbSelectionControl::hideHandles()
       
   302 {
       
   303     Q_D(HbSelectionControl);
       
   304     if (!d->mHandlesDisabled) {
       
   305         d->mHandlesDisabled = true;
       
   306         hide();
       
   307         d->reparentHandles(this);
       
   308     }
       
   309 }
       
   310 
       
   311 void HbSelectionControl::showHandles()
       
   312 {
       
   313     Q_D(HbSelectionControl);
       
   314     if (d->mHandlesDisabled) {
       
   315         d->mHandlesDisabled = false;
       
   316         show();
       
   317     }
       
   318 }
       
   319 
       
   320 void HbSelectionControl::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
       
   321 {
       
   322     Q_D(HbSelectionControl);
       
   323 
       
   324     QPointF editPos = d->mEdit->mapFromScene(event->scenePos());
       
   325 
       
   326     QRectF handleRect = d->mSelectionStartHandle->boundingRect();
       
   327     handleRect.moveTopLeft(editPos + d->mMouseOffset);
       
   328 
       
   329     QPointF hitTestPos = handleRect.center();
       
   330 
       
   331     if (d->mPressed == SelectionStartHandle) {
       
   332         hitTestPos.setY(handleRect.bottom()+1);
       
   333     } else {
       
   334         hitTestPos.setY(handleRect.top()-1);
       
   335     }
       
   336 
       
   337     // Hit test for the center of current selection touch area
       
   338     int hitPos = HbAbstractEditPrivate::d_ptr(d->mEdit)->hitTest(hitTestPos,Qt::FuzzyHit);
       
   339     if (hitPos == -1) {
       
   340         return;
       
   341     }
       
   342 
       
   343     QTextCursor cursor = d->mEdit->textCursor();
       
   344 
       
   345     if (hitPos != cursor.position()) {
       
   346         d->mHandlesMoved = true;
       
   347     }
       
   348     cursor.setPosition(hitPos, QTextCursor::KeepAnchor);
       
   349     if (d->mHandlesMoved) {
       
   350         if (d->mEdit) {
       
   351             HbWidgetFeedback::triggered(d->mEdit, Hb::InstantDraggedOver);
       
   352         }
       
   353         // Restart timer every time when a selection handle moved
       
   354         d->mWordSnapTimer.start(SNAP_DELAY, this);
       
   355         d->mEdit->setTextCursor(cursor);
       
   356     }
       
   357 
       
   358     // Ensure that the hitPos is visible
       
   359     HbAbstractEditPrivate::d_ptr(d->mEdit)->ensurePositionVisible(hitPos);
       
   360     updatePrimitives();
       
   361 }
       
   362 
       
   363 void HbSelectionControl::mousePressEvent (QGraphicsSceneMouseEvent *event)
       
   364 {
       
   365     Q_D(HbSelectionControl);
       
   366 
       
   367     if (d->mEdit) {
       
   368         HbWidgetFeedback::triggered(d->mEdit, Hb::InstantPressed);
       
   369     }
       
   370 
       
   371     d->mPressed = DummyHandle;
       
   372 
       
   373     // Find out which handle is being moved
       
   374     if (d->mSelectionStartTouchArea->contains(mapToItem(d->mSelectionStartTouchArea, event->pos()))) {
       
   375         d->mPressed = SelectionStartHandle;
       
   376         d->mMouseOffset = d->mSelectionStartHandle->pos() - event->pos();
       
   377     }
       
   378     if (d->mSelectionEndTouchArea->contains(mapToItem(d->mSelectionEndTouchArea, event->pos()))) {
       
   379         bool useArea = true;
       
   380         if(d->mPressed != DummyHandle) {
       
   381 
       
   382             // The press point was inside in both of the touch areas
       
   383             // choose the touch area whose center is closer to the press point
       
   384             QRectF rect = d->mSelectionStartTouchArea->boundingRect();
       
   385             rect.moveTopLeft(d->mSelectionStartTouchArea->pos());
       
   386             QLineF  lineEventPosSelStartCenter(event->pos(),rect.center());
       
   387 
       
   388             rect = d->mSelectionEndTouchArea->boundingRect();
       
   389             rect.moveTopLeft(d->mSelectionEndTouchArea->pos());
       
   390             QLineF  lineEventPosSelEndCenter(event->pos(),rect.center());
       
   391 
       
   392             if (lineEventPosSelStartCenter.length() < lineEventPosSelEndCenter.length()) {
       
   393                 useArea = false;
       
   394             }
       
   395         }
       
   396         if (useArea) {
       
   397             d->mPressed = SelectionEndHandle;
       
   398             d->mMouseOffset = d->mSelectionEndHandle->pos() - event->pos();
       
   399         }
       
   400     }
       
   401 
       
   402     if (d->mPressed == DummyHandle) {
       
   403         // Hit is outside touch areas, ignore
       
   404         event->ignore();
       
   405         return;
       
   406     }
       
   407 
       
   408     // Position cursor at the pressed selection handle
       
   409 
       
   410     QTextCursor cursor = d->mEdit->textCursor();
       
   411     int selStartPos = qMin(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
       
   412     int selEndPos = qMax(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
       
   413 
       
   414     if (d->mPressed == SelectionStartHandle) {
       
   415         cursor.setPosition(selEndPos);
       
   416         cursor.setPosition(selStartPos, QTextCursor::KeepAnchor);
       
   417     } else {
       
   418         cursor.setPosition(selStartPos);
       
   419         cursor.setPosition(selEndPos, QTextCursor::KeepAnchor);
       
   420     }
       
   421     d->mEdit->setTextCursor(cursor);
       
   422 
       
   423 }
       
   424 
       
   425 void HbSelectionControl::mouseReleaseEvent (QGraphicsSceneMouseEvent *event)
       
   426 {
       
   427     Q_D(HbSelectionControl);
       
   428     Q_UNUSED(event);
       
   429 
       
   430     if (d->mEdit) {
       
   431         HbWidgetFeedback::triggered(d->mEdit, Hb::InstantReleased);
       
   432     }
       
   433 
       
   434     if (d->mWordSnapTimer.isActive()) {
       
   435 
       
   436         // Snap selection to word beginning or end
       
   437         QTextCursor cursor = d->mEdit->textCursor();
       
   438         int curPos = d->mEdit->textCursor().position();
       
   439         int anchPos = d->mEdit->textCursor().anchor();
       
   440         cursor.select(QTextCursor::WordUnderCursor);
       
   441 
       
   442         // Snap direction depends on cursor position
       
   443         curPos = ((curPos > anchPos)?cursor.position():cursor.anchor());
       
   444 
       
   445         cursor.setPosition(anchPos);
       
   446         cursor.setPosition(curPos, QTextCursor::KeepAnchor);
       
   447         d->mEdit->setTextCursor(cursor);
       
   448     }
       
   449 
       
   450     d->mPressed = DummyHandle;
       
   451     updatePrimitives();
       
   452 
       
   453     if (!d->mHandlesMoved) {
       
   454         if (d->mEdit->contextMenuFlags().testFlag(Hb::ShowTextContextMenuOnSelectionClicked)) {
       
   455             d->mEdit->showContextMenu(event->scenePos());
       
   456         }
       
   457     }
       
   458     d->mHandlesMoved = false;
       
   459 }
       
   460 
       
   461 void HbSelectionControl::panStarted()
       
   462 {
       
   463     Q_D(HbSelectionControl);
       
   464 
       
   465     if (!d->mHandlesDisabled) {
       
   466         d->mPanInProgress = true;
       
   467         // Reparent handle items to editor canvas on pan start
       
   468         d->reparentHandles(HbAbstractEditPrivate::d_ptr(d->mEdit)->canvas);
       
   469     }
       
   470 }
       
   471 
       
   472 void HbSelectionControl::panFinished()
       
   473 {
       
   474     Q_D(HbSelectionControl);
       
   475 
       
   476     if (!d->mHandlesDisabled) {
       
   477         d->mPanInProgress = false;
       
   478         updatePrimitives();
       
   479     }
       
   480 }
       
   481 
       
   482 
       
   483 void HbSelectionControl::timerEvent(QTimerEvent *event)
       
   484 {
       
   485     Q_D(HbSelectionControl);
       
   486 
       
   487     if (event->timerId() == d->mWordSnapTimer.timerId()) {
       
   488         d->mWordSnapTimer.stop();
       
   489     }
       
   490 }
       
   491 
       
   492 void HbSelectionControl::polish( HbStyleParameters& params )
       
   493 {
       
   494     Q_D(HbSelectionControl);
       
   495 
       
   496     HbPopup::polish(params);
       
   497     QSizeF size = d->mSelectionStartTouchArea->preferredSize();
       
   498     d->mSelectionStartTouchArea->resize(size);
       
   499     d->mSelectionEndTouchArea->resize(size);
       
   500 }
       
   501 
       
   502 QVariant HbSelectionControl::itemChange(GraphicsItemChange change, const QVariant &value)
       
   503 {
       
   504     if (change == QGraphicsItem::ItemPositionChange) {
       
   505         return qVariantFromValue(QPointF(0,0));
       
   506     } else if (change == QGraphicsItem::ItemVisibleChange) {        
       
   507         updatePrimitives();
       
   508     }
       
   509 
       
   510     return HbPopup::itemChange(change, value);
       
   511 }