src/hbwidgets/editors/hbselectioncontrol_p.cpp
changeset 34 ed14f46c0e55
parent 7 923ff622b8b9
--- a/src/hbwidgets/editors/hbselectioncontrol_p.cpp	Mon Oct 04 17:49:30 2010 +0300
+++ b/src/hbwidgets/editors/hbselectioncontrol_p.cpp	Mon Oct 18 18:23:13 2010 +0300
@@ -40,17 +40,23 @@
 #include "hbdialog_p.h"
 #include "hbabstractedit.h"
 #include "hbabstractedit_p.h"
+#include "hblineedit.h"
 #include "hbtoucharea.h"
 #include "hbpangesture.h"
 #include "hbtapgesture.h"
 #include "hbevent.h"
 #include "hbpopup.h"
+#include "hbmagnifier_p.h"
 #include "hbnamespace_p.h"
 #include "hbmainwindow.h"
+#include "hbdeviceprofile.h"
+#include "hbiconitem.h"
 
 
 #include <QTextCursor>
+#include <QTextBlock>
 #include <QGraphicsItem>
+#include <QGraphicsWidget>
 #include <QAbstractTextDocumentLayout>
 #include <QGraphicsSceneMouseEvent>
 #include <QBasicTimer>
@@ -61,69 +67,185 @@
 
 #include <hbwidgetfeedback.h>
 
+
+#define HB_DEBUG_PAINT_INFO 0
+
 typedef QHash<HbMainWindow*,HbSelectionControl*> HbSelectionControlHash;
 Q_GLOBAL_STATIC(HbSelectionControlHash, globalSelectionControlHash)
 
 namespace {
     static const int SNAP_DELAY = 300;
+    static const int MAGNIFIER_OPEN_DELAY = 200;
+    static const qreal MAGNIFIER_SCALE_FACTOR = 1.5;
 }
 
+class MyEditor: public HbAbstractEdit
+{
+public:
+    using HbAbstractEdit::drawContents;
+};
+
+
+class HbMagnifierDelegateEditor : public HbMagnifierDelegate
+{
+public:
+    HbMagnifierDelegateEditor(HbAbstractEdit * editor) {this->editor = editor;}
+    virtual void drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option)
+    {
+        if (option && editor) {
+            static_cast<MyEditor*>(editor)->drawContents(painter,*option);
+        }
+    }
+    void setEditor(HbAbstractEdit * editor){this->editor = editor;}
+private:
+    HbAbstractEdit * editor;
+};
+
 
 class HbSelectionControlPrivate :public HbDialogPrivate
 {
     Q_DECLARE_PUBLIC(HbSelectionControl)
 
 public:
+    enum InteractionMode {
+        Selection,
+        CursorPositioning
+    };
+
+    enum HandleType {
+        DummyHandle,
+        SelectionStartHandle,
+        SelectionEndHandle
+    };
+
+
     HbSelectionControlPrivate();
+    ~HbSelectionControlPrivate();
     void init();
     void createPrimitives();
-    void updateHandle(int newHandlePos,
+    void updateHandle(int cursorPos,
+                      int newHandlePos,
                       Qt::AlignmentFlag handleAlignment,
                       QGraphicsItem *handle,
-                      QGraphicsItem *handleTouchArea,
-                      HbStyle::Primitive handlePrimitive);
+                      QGraphicsItem *handleTouchArea);
+    void updateMagnifier();
+    void initMagnifier();
+
     QGraphicsItem * reparent(QGraphicsItem *item);
     void reparent(QGraphicsItem *item, QGraphicsItem *newParent);
     void reparentHandles(QGraphicsItem *newParent);
-    void tapGestureFinished (const QPointF& point);
+    QPointF constrainHitTestPoint(const QPointF& point);
+    HbSelectionControlPrivate::HandleType handleForPoint(const QPointF& point);
+    void gestureStarted(const QPointF &point);
+    void tapGestureStarted(HbTapGesture *gesture);
+    void tapGestureFinished();
+    void delayedTapFinished();
     void panGestureStarted (HbPanGesture *gesture);
     void panGestureUpdated (HbPanGesture *gesture);
     void panGestureFinished (HbPanGesture *gesture);
+    void panGestureCanceled();
     void show();
     void _q_aboutToChangeView();
     void detachEditor(bool updateAtthachedEditorState);
 
+#if HB_DEBUG_PAINT_INFO
+    void updateDebugPaintInfo();
+#endif
+
+
 public:
 
     HbAbstractEdit *mEdit;
-    QGraphicsItem *mTopLevelAncestor;
+    HbMagnifierDelegateEditor *mMagnifierDelegate; // Owned by mMagnifier
+    HbMagnifier* mMagnifier;
 
-    QGraphicsItem *mSelectionStartHandle;
-    QGraphicsItem *mSelectionEndHandle;
+    QGraphicsItem *mTopLevelAncestor;    
+    // The offset between the gesture start point and first hit test point
+    QPointF mTouchOffsetFromHitTestPoint;
+
+    HbIconItem *mSelectionStartHandle;
+    HbIconItem *mSelectionEndHandle;
     HbTouchArea* mSelectionStartTouchArea;
     HbTouchArea* mSelectionEndTouchArea;
 
-    HbSelectionControl::HandleType mPressed;
-    bool mPanInProgress;
+    InteractionMode mInteractionMode;
+    HandleType mPressed;
+    bool mScrollInProgress;
+    QPointF mHitTestPoint;                 // HbSelectionControl's coordinate system
+    QPointF mTouchPoint;                   // HbSelectionControl's coordinate system
+    QPointF mStartHandleHitTestPoint;      // HbSelectionControl's coordinate system
+    QPointF mEndHandleHitTestPoint;        // HbSelectionControl's coordinate system
     QBasicTimer mWordSnapTimer;
+    QBasicTimer mDelayedTapTimer;
+
+    bool mMagnifierEnabled;
+    qreal mLastCursorHeight;
+    qreal mHandleMarginFromLine;
+    qreal mMagnifierMarginFromLine;
+    qreal mMagnifierLeftRightMarginFromHandle;
+    qreal mMagnifierMaxDescent;
+    qreal mVerticalScreenMargin;
+    qreal mTouchYOffsetFromMagnifierReferenceLine; // HbSelectionControl's coordinate system
+    qreal mStartHandleMagnifierReferenceLine;     // HbSelectionControl's coordinate system
+    qreal mEndHandleMagnifierReferenceLine;       // HbSelectionControl's coordinate system
+    qreal mMagnifierMaxYOffsetFromTouchPoint;
+    qreal mMagnifierMinYOffsetFromTouchPoint;
+    qreal mMaxHitTestPointOffsetYFromLine;
+    bool mIsPanActive;
+
+#if HB_DEBUG_PAINT_INFO
+    QRectF  mDocRectDebug;      // HbSelectionControl's coordinate system
+#endif
 };
 
 
 HbSelectionControlPrivate::HbSelectionControlPrivate():
     mEdit(0),
+    mMagnifierDelegate(0),
+    mMagnifier(0),
     mTopLevelAncestor(0),
     mSelectionStartHandle(0),
     mSelectionEndHandle(0),
     mSelectionStartTouchArea(0),
     mSelectionEndTouchArea(0),
-    mPressed(HbSelectionControl::HandleType(0)),
-    mPanInProgress(false)
+    mInteractionMode(InteractionMode(0)),
+    mPressed(HandleType(0)),
+    mScrollInProgress(false),
+    mMagnifierEnabled(true),
+    mLastCursorHeight(0.0),
+    mHandleMarginFromLine(0.0),
+    mMagnifierMarginFromLine(0.0),
+    mMagnifierLeftRightMarginFromHandle(0.0),
+    mMagnifierMaxDescent(0.0),
+    mVerticalScreenMargin(0.0),
+    mTouchYOffsetFromMagnifierReferenceLine(0.0),
+    mStartHandleMagnifierReferenceLine(0.0),
+    mEndHandleMagnifierReferenceLine(0.0),
+    mMagnifierMaxYOffsetFromTouchPoint(0.0),
+    mMagnifierMinYOffsetFromTouchPoint(0.0),
+    mMaxHitTestPointOffsetYFromLine(0.0),
+    mIsPanActive(false)
 {    
 }
 
+HbSelectionControlPrivate::~HbSelectionControlPrivate()
+{
+}
+
 void HbSelectionControlPrivate::init()
 {
     Q_Q(HbSelectionControl);
+
+    // Set the size of the control to 0
+    q->resize(0,0);
+
+#if HB_DEBUG_PAINT_INFO
+    // Override 0 size to be able to paint paint
+    q->resize(1,1);
+#endif
+
+    q->style()->parameter(QLatin1String("hb-param-margin-gene-middle-vertical"), mVerticalScreenMargin);
+
     createPrimitives();
 
     q->setVisible(false);
@@ -136,23 +258,25 @@
     q->setFlags(itemFlags);
     q->setFocusPolicy(Qt::NoFocus);
     q->setActive(false);
-
-    // Control will handle all events going to different handlers.
-    q->setHandlesChildEvents(true);
 }
 
 void HbSelectionControlPrivate::createPrimitives()
 {
     Q_Q(HbSelectionControl);
     if (!mSelectionStartHandle) {
-        mSelectionStartHandle = q->style()->createPrimitive(HbStyle::P_SelectionControl_selectionstart, q);
+
+        mSelectionStartHandle = new HbIconItem(q);
+        mSelectionStartHandle->setIconName(QLatin1String("qtg_graf_editor_handle_begin"));
+        HbStyle::setItemName(mSelectionStartHandle, QLatin1String("handle-icon"));
         mSelectionStartHandle->setFlag(QGraphicsItem::ItemIsPanel);
         mSelectionStartHandle->setFlag(QGraphicsItem::ItemIsFocusable,false);
         mSelectionStartHandle->setActive(false);
     }
 
     if (!mSelectionEndHandle) {
-        mSelectionEndHandle = q->style()->createPrimitive(HbStyle::P_SelectionControl_selectionend, q);
+        mSelectionEndHandle = new HbIconItem(q);
+        mSelectionEndHandle->setIconName(QLatin1String("qtg_graf_editor_handle_end"));
+        HbStyle::setItemName(mSelectionEndHandle, QLatin1String("handle-icon"));
         mSelectionEndHandle->setFlag(QGraphicsItem::ItemIsPanel);
         mSelectionEndHandle->setFlag(QGraphicsItem::ItemIsFocusable,false);
         mSelectionEndHandle->setActive(false);
@@ -163,9 +287,10 @@
         mSelectionStartTouchArea->setFlag(QGraphicsItem::ItemIsPanel);
         mSelectionStartTouchArea->setFlag(QGraphicsItem::ItemIsFocusable,false);
         mSelectionStartTouchArea->setActive(false);
-        HbStyle::setItemName(mSelectionStartTouchArea, "handle-toucharea");
+        HbStyle::setItemName(mSelectionStartTouchArea, QLatin1String("handle-toucharea"));
         mSelectionStartTouchArea->grabGesture(Qt::TapGesture);
         mSelectionStartTouchArea->grabGesture(Qt::PanGesture);
+        mSelectionStartTouchArea->installEventFilter(q);
     }
 
     if (!mSelectionEndTouchArea) {
@@ -173,31 +298,60 @@
         mSelectionEndTouchArea->setFlag(QGraphicsItem::ItemIsPanel);
         mSelectionEndTouchArea->setFlag(QGraphicsItem::ItemIsFocusable,false);
         mSelectionEndTouchArea->setActive(false);
-        HbStyle::setItemName(mSelectionEndTouchArea, "handle-toucharea");
+        HbStyle::setItemName(mSelectionEndTouchArea, QLatin1String("handle-toucharea"));
         mSelectionEndTouchArea->grabGesture(Qt::TapGesture);
         mSelectionEndTouchArea->grabGesture(Qt::PanGesture);
+        mSelectionEndTouchArea->installEventFilter(q);
+    }
+    if(!mMagnifier) {
+        mMagnifier = new HbMagnifier(q);
+        mMagnifier->setFlag(QGraphicsItem::ItemIsPanel);
+        mMagnifier->setFlag(QGraphicsItem::ItemIsFocusable,false);
+        mMagnifier->setActive(false);
+        HbStyle::setItemName(mMagnifier, QLatin1String("magnifier"));
+        mMagnifierDelegate = new HbMagnifierDelegateEditor(mEdit);
+        mMagnifier->setContentDelegate(mMagnifierDelegate);
+        mMagnifier->hide();
+        initMagnifier();
     }
 }
 
 /*
    Updates given handle associated with handleTouchArea to place it
    newHandlePos in the selected text.
-   handlePrimitive identifies handle graphics.
 */
-void HbSelectionControlPrivate::updateHandle(int newHandlePos,
+void HbSelectionControlPrivate::updateHandle(
+                  int cursorPos,
+                  int newHandlePos,
                   Qt::AlignmentFlag handleAlignment,
                   QGraphicsItem *handle,
-                  QGraphicsItem *handleTouchArea,
-                  HbStyle::Primitive handlePrimitive)
+                  QGraphicsItem *handleTouchArea)
 {    
     Q_Q(HbSelectionControl);
 
-    HbStyleOption option;
+    QRectF rect = mEdit->rectForPosition(newHandlePos,(Qt::AlignTop||mInteractionMode == CursorPositioning)?
+                                                       QTextLine::Leading:QTextLine::Trailing);
+    qreal lineHeight = rect.height();
+
+    if(cursorPos == newHandlePos) {
+        mLastCursorHeight = lineHeight;
+    }
+
+    // Store the current hit test point for the given handle
+    // Convert rect to HbSelectionControl's coordinate system
+    QRectF rectInSelectionControlCoord = q->mapRectFromItem(mEdit,rect);
 
-    q->initStyleOption(&option);
-    mEdit->style()->updatePrimitive(handle, handlePrimitive, &option);
-
-    QRectF rect = mEdit->rectForPosition(newHandlePos,Qt::AlignTop?QTextLine::Leading:QTextLine::Trailing);
+    if (handleAlignment == Qt::AlignTop) {
+        mStartHandleHitTestPoint = rectInSelectionControlCoord.center();
+        mStartHandleHitTestPoint.setY(qMin(mStartHandleHitTestPoint.y(),
+                                           rectInSelectionControlCoord.top()+mMaxHitTestPointOffsetYFromLine));
+        mStartHandleMagnifierReferenceLine = rectInSelectionControlCoord.top();
+    } else {
+        mEndHandleHitTestPoint = rectInSelectionControlCoord.center();
+        mEndHandleHitTestPoint.setY(qMax(mEndHandleHitTestPoint.y(),
+                                         rectInSelectionControlCoord.bottom()-mMaxHitTestPointOffsetYFromLine));
+        mEndHandleMagnifierReferenceLine = rectInSelectionControlCoord.bottom();
+    }
 
     // Convert rect to handle's parent coordinate system
     rect = handle->parentItem()->mapRectFromItem(mEdit,rect);
@@ -205,29 +359,132 @@
     // Center handle around center point of rect
     QRectF boundingRect = handle->boundingRect();
 
+    bool positionHandlesTouchEachOther = (boundingRect.height()*2 > lineHeight);
+
     boundingRect.moveCenter(rect.center());
 
+    // Position handle either on top of rect using margin mHandleMarginFromLine or so that
+    // the two handles touch eachother
     if (handleAlignment == Qt::AlignTop) {
-       boundingRect.moveBottom(rect.top());
+        if (positionHandlesTouchEachOther) {
+            boundingRect.moveBottom(rect.center().y());
+        } else {
+            boundingRect.moveTop(rect.top()-mHandleMarginFromLine);
+        }
     } else {
-       boundingRect.moveTop(rect.bottom());
+        if (positionHandlesTouchEachOther) {
+            boundingRect.moveTop(rect.center().y());
+        } else {
+            boundingRect.moveBottom(rect.bottom()+mHandleMarginFromLine);
+        }
     }
 
     handle->setPos(boundingRect.topLeft());
 
-    // Center handle touch area around center pos of handle
+    // Position handle touch area around center-top of handle
     QPointF centerPos = boundingRect.center();
+    rect = boundingRect;
     boundingRect = handleTouchArea->boundingRect();
     boundingRect.moveCenter(centerPos);
+
+    if (handleAlignment != Qt::AlignTop) {
+        boundingRect.moveTop(rect.top());
+    }
+
     handleTouchArea->setPos(boundingRect.topLeft());
 
-    if (!mPanInProgress) {
+    if (!mScrollInProgress) {
         QGraphicsItem * newParent = reparent(handle);
         reparent(handleTouchArea, newParent);
     }
 }
 
 
+void HbSelectionControlPrivate::updateMagnifier()
+{
+    Q_Q(HbSelectionControl);
+
+    if (mMagnifier->isVisible()) {
+        QPointF canvasHitTestPos = q->mapToItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas,mHitTestPoint);
+        QRectF canvasCursorRect = HbAbstractEditPrivate::d_ptr(mEdit)->rectForPositionInCanvasCoords(mEdit->textCursor().position(),QTextLine::Leading);
+        QPointF centerOfMagnification = canvasCursorRect.center();
+        centerOfMagnification.setX(canvasHitTestPos.x());
+        canvasCursorRect.moveCenter(centerOfMagnification);
+
+        // Set the visible content to be magnified
+        qreal magnifierHeight = mMagnifier->boundingRect().height();
+
+        // Check if the line fits vertically to the magnifier if not align the bottom of the line with the bottom of the
+        // magnifier constraining a maximum line descent.
+        if (magnifierHeight < canvasCursorRect.height()*mMagnifier->contentScale()) {
+            const QTextCursor cursor = mEdit->textCursor();
+            const QTextBlock block = cursor.block();
+            const int positionInBlock = cursor.position() - block.position();
+            QTextLine line = block.layout()->lineForTextPosition(positionInBlock);
+            qreal lineDescent = line.descent();
+            qreal lineBottom  = canvasCursorRect.bottom();
+            if (mMagnifierMaxDescent < lineDescent) {
+                lineBottom-= lineDescent-mMagnifierMaxDescent;
+            }
+            // Compensate for rounding error by adding +1
+            centerOfMagnification.setY(lineBottom-magnifierHeight/(2*mMagnifier->contentScale())+1);
+        }
+
+        mMagnifier->centerOnContent(centerOfMagnification);
+
+        // -- Position magnifier --
+
+        // Convert cursorRect to HbSelectionControl's coordinate system
+        QRectF rect = q->mapRectFromItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas,canvasCursorRect);
+
+        QRectF boundingRect = mMagnifier->boundingRect();
+        boundingRect.moveCenter(rect.center());
+
+        const qreal KMagnifierBottomUpperBound = rect.top()-mMagnifierMarginFromLine+mTouchYOffsetFromMagnifierReferenceLine;
+        const qreal KMagnifierBottomLowerBound = rect.bottom()-mMagnifierMarginFromLine+mTouchYOffsetFromMagnifierReferenceLine;
+
+        qreal newMagnifierBottom = (mPressed == SelectionStartHandle?KMagnifierBottomUpperBound:KMagnifierBottomLowerBound);
+
+        // -- Constrain vertical position --
+
+        // Constrain maximum distance of bottom of the magnifier from touch point
+        newMagnifierBottom = qMax(newMagnifierBottom,mTouchPoint.y()-mMagnifierMaxYOffsetFromTouchPoint);
+
+        // Constrain minimum distance of bottom of the magnifier from touch point
+        newMagnifierBottom = qMin(newMagnifierBottom,mTouchPoint.y()-mMagnifierMinYOffsetFromTouchPoint);
+
+        // Constrain the bottom of the magnifier to be within the feasible bounds
+        newMagnifierBottom = qMin(qMax(newMagnifierBottom,KMagnifierBottomUpperBound),KMagnifierBottomLowerBound);
+
+        boundingRect.moveBottom(newMagnifierBottom);
+        boundingRect.moveTop(qMax(mVerticalScreenMargin,boundingRect.top()));
+
+        // Readjust magnifier position if there is not enough space at the top of the screen
+        if (mTouchPoint.y()-mMagnifierMinYOffsetFromTouchPoint < boundingRect.bottom()) {
+
+            boundingRect.moveRight(mTouchPoint.x()-mMagnifierLeftRightMarginFromHandle);
+
+            if(boundingRect.left() < 0) {
+                boundingRect.moveLeft(mTouchPoint.x()+mMagnifierLeftRightMarginFromHandle);
+            }
+        }
+        mMagnifier->setPos(boundingRect.topLeft());
+#if HB_DEBUG_PAINT_INFO
+    updateDebugPaintInfo();
+#endif
+    }
+
+}
+
+void HbSelectionControlPrivate::initMagnifier()
+{   
+    mMagnifier->setContentScale(MAGNIFIER_SCALE_FACTOR);
+    mMagnifier->setZValue(100); 
+    mMagnifier->setMask(QLatin1String("qtg_fr_editor_magnifier_mask"));
+    mMagnifier->setOverlay(QLatin1String("qtg_fr_editor_magnifier_overlay"));
+    updateMagnifier();
+}
+
 
 /*
    Reparents item to q if item's bounding rect intersects mEdit's viewPort rectangle or otherwise to
@@ -256,23 +513,23 @@
 
 void HbSelectionControlPrivate::reparent(QGraphicsItem *item, QGraphicsItem *newParent)
 {
-    Q_Q(HbSelectionControl);
-
     if (item && newParent && newParent != item->parentItem()) {
 
         // Reparent handle items to newParent
         QPointF pos = newParent->mapFromItem(item->parentItem(),item->pos());
 
-        // If the item is parented to other then q we have to
-        // turn off the QGraphicsItem::ItemIsPanel flag because
-        // otherwise the new parent loses its activeness.
-        bool enablePanel = (newParent == q);
+        // ----------
+        // Workaround for Qt bug: Missing repaint when QGraphicsItem is reparented to another
+        // item with flag QGraphicsItem::ItemClipsChildrenToShape is set.
+        // ----------
+        // Remove when the bug is fixed
+        if(item->isWidget()) {
+            if (qobject_cast<HbIconItem *>(static_cast<QGraphicsWidget *>(item))) {
+                item->scene()->invalidate(item->sceneBoundingRect());
+            }
+        }
+        // ----------------------------------------------------------------------------------
 
-        item->setFlag(QGraphicsItem::ItemIsPanel,enablePanel);
-
-        // TODO: This is a workaround for a Qt bug when reparenting from a clipping parent to a
-        //       non-clipping parent
-        item->setParentItem(0);
         item->setParentItem(newParent);
         item->setPos(pos);
     }
@@ -288,36 +545,58 @@
 }
 
 
-void HbSelectionControlPrivate::tapGestureFinished(const QPointF &pos)
-{
-    if (mEdit->contextMenuFlags().testFlag(Hb::ShowTextContextMenuOnSelectionClicked)) {
-        mEdit->showContextMenu(pos);
-    }
-}
-
-void HbSelectionControlPrivate::panGestureStarted(HbPanGesture *gesture)
+/*
+    Returns the constrained hit test point calculated from point.
+    point has to be in HbSelectionControl's coordinate system.
+    The returned pos is also in HbSelectionControl's coordinate system.
+*/
+QPointF HbSelectionControlPrivate::constrainHitTestPoint(const QPointF& point)
 {
     Q_Q(HbSelectionControl);
 
-    QPointF point = q->mapFromScene(gesture->sceneStartPos());
-    mPressed = HbSelectionControl::DummyHandle;
+    QRectF docRect = QRectF(q->mapFromItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas->parentItem(),
+                            HbAbstractEditPrivate::d_ptr(mEdit)->canvas->pos()),
+                            HbAbstractEditPrivate::d_ptr(mEdit)->doc->size());
+
+    // Constrain hitTestPos within docRect with mLastCursorHeight/2 top/bottom margins.
+    QPointF hitTestPos = QPointF(qMin(qMax(point.x(),docRect.left()),docRect.right()),
+                         qMin(qMax(point.y(),docRect.top()+mLastCursorHeight/2),docRect.bottom()-mLastCursorHeight/2));
+
+#if HB_DEBUG_PAINT_INFO
+    mDocRectDebug = docRect;
+#endif
+
+    return hitTestPos;
+}
+
+
+/*
+    Returns the handle whose touch area contains point.
+    point has to be in HbSelectionControl's coordinate system.
+*/
+HbSelectionControlPrivate::HandleType HbSelectionControlPrivate::handleForPoint(const QPointF& point) {
+
+    Q_Q(HbSelectionControl);
+
+    HandleType pressed = DummyHandle;
 
     // Find out which handle is being moved
-    if (mSelectionStartTouchArea->contains(q->mapToItem(mSelectionStartTouchArea, point))) {
-        mPressed = HbSelectionControl::SelectionStartHandle;
+    if (mSelectionStartTouchArea->isVisible() &&
+        mSelectionStartTouchArea->contains(q->mapToItem(mSelectionStartTouchArea, point))) {
+        pressed = SelectionStartHandle;
     }
     if (mSelectionEndTouchArea->contains(q->mapToItem(mSelectionEndTouchArea, point))) {
         bool useArea = true;
-        if(mPressed != HbSelectionControl::DummyHandle) {
+        if(pressed != DummyHandle) {
 
             // The press point was inside in both of the touch areas
             // choose the touch area whose center is closer to the press point
-            QRectF rect = mSelectionStartTouchArea->boundingRect();
-            rect.moveTopLeft(mSelectionStartTouchArea->pos());
+            QRectF rect = mSelectionStartHandle->boundingRect();
+            rect.moveTopLeft(mSelectionStartHandle->pos());
             QLineF  lineEventPosSelStartCenter(point,rect.center());
 
-            rect = mSelectionEndTouchArea->boundingRect();
-            rect.moveTopLeft(mSelectionEndTouchArea->pos());
+            rect = mSelectionEndHandle->boundingRect();
+            rect.moveTopLeft(mSelectionEndHandle->pos());
             QLineF  lineEventPosSelEndCenter(point,rect.center());
 
             if (lineEventPosSelStartCenter.length() < lineEventPosSelEndCenter.length()) {
@@ -325,22 +604,41 @@
             }
         }
         if (useArea) {
-            mPressed = HbSelectionControl::SelectionEndHandle;
+            pressed = SelectionEndHandle;
         }
     }
 
-    if (mPressed == HbSelectionControl::DummyHandle) {
+    return pressed;
+}
+
+void HbSelectionControlPrivate::gestureStarted(const QPointF &point)
+{
+    mPressed = handleForPoint(point);
+    mTouchPoint = point;
+
+    if (mPressed == DummyHandle) {
         // Hit is outside touch areas, ignore
         return;
     }
 
+    // Calculate touch offsets
+    mHitTestPoint = mStartHandleHitTestPoint;
+    qreal magnifierReferenceLine = mStartHandleMagnifierReferenceLine;
+    if (mPressed == SelectionEndHandle) {
+        mHitTestPoint = mEndHandleHitTestPoint;
+        magnifierReferenceLine = mEndHandleMagnifierReferenceLine;
+    }
+
+    mTouchYOffsetFromMagnifierReferenceLine = point.y() - magnifierReferenceLine;
+    mTouchOffsetFromHitTestPoint = mHitTestPoint - point;
+
     // Position cursor at the pressed selection handle
 
     QTextCursor cursor = mEdit->textCursor();
     int selStartPos = qMin(mEdit->textCursor().anchor(),mEdit->textCursor().position());
     int selEndPos = qMax(mEdit->textCursor().anchor(),mEdit->textCursor().position());
 
-    if (mPressed == HbSelectionControl::SelectionStartHandle) {
+    if (mPressed == SelectionStartHandle) {
         cursor.setPosition(selEndPos);
         cursor.setPosition(selStartPos, QTextCursor::KeepAnchor);
     } else {
@@ -348,7 +646,60 @@
         cursor.setPosition(selEndPos, QTextCursor::KeepAnchor);
     }
     mEdit->setTextCursor(cursor);
+}
 
+void HbSelectionControlPrivate::tapGestureStarted(HbTapGesture *gesture)
+{
+    Q_Q(HbSelectionControl);
+
+    mMagnifier->hideWithEffect();
+    if (!mDelayedTapTimer.isActive()) {
+        mDelayedTapTimer.start(MAGNIFIER_OPEN_DELAY,q);
+    }
+    QPointF point = q->mapFromScene(gesture->sceneStartPos());
+    gestureStarted(point);
+}
+
+void HbSelectionControlPrivate::tapGestureFinished()
+{
+    mDelayedTapTimer.stop();
+    if (!mIsPanActive) {
+        mSelectionStartTouchArea->show();
+        mSelectionStartHandle->show();
+        mMagnifier->hideWithEffect();
+    }
+}
+
+void HbSelectionControlPrivate::delayedTapFinished()
+{
+    Q_Q(HbSelectionControl);
+
+    // Reset gesture override to have enable more responsive pan
+    q->scene()->setProperty(HbPrivate::OverridingGesture.latin1(),QVariant());
+
+    if (mPressed == HbSelectionControlPrivate::SelectionEndHandle && mInteractionMode == HbSelectionControlPrivate::CursorPositioning) {
+        mSelectionStartTouchArea->hide();
+        mSelectionStartHandle->hide();
+    }
+    if (mMagnifierEnabled) {
+        mMagnifier->showWithEffect();
+        q->updatePrimitives();
+    }
+}
+
+
+
+void HbSelectionControlPrivate::panGestureStarted(HbPanGesture *gesture)
+{
+    Q_Q(HbSelectionControl);
+
+    mIsPanActive = true;
+    QPointF point = q->mapFromScene(gesture->sceneStartPos());
+    gestureStarted(point);
+    if (mPressed == SelectionEndHandle && mInteractionMode == HbSelectionControlPrivate::CursorPositioning) {
+        mSelectionStartTouchArea->hide();
+        mSelectionStartHandle->hide();
+    }
 }
 
 
@@ -373,8 +724,21 @@
         mEdit->setTextCursor(cursor);
     }
 
-    mPressed = HbSelectionControl::DummyHandle;
+    mSelectionStartTouchArea->show();
+    mSelectionStartHandle->show();
+    // This has to be set before updatePrimitives call
+    mIsPanActive = false;
     q->updatePrimitives();
+    mMagnifier->hideWithEffect();
+
+    // This has to be set at last
+    mPressed = DummyHandle;
+}
+
+void HbSelectionControlPrivate::panGestureCanceled()
+{
+    mMagnifier->hideWithEffect();
+    mIsPanActive = false;
 }
 
 
@@ -382,67 +746,53 @@
 {
     Q_Q(HbSelectionControl);
 
-    QRectF docRect = QRectF(mEdit->mapFromItem(HbAbstractEditPrivate::d_ptr(mEdit)->canvas->parentItem(),
-                            HbAbstractEditPrivate::d_ptr(mEdit)->canvas->pos()),
-                            HbAbstractEditPrivate::d_ptr(mEdit)->doc->size());
-    
-    QPointF editPos = mEdit->mapFromScene(gesture->sceneStartPos() + gesture->sceneOffset());
-    QPointF origEditPos = editPos;
-    bool outsideCanvas = !docRect.contains(origEditPos);
-
-    // Constrain editPos within docRect
-    editPos = QPointF(qMin(qMax(editPos.x(),docRect.left()),docRect.right()),
-                      qMin(qMax(editPos.y(),docRect.top()),docRect.bottom()));
-
-    QRectF handleRect = mSelectionStartHandle->boundingRect();
-
-    handleRect.moveCenter(editPos);
+    // Calculate new hittest point
+    QPointF point = q->mapFromScene(gesture->sceneStartPos() + gesture->sceneOffset());
+    mTouchPoint = point;
+    mHitTestPoint = (point + mTouchOffsetFromHitTestPoint);
+    mHitTestPoint = constrainHitTestPoint(mHitTestPoint);
 
-    // Set hitTestPos based on which handle was grabbed
-    QPointF hitTestPos = handleRect.center();
-
-    if (mPressed == HbSelectionControl::SelectionStartHandle) {
-        hitTestPos.setY(handleRect.bottom()+1);
-    } else {
-        hitTestPos.setY(handleRect.top()-1);
-    }
-
-    // Override hitTestPos if origEditPos was outside the canvas
-    if (outsideCanvas) {
-        if (origEditPos.y() < docRect.top()) {
-            hitTestPos.setY(handleRect.bottom()+1);
-        } else if (docRect.bottom() < origEditPos.y()) {
-            hitTestPos.setY(handleRect.top()-1);
-        }
-    }
+#if HB_DEBUG_PAINT_INFO
+    updateDebugPaintInfo();
+#endif
 
     QTextCursor cursor;
     cursor = mEdit->textCursor();
-    // Hit test for the center of current selection touch area
-    int hitPos = HbAbstractEditPrivate::d_ptr(mEdit)->hitTest(hitTestPos,Qt::FuzzyHit);
 
-    // if no valid hit pos or empty selection return
-    if (hitPos == -1 || hitPos == cursor.anchor()) {
+    // Hit test for the center of current selection touch area
+    int hitPos = HbAbstractEditPrivate::d_ptr(mEdit)->hitTest(q->mapToItem(mEdit,mHitTestPoint),Qt::FuzzyHit);
+
+    // if no valid hit pos or empty selection in read-only mode return
+    if (hitPos == -1 || (mEdit->isReadOnly() && hitPos == cursor.anchor() && mInteractionMode == Selection)) {
         return;
     }
 
-    bool handlesMoved(false);
+    bool isCursorMoved = false;
     if (hitPos != cursor.position()) {
-        handlesMoved = true;
+        isCursorMoved = true;
     }
-    cursor.setPosition(hitPos, QTextCursor::KeepAnchor);
-    if (handlesMoved) {
+
+    cursor.setPosition(hitPos, ((mInteractionMode==Selection||mPressed == SelectionStartHandle)
+                               ?QTextCursor::KeepAnchor:QTextCursor::MoveAnchor));
+
+    if (isCursorMoved) {
         if (mEdit) {
             HbWidgetFeedback::triggered(mEdit, Hb::InstantDraggedOver);
         }
-        // Restart timer every time when a selection handle moved
-        mWordSnapTimer.start(SNAP_DELAY, q);
+        if (mInteractionMode==Selection) {
+            // Restart timer every time when a selection handle moved
+            mWordSnapTimer.start(SNAP_DELAY, q);
+        }
         mEdit->setTextCursor(cursor);
     }
 
     // Ensure that the hitPos is visible
     HbAbstractEditPrivate::d_ptr(mEdit)->ensurePositionVisible(hitPos);
+    if (mMagnifierEnabled) {
+        mMagnifier->showWithEffect();
+    }
     q->updatePrimitives();
+
 }
 
 void HbSelectionControlPrivate::show() {
@@ -456,8 +806,9 @@
     }
 
     if (q->scene() != mEdit->scene() && mEdit->scene()) {
-        mEdit->scene()->addItem(q);
-    }
+        mEdit->scene()->addItem(q);    
+    }    
+
     q->show();    
     q->updatePrimitives();
 }
@@ -469,6 +820,7 @@
 
     if (mEdit && q->isVisible()) {
         mEdit->deselect();
+        q->hideHandles();
     }
 }
 
@@ -488,6 +840,16 @@
     }
 }
 
+#if HB_DEBUG_PAINT_INFO
+void HbSelectionControlPrivate::updateDebugPaintInfo()
+{
+    Q_Q(HbSelectionControl);
+    mEdit->update();
+    q->update();
+}
+#endif
+
+
 HbSelectionControl::HbSelectionControl() : HbWidget(*new HbSelectionControlPrivate(),0)
 
 {
@@ -519,18 +881,47 @@
         d->mEdit = edit;        
         QObject::connect(d->mEdit, SIGNAL(cursorPositionChanged(int, int)), control, SLOT(updatePrimitives()));
         QObject::connect(d->mEdit, SIGNAL(contentsChanged()), control, SLOT(updatePrimitives()));
+        d->mMagnifierDelegate->setEditor(d->mEdit);
+
+
+        // Set the background of magnifier to the background of HbLineEdit or HbTextEdit
+        QLatin1String magnifierBackground("qtg_fr_lineedit_normal_c");
+        if (!qobject_cast<HbLineEdit*>(d->mEdit)) {
+            magnifierBackground = QLatin1String("qtg_fr_textedit_normal_c");
+
+        }
+        d->mMagnifier->setBackground(magnifierBackground);
 
         // find first top-level ancestor of d->mEdit
         for(d->mTopLevelAncestor = d->mEdit;
             d->mTopLevelAncestor->parentItem();
-            d->mTopLevelAncestor = d->mTopLevelAncestor->parentItem()){};
+            d->mTopLevelAncestor = d->mTopLevelAncestor->parentItem()){
+
+            // Workaround for Qt bug: QTBUG-13473
+            // This line could be removed after Qt fixes this bug.
+            d->mTopLevelAncestor->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+
+        };
     }
     return control;
 }
 
+void HbSelectionControl::detachEditor(HbAbstractEdit *edit)
+{
+    if(!edit || !edit->mainWindow()) {
+        qWarning("HbSelectionControl: attempting to detach to null editor pointer!");
+    }
+
+    HbSelectionControl *control = globalSelectionControlHash()->value(edit->mainWindow());
+    if (control) {
+        control->detachEditor();
+    }
+}
+
 void HbSelectionControl::detachEditor()
 {
     Q_D(HbSelectionControl);
+    d->mMagnifierDelegate->setEditor(0);
     d->detachEditor(true);
 }
 
@@ -554,7 +945,10 @@
     Q_D(HbSelectionControl);
     if (!isVisible() && d->mEdit) {
         d->show();
-    }
+    }    
+    // selection start handles might be hidden
+    d->mSelectionStartTouchArea->show();
+    d->mSelectionStartHandle->show();
 }
 
 void HbSelectionControl::scrollStarted()
@@ -562,9 +956,13 @@
     Q_D(HbSelectionControl);
 
     if (isVisible() && d->mEdit) {
-        d->mPanInProgress = true;
+        d->mScrollInProgress = true;
         // Reparent handle items to editor canvas on pan start
         d->reparentHandles(HbAbstractEditPrivate::d_ptr(d->mEdit)->canvas);
+        d->mMagnifier->hideWithEffect();
+        // Note: This call is not needed if it is guaranteed that this method
+        // will be called before the scrolling started.
+        updatePrimitives();
     }
 }
 
@@ -573,7 +971,7 @@
     Q_D(HbSelectionControl);
 
     if (isVisible() && d->mEdit) {
-        d->mPanInProgress = false;
+        d->mScrollInProgress = false;
         updatePrimitives();
     }
 }
@@ -585,6 +983,9 @@
 
     if (event->timerId() == d->mWordSnapTimer.timerId()) {
         d->mWordSnapTimer.stop();
+    } else  if (event->timerId() == d->mDelayedTapTimer.timerId()) {
+        d->mDelayedTapTimer.stop();
+        d->delayedTapFinished();
     }
 }
 
@@ -592,11 +993,67 @@
 {
     Q_D(HbSelectionControl);
 
-    HbWidget::polish(params);
-    QSizeF size = d->mSelectionStartTouchArea->preferredSize();
-    d->mSelectionStartTouchArea->resize(size);
-    d->mSelectionEndTouchArea->resize(size);
-    updatePrimitives();
+    if (isVisible()) {
+
+        const QLatin1String KHandleMarginFromLine("handle-margin-from-line");
+        const QLatin1String KMagnifierMarginFromLine("magnifier-margin-from-line");
+        const QLatin1String KMagnifierLeftRightMarginFromHandle("magnifier-left-right-margin-from-handle");
+        const QLatin1String KMagnifierMaxDescent("magnifier-max-descent");
+
+        params.addParameter(KHandleMarginFromLine);
+        params.addParameter(KMagnifierMarginFromLine);
+        params.addParameter(KMagnifierLeftRightMarginFromHandle);
+        params.addParameter(KMagnifierMaxDescent);
+
+        HbWidget::polish(params);
+
+        // Set size of handles
+        QSizeF size = d->mSelectionStartHandle->preferredSize();
+        d->mSelectionStartHandle->setSize(size);
+        d->mSelectionEndHandle->setSize(size);
+
+        // Set max y offset for hit test point
+        // TODO: consider setting this value from css
+        d->mMaxHitTestPointOffsetYFromLine = size.height();
+
+        // Set size of touch areas
+        size = d->mSelectionEndTouchArea->preferredSize();
+        d->mSelectionEndTouchArea->resize(size);
+
+
+        // Increase the height of the touch area of the start selection handle
+        size.setHeight(size.height()*2-d->mSelectionStartHandle->size().height());
+        d->mSelectionStartTouchArea->resize(size);
+
+
+
+        // Set size of magnifier
+        d->mMagnifier->resize(d->mMagnifier->preferredSize());
+
+        if (params.value(KHandleMarginFromLine).isValid()) {
+            d->mHandleMarginFromLine = params.value(KHandleMarginFromLine).toReal();
+        }
+
+        if (params.value(KMagnifierMarginFromLine).isValid()) {
+            d->mMagnifierMarginFromLine = params.value(KMagnifierMarginFromLine).toReal();
+        }
+        if (params.value(KMagnifierLeftRightMarginFromHandle).isValid()) {
+            d->mMagnifierLeftRightMarginFromHandle = params.value(KMagnifierLeftRightMarginFromHandle).toReal();
+        }
+
+        if (params.value(KMagnifierMaxDescent).isValid()) {
+            d->mMagnifierMaxDescent = params.value(KMagnifierMaxDescent).toReal();
+        }
+
+        // Set the min/max magnifier touch offsets
+        // TODO: consider setting this value from css
+        d->mMagnifierMaxYOffsetFromTouchPoint = d->mMagnifierMarginFromLine*2;
+        d->mMagnifierMinYOffsetFromTouchPoint = d->mMagnifierMarginFromLine/2;
+
+        updatePrimitives();
+    } else {
+        HbWidget::polish(params);
+    }
 }
 
 QVariant HbSelectionControl::itemChange(GraphicsItemChange change, const QVariant &value)
@@ -610,26 +1067,26 @@
 
 void HbSelectionControl::gestureEvent(QGestureEvent* event) {
     Q_D(HbSelectionControl);
+
     if(HbTapGesture *tap = qobject_cast<HbTapGesture*>(event->gesture(Qt::TapGesture))) {
-        QPointF pos = event->mapToGraphicsScene(tap->position());
+        if (d->mEdit) {
+            // GestureFinshed is only delegated while the mDelayedTapTimer is active
+            if (tap->state() != Qt::GestureFinished || d->mDelayedTapTimer.isActive()) {
+                HbAbstractEditPrivate::d_ptr(d->mEdit)->gestureEvent(event,this);
+            }
+        }
+
         switch(tap->state()) {
+
         case Qt::GestureStarted:
-            if (d->mEdit) {
-                HbWidgetFeedback::triggered(this, Hb::InstantPressed);
-            }
-            break;
-        case Qt::GestureUpdated:
+            d->tapGestureStarted(tap);
             break;
-      case Qt::GestureFinished:
-            if (d->mEdit) {
-                d->tapGestureFinished(pos);
-                HbWidgetFeedback::triggered(this, Hb::InstantReleased);
-            }
+        case Qt::GestureCanceled:
+        case Qt::GestureFinished:
+            d->tapGestureFinished();
             break;
-      case Qt::GestureCanceled:
-            break;
-      default:
-            break;
+        default:
+              break;
         }
     }
 
@@ -652,6 +1109,10 @@
             }
             break;
       case Qt::GestureCanceled:
+            if (d->mEdit) {
+                d->panGestureCanceled();
+                HbWidgetFeedback::triggered(d->mEdit, Hb::InstantReleased);
+            }
             break;
       default:
             break;
@@ -659,6 +1120,19 @@
     }
 }
 
+bool HbSelectionControl::eventFilter(QObject * watched, QEvent *event)
+{
+    Q_UNUSED(watched)
+
+    // Filter gesture events and delegate to gestureEvent()
+    if (event->type() == QEvent::Gesture || event->type() == QEvent::GestureOverride) {
+        gestureEvent(static_cast<QGestureEvent*>(event));
+        return true;
+    }
+    return false;
+}
+
+
 bool HbSelectionControl::event(QEvent *event)
 {
     Q_D(HbSelectionControl);
@@ -666,28 +1140,103 @@
     if (event->type() == HbEvent::DeviceProfileChanged && d->mEdit) {
         HbDeviceProfileChangedEvent* dpEvent = static_cast<HbDeviceProfileChangedEvent*>(event);
         if ( dpEvent->profile().alternateProfileName() == dpEvent->oldProfile().name() ) {
+            d->initMagnifier();
             updatePrimitives();
         }
     }
     return HbWidget::event(event);
 }
 
+
+void HbSelectionControl::setMagnifierEnabled(bool enable)
+{
+    Q_D(HbSelectionControl);
+    d->mMagnifierEnabled = enable;
+}
+
+bool HbSelectionControl::isMagnifierEnabled() const
+{
+    Q_D(const HbSelectionControl);
+    return d->mMagnifierEnabled;
+}
+
+void HbSelectionControl::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+    Q_UNUSED(widget)
+    Q_UNUSED(option)    
+    Q_UNUSED(painter)
+
+#if HB_DEBUG_PAINT_INFO
+    Q_D(HbSelectionControl);
+    painter->save();
+
+    // draw mHitTestPoint
+    painter->setPen(Qt::yellow);
+    painter->setBrush(Qt::yellow);
+    painter->drawEllipse(d->mHitTestPoint, 3,3);
+
+    // draw mDocRectDebug
+    painter->setBrush(Qt::NoBrush);
+    painter->drawRect(d->mDocRectDebug);
+
+    // draw line representing mMagnifierMaxYOffsetFromTouchPoint
+    painter->setPen(Qt::red);
+    qreal magnifierMaxYOffsetLine = d->mTouchPoint.y()-d->mMagnifierMaxYOffsetFromTouchPoint;
+    painter->drawLine(QPointF(0,magnifierMaxYOffsetLine),QPointF(500,magnifierMaxYOffsetLine));
+
+    // draw line representing mMagnifierMinYOffsetFromTouchPoint
+    painter->setPen(Qt::green);
+    qreal magnifierMinYOffsetLine = d->mTouchPoint.y()-d->mMagnifierMinYOffsetFromTouchPoint;
+    painter->drawLine(QPointF(0,magnifierMinYOffsetLine),QPointF(500,magnifierMinYOffsetLine));
+
+    // draw line representing magnifierStartYOffsetLine
+    painter->setPen(Qt::black);
+    qreal magnifierStartYOffsetLine = d->mStartHandleMagnifierReferenceLine - d->mMagnifierMarginFromLine;
+    painter->drawLine(QPointF(0,magnifierStartYOffsetLine),QPointF(500,magnifierStartYOffsetLine));
+
+    // draw line representing magnifierStartYOffsetLine
+    painter->setPen(Qt::magenta);
+    qreal magnifierEndYOffsetLine = d->mEndHandleMagnifierReferenceLine - d->mMagnifierMarginFromLine;
+    painter->drawLine(QPointF(0,magnifierEndYOffsetLine),QPointF(500,magnifierEndYOffsetLine));
+
+
+    painter->drawRect(boundingRect());
+
+    painter->restore();
+#endif
+
+}
+
+
 void HbSelectionControl::updatePrimitives()
 {
     Q_D(HbSelectionControl);
 
     if (isVisible() && d->polished && d->mEdit) {
-        if (d->mEdit->textCursor().hasSelection() ) {
-           
-            int selStartPos = qMin(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
-            int selEndPos = qMax(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position());
+
+        const bool hasSelection = d->mEdit->textCursor().hasSelection();
+
+        // The interaction mode can be change only when there is no pan in progress
+        if (!d->mIsPanActive) {
+            d->mInteractionMode = (hasSelection?HbSelectionControlPrivate::Selection:
+                                                HbSelectionControlPrivate::CursorPositioning);
+        }
 
-            d->updateHandle(selStartPos,Qt::AlignTop,d->mSelectionStartHandle,d->mSelectionStartTouchArea,HbStyle::P_SelectionControl_selectionstart);
-            d->updateHandle(selEndPos,Qt::AlignBottom,d->mSelectionEndHandle,d->mSelectionEndTouchArea,HbStyle::P_SelectionControl_selectionend);
+        const int cursorPos = d->mEdit->textCursor().position();
+        int selStartPos = cursorPos;
+        int selEndPos = cursorPos;
+        d->mSelectionEndHandle->setIconName(QLatin1String("qtg_graf_editor_handle_finetune"));
+
+        if (hasSelection) {
+            selStartPos = qMin(d->mEdit->textCursor().anchor(),cursorPos);
+            selEndPos = qMax(d->mEdit->textCursor().anchor(),cursorPos);
+            d->mSelectionEndHandle->setIconName(QLatin1String("qtg_graf_editor_handle_end"));
         }
-        else {
-            hide();
-        }
+
+        d->updateHandle(cursorPos,selStartPos,Qt::AlignTop,d->mSelectionStartHandle,d->mSelectionStartTouchArea);
+        d->updateHandle(cursorPos,selEndPos,Qt::AlignBottom,d->mSelectionEndHandle,d->mSelectionEndTouchArea);
+        d->updateMagnifier();
     }
 }
 #include "moc_hbselectioncontrol_p.cpp"
+