diff -r 000000000000 -r 16d8024aca5e src/hbwidgets/editors/hbselectioncontrol_p.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hbwidgets/editors/hbselectioncontrol_p.cpp Mon Apr 19 14:02:13 2010 +0300 @@ -0,0 +1,511 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Hb API. It exists purely as an +// implementation detail. This file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "hbselectioncontrol_p.h" +#include "hbstyleoption.h" +#include "hbeffect.h" +#include "hbdialog_p.h" +#include "hbabstractedit.h" +#include "hbabstractedit_p.h" +#include "hbtoucharea.h" + + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + static const int SNAP_DELAY = 300; +} + + +class HbSelectionControlPrivate :public HbDialogPrivate +{ + Q_DECLARE_PUBLIC(HbSelectionControl) + +public: + HbSelectionControlPrivate(HbAbstractEdit *edit); + void init(); + void createPrimitives(); + void updateHandle(int newHandlePos, + Qt::AlignmentFlag handleAlignment, + QGraphicsItem *handle, + QGraphicsItem *handleTouchArea, + HbStyle::Primitive handlePrimitive); + QGraphicsItem * reparent(QGraphicsItem *item); + void reparent(QGraphicsItem *item, QGraphicsItem *newParent); + void reparentHandles(QGraphicsItem *newParent); + +public: + + HbAbstractEdit *mEdit; + QPointF mMouseOffset; + + QGraphicsItem *mSelectionStartHandle; + QGraphicsItem *mSelectionEndHandle; + HbTouchArea* mSelectionStartTouchArea; + HbTouchArea* mSelectionEndTouchArea; + + HbSelectionControl::HandleType mPressed; + bool mHandlesDisabled; + bool mPanInProgress; + bool mHandlesMoved; + QBasicTimer mWordSnapTimer; +}; + + +HbSelectionControlPrivate::HbSelectionControlPrivate(HbAbstractEdit *edit): + mEdit(edit), + mSelectionStartHandle(0), + mSelectionEndHandle(0), + mSelectionStartTouchArea(0), + mSelectionEndTouchArea(0), + mPressed(HbSelectionControl::HandleType(0)), + mHandlesDisabled(true), + mPanInProgress(false), + mHandlesMoved(false) +{ +} + +void HbSelectionControlPrivate::init() +{ + Q_Q(HbSelectionControl); + createPrimitives(); + + q->setBackgroundItem(HbStyle::P_None); + q->setFocusPolicy(Qt::NoFocus); + q->setTimeout(HbPopup::NoTimeout); + q->setBackgroundFaded(false); + q->setVisible(false); + q->setDismissPolicy(HbPopup::NoDismiss); + q->setModal(false); + + #ifdef HB_EFFECTS + HbEffect::disable(q); + #endif + + q->setParent(mEdit); + + // Control will handle all events going to different handlers. + q->setHandlesChildEvents(true); + + QObject::connect(mEdit, SIGNAL(cursorPositionChanged(int, int)), q, SLOT(updatePrimitives())); + QObject::connect(mEdit, SIGNAL(selectionChanged(const QTextCursor&, const QTextCursor&)), q, SLOT(updatePrimitives())); + QObject::connect(mEdit, SIGNAL(contentsChanged()), q, SLOT(updatePrimitives())); + + q->updatePrimitives(); + +} + +void HbSelectionControlPrivate::createPrimitives() +{ + Q_Q(HbSelectionControl); + if (!mSelectionStartHandle) { + mSelectionStartHandle = mEdit->style()->createPrimitive(HbStyle::P_SelectionControl_selectionstart, q); + mSelectionStartHandle->hide(); + } + + if (!mSelectionEndHandle) { + mSelectionEndHandle = mEdit->style()->createPrimitive(HbStyle::P_SelectionControl_selectionend, q); + mSelectionEndHandle->hide(); + } + + if (!mSelectionStartTouchArea) { + mSelectionStartTouchArea = new HbTouchArea(q); + mSelectionStartTouchArea->hide(); + HbStyle::setItemName(mSelectionStartTouchArea, "handle-toucharea"); + + } + + if (!mSelectionEndTouchArea) { + mSelectionEndTouchArea = new HbTouchArea(q); + mSelectionEndTouchArea->hide(); + HbStyle::setItemName(mSelectionEndTouchArea, "handle-toucharea"); + } +} + +/* + Updates given handle associated with handleTouchArea to place it + newHandlePos in the selected text. + handlePrimitive identifies handle graphics. +*/ +void HbSelectionControlPrivate::updateHandle(int newHandlePos, + Qt::AlignmentFlag handleAlignment, + QGraphicsItem *handle, + QGraphicsItem *handleTouchArea, + HbStyle::Primitive handlePrimitive) +{ + Q_Q(HbSelectionControl); + + HbStyleOption option; + + q->initStyleOption(&option); + mEdit->style()->updatePrimitive(handle, handlePrimitive, &option); + + QRectF rect = mEdit->rectForPosition(newHandlePos,Qt::AlignTop?QTextLine::Leading:QTextLine::Trailing); + + // Convert rect to handle's parent coordinate system + rect = handle->parentItem()->mapRectFromItem(mEdit,rect); + + // Center handle around center point of rect + QRectF boundingRect = handle->boundingRect(); + + boundingRect.moveCenter(rect.center()); + + if (handleAlignment == Qt::AlignTop) { + boundingRect.moveBottom(rect.top()); + } else { + boundingRect.moveTop(rect.bottom()); + } + + handle->setPos(boundingRect.topLeft()); + + // Center handle touch area around center pos of handle + QPointF centerPos = boundingRect.center(); + boundingRect = handleTouchArea->boundingRect(); + boundingRect.moveCenter(centerPos); + handleTouchArea->setPos(boundingRect.topLeft()); + + if (!mPanInProgress) { + QGraphicsItem * newParent = reparent(handle); + reparent(handleTouchArea, newParent); + } + + handle->show(); + handleTouchArea->show() ; +} + + + +/* + Reparents item to q if item's bounding rect intersects mEdit bounding rectangle or otherwise to + HbAbstractEditPrivate::d_ptr(d->mEdit)->canvas. + Returns new parent. +*/ +QGraphicsItem * HbSelectionControlPrivate::reparent(QGraphicsItem *item) +{ + Q_Q(HbSelectionControl); + + QGraphicsItem *newParent = HbAbstractEditPrivate::d_ptr(mEdit)->canvas; + + // Convert bounding rect to mEdit's coordinate system + QRectF rect = item->boundingRect(); + rect = item->mapRectToItem(mEdit,rect); + + if (mEdit->contains(rect.topLeft()) || mEdit->contains(rect.bottomRight())) { + newParent = q; + } + + reparent(item, newParent); + return newParent; +} + +void HbSelectionControlPrivate::reparent(QGraphicsItem *item, QGraphicsItem *newParent) +{ + if (item && newParent && newParent != item->parentItem()) { + + // Reparent handle items to newParent + QPointF pos = newParent->mapFromItem(item->parentItem(),item->pos()); + + // 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); + } +} + +void HbSelectionControlPrivate::reparentHandles(QGraphicsItem *newParent) +{ + // Reparent handle items to newParent + reparent(mSelectionStartHandle, newParent); + reparent(mSelectionStartTouchArea, newParent); + reparent(mSelectionEndHandle, newParent); + reparent(mSelectionEndTouchArea, newParent); +} + + +HbSelectionControl::HbSelectionControl(HbAbstractEdit *edit) : + HbPopup(*new HbSelectionControlPrivate(edit),0) + +{ + Q_D(HbSelectionControl); + d->q_ptr = this; + d->init(); + //TODO: selection control could be singleton per main window + // since only one selection control is used at a time +} + + +void HbSelectionControl::updatePrimitives() +{ + Q_D(HbSelectionControl); + if (!d->mHandlesDisabled && d->polished) { + if (d->mEdit->textCursor().hasSelection() || + (!d->mEdit->textCursor().hasSelection() && (d->mPressed == SelectionStartHandle || d->mPressed == SelectionEndHandle))) { + + int selStartPos = qMin(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position()); + int selEndPos = qMax(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position()); + + 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); + } + else { + d->mSelectionStartHandle->hide(); + d->mSelectionStartTouchArea->hide() ; + d->mSelectionEndHandle->hide(); + d->mSelectionEndTouchArea->hide() ; + } + } +} + +void HbSelectionControl::hideHandles() +{ + Q_D(HbSelectionControl); + if (!d->mHandlesDisabled) { + d->mHandlesDisabled = true; + hide(); + d->reparentHandles(this); + } +} + +void HbSelectionControl::showHandles() +{ + Q_D(HbSelectionControl); + if (d->mHandlesDisabled) { + d->mHandlesDisabled = false; + show(); + } +} + +void HbSelectionControl::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(HbSelectionControl); + + QPointF editPos = d->mEdit->mapFromScene(event->scenePos()); + + QRectF handleRect = d->mSelectionStartHandle->boundingRect(); + handleRect.moveTopLeft(editPos + d->mMouseOffset); + + QPointF hitTestPos = handleRect.center(); + + if (d->mPressed == SelectionStartHandle) { + hitTestPos.setY(handleRect.bottom()+1); + } else { + hitTestPos.setY(handleRect.top()-1); + } + + // Hit test for the center of current selection touch area + int hitPos = HbAbstractEditPrivate::d_ptr(d->mEdit)->hitTest(hitTestPos,Qt::FuzzyHit); + if (hitPos == -1) { + return; + } + + QTextCursor cursor = d->mEdit->textCursor(); + + if (hitPos != cursor.position()) { + d->mHandlesMoved = true; + } + cursor.setPosition(hitPos, QTextCursor::KeepAnchor); + if (d->mHandlesMoved) { + if (d->mEdit) { + HbWidgetFeedback::triggered(d->mEdit, Hb::InstantDraggedOver); + } + // Restart timer every time when a selection handle moved + d->mWordSnapTimer.start(SNAP_DELAY, this); + d->mEdit->setTextCursor(cursor); + } + + // Ensure that the hitPos is visible + HbAbstractEditPrivate::d_ptr(d->mEdit)->ensurePositionVisible(hitPos); + updatePrimitives(); +} + +void HbSelectionControl::mousePressEvent (QGraphicsSceneMouseEvent *event) +{ + Q_D(HbSelectionControl); + + if (d->mEdit) { + HbWidgetFeedback::triggered(d->mEdit, Hb::InstantPressed); + } + + d->mPressed = DummyHandle; + + // Find out which handle is being moved + if (d->mSelectionStartTouchArea->contains(mapToItem(d->mSelectionStartTouchArea, event->pos()))) { + d->mPressed = SelectionStartHandle; + d->mMouseOffset = d->mSelectionStartHandle->pos() - event->pos(); + } + if (d->mSelectionEndTouchArea->contains(mapToItem(d->mSelectionEndTouchArea, event->pos()))) { + bool useArea = true; + if(d->mPressed != 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 = d->mSelectionStartTouchArea->boundingRect(); + rect.moveTopLeft(d->mSelectionStartTouchArea->pos()); + QLineF lineEventPosSelStartCenter(event->pos(),rect.center()); + + rect = d->mSelectionEndTouchArea->boundingRect(); + rect.moveTopLeft(d->mSelectionEndTouchArea->pos()); + QLineF lineEventPosSelEndCenter(event->pos(),rect.center()); + + if (lineEventPosSelStartCenter.length() < lineEventPosSelEndCenter.length()) { + useArea = false; + } + } + if (useArea) { + d->mPressed = SelectionEndHandle; + d->mMouseOffset = d->mSelectionEndHandle->pos() - event->pos(); + } + } + + if (d->mPressed == DummyHandle) { + // Hit is outside touch areas, ignore + event->ignore(); + return; + } + + // Position cursor at the pressed selection handle + + QTextCursor cursor = d->mEdit->textCursor(); + int selStartPos = qMin(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position()); + int selEndPos = qMax(d->mEdit->textCursor().anchor(),d->mEdit->textCursor().position()); + + if (d->mPressed == SelectionStartHandle) { + cursor.setPosition(selEndPos); + cursor.setPosition(selStartPos, QTextCursor::KeepAnchor); + } else { + cursor.setPosition(selStartPos); + cursor.setPosition(selEndPos, QTextCursor::KeepAnchor); + } + d->mEdit->setTextCursor(cursor); + +} + +void HbSelectionControl::mouseReleaseEvent (QGraphicsSceneMouseEvent *event) +{ + Q_D(HbSelectionControl); + Q_UNUSED(event); + + if (d->mEdit) { + HbWidgetFeedback::triggered(d->mEdit, Hb::InstantReleased); + } + + if (d->mWordSnapTimer.isActive()) { + + // Snap selection to word beginning or end + QTextCursor cursor = d->mEdit->textCursor(); + int curPos = d->mEdit->textCursor().position(); + int anchPos = d->mEdit->textCursor().anchor(); + cursor.select(QTextCursor::WordUnderCursor); + + // Snap direction depends on cursor position + curPos = ((curPos > anchPos)?cursor.position():cursor.anchor()); + + cursor.setPosition(anchPos); + cursor.setPosition(curPos, QTextCursor::KeepAnchor); + d->mEdit->setTextCursor(cursor); + } + + d->mPressed = DummyHandle; + updatePrimitives(); + + if (!d->mHandlesMoved) { + if (d->mEdit->contextMenuFlags().testFlag(Hb::ShowTextContextMenuOnSelectionClicked)) { + d->mEdit->showContextMenu(event->scenePos()); + } + } + d->mHandlesMoved = false; +} + +void HbSelectionControl::panStarted() +{ + Q_D(HbSelectionControl); + + if (!d->mHandlesDisabled) { + d->mPanInProgress = true; + // Reparent handle items to editor canvas on pan start + d->reparentHandles(HbAbstractEditPrivate::d_ptr(d->mEdit)->canvas); + } +} + +void HbSelectionControl::panFinished() +{ + Q_D(HbSelectionControl); + + if (!d->mHandlesDisabled) { + d->mPanInProgress = false; + updatePrimitives(); + } +} + + +void HbSelectionControl::timerEvent(QTimerEvent *event) +{ + Q_D(HbSelectionControl); + + if (event->timerId() == d->mWordSnapTimer.timerId()) { + d->mWordSnapTimer.stop(); + } +} + +void HbSelectionControl::polish( HbStyleParameters& params ) +{ + Q_D(HbSelectionControl); + + HbPopup::polish(params); + QSizeF size = d->mSelectionStartTouchArea->preferredSize(); + d->mSelectionStartTouchArea->resize(size); + d->mSelectionEndTouchArea->resize(size); +} + +QVariant HbSelectionControl::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == QGraphicsItem::ItemPositionChange) { + return qVariantFromValue(QPointF(0,0)); + } else if (change == QGraphicsItem::ItemVisibleChange) { + updatePrimitives(); + } + + return HbPopup::itemChange(change, value); +}