added android components
[mardrone] / mardrone / imports / com / nokia / android.1.1 / TextTouchController.qml
diff --git a/mardrone/imports/com/nokia/android.1.1/TextTouchController.qml b/mardrone/imports/com/nokia/android.1.1/TextTouchController.qml
new file mode 100644 (file)
index 0000000..0a2948b
--- /dev/null
@@ -0,0 +1,381 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Components project.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 1.1
+import "." 1.1
+import "RectUtils.js" as Utils
+
+
+MouseArea {
+    id: root
+
+    property Item editor: parent
+    property alias touchTools: touchToolsLoader.item
+    property real editorScrolledX: 0
+    property real editorScrolledY: 0
+    property bool copyEnabled: false
+    property bool cutEnabled: false
+    property bool platformInverted: false
+
+    enabled: !editor.inputMethodComposing
+
+    LayoutMirroring.enabled: false
+    LayoutMirroring.childrenInherit: true
+
+    function updateGeometry() {
+        if (touchTools) {
+            touchTools.handleBegin.updateGeometry();
+            touchTools.handleEnd.updateGeometry();
+            touchTools.contextMenu.calculatePosition(); // Update context menu position
+        }
+    }
+
+    function flickEnded() {
+        if (internal.editorHasSelection && internal.selectionVisible())
+            touchTools.contextMenu.show();
+        else
+            touchTools.contextMenu.hide();
+    }
+
+    onPressed: {
+        if (!touchTools)
+            touchToolsLoader.sourceComponent = touchToolsComponent
+
+        internal.currentTouchPoint = root.mapToItem(editor, mouse.x, mouse.y);
+
+        if (internal.currentTouchPoint.x < 0)
+            internal.currentTouchPoint.x = 0
+
+        if (internal.currentTouchPoint.y < 0)
+            internal.currentTouchPoint.y = 0
+
+        if (internal.tapCounter == 0)
+            internal.touchPoint = internal.currentTouchPoint;
+
+        editor.forceActiveFocus();
+        touchTools.contextMenu.hide();
+        internal.handleMoved = false;
+
+        touchTools.handleBegin.viewPortRect = internal.mapViewPortRectToHandle(touchTools.handleBegin);
+        touchTools.handleEnd.viewPortRect = internal.mapViewPortRectToHandle(touchTools.handleEnd);
+
+        internal.pressedHandle = internal.handleForPoint({x: internal.currentTouchPoint.x, y: internal.currentTouchPoint.y});
+
+        if (internal.pressedHandle != null) {
+            internal.handleGrabbed();
+            // Position cursor at the pressed selection handle
+            // TODO: Add bug ID!!
+            var tempStart = editor.selectionStart
+            var tempEnd = editor.selectionEnd
+            if (internal.pressedHandle == touchTools.handleBegin) {
+                editor.cursorPosition = editor.selectionStart
+                editor.select(tempEnd, tempStart);
+            } else {
+                editor.cursorPosition = editor.selectionEnd
+                editor.select(tempStart, tempEnd);
+            }
+        }
+    }
+
+    onClicked: {
+        ++internal.tapCounter;
+        if (internal.tapCounter == 1) {
+            internal.onSingleTap();
+            clickTimer.start();
+        } else if (internal.tapCounter == 2 && clickTimer.running) {
+            internal.onDoubleTap();
+            clickTimer.restart();
+        } else if (internal.tapCounter == 3 && clickTimer.running)
+            internal.onTripleTap();
+    }
+
+    onPressAndHold: {
+        clickTimer.stop();
+        internal.tapCounter = 0;
+        internal.longTap = true
+        if (!internal.handleMoved) {
+            if (internal.pressedHandle == null) {
+                // position the cursor under the long tap and make the cursor handle grabbed
+                editor.select(editor.cursorPosition, editor.cursorPosition);
+                editor.cursorPosition = editor.positionAt(internal.touchPoint.x,internal.touchPoint.y);
+                internal.pressedHandle = touchTools.handleEnd;
+                if (editor.readOnly)
+                    touchTools.magnifier.hide();
+                internal.handleGrabbed();
+            }
+            touchTools.contextMenu.hide();
+        }
+
+        if (!editor.readOnly || internal.editorHasSelection)
+            touchTools.magnifier.show();
+    }
+
+    onReleased: {
+        touchTools.magnifier.hide();
+
+        mouseGrabDisabler.setKeepMouseGrab(root, false);
+        internal.forcedSelection = false;
+
+        if ((internal.pressedHandle != null && internal.handleMoved) ||
+           (internal.longTap && !editor.readOnly) ||
+           (internal.pressedHandle != null && internal.longTap))
+            touchTools.contextMenu.show();
+        internal.longTap = false;
+    }
+
+    onPositionChanged: {
+
+        internal.currentTouchPoint = root.mapToItem(editor, mouse.x, mouse.y);
+
+        if (internal.pressedHandle != null) {
+            internal.hitTestPoint = {x:internal.currentTouchPoint.x, y:internal.currentTouchPoint.y};
+
+            var newPosition = editor.positionAt(internal.hitTestPoint.x, internal.hitTestPoint.y);
+            if (newPosition >= 0 && newPosition != editor.cursorPosition) {
+                if (internal.hasSelection) {
+                    var anchorPos = internal.pressedHandle == touchTools.handleBegin ? editor.selectionEnd
+                                                                                     : editor.selectionStart
+                    if (editor.selectionStart == editor.cursorPosition)
+                        anchorPos = editor.selectionEnd;
+                    else if (editor.selectionEnd == editor.cursorPosition)
+                        anchorPos = editor.selectionStart;
+                    editor.select(anchorPos, newPosition);
+                } else {
+                    editor.cursorPosition = newPosition;
+                }
+                if (!editor.readOnly || internal.editorHasSelection)
+                    touchTools.magnifier.show();
+                internal.handleMoved = true;
+            }
+        }
+    }
+
+    Connections {
+        target: editor
+        onTextChanged: internal.onEditorTextChanged
+    }
+
+    // Private
+    QtObject {
+        id: internal
+
+        property bool forcedSelection: false
+        property bool hasSelection: editorHasSelection || forcedSelection
+        property bool editorHasSelection: editor.selectionStart != editor.selectionEnd
+        property bool handleMoved: false
+        property bool longTap: false
+        property int tapCounter: 0
+        property variant pressedHandle: null
+        property variant hitTestPoint: Qt.point(0, 0)
+        property variant touchPoint: Qt.point(0, 0)
+        property variant currentTouchPoint: Qt.point(0, 0)
+
+        function onSingleTap() {
+            if (!internal.handleMoved) {
+                // need to deselect, because if the cursor position doesn't change the selection remains
+                // even after setting to cursorPosition
+                editor.deselect();
+                editor.cursorPosition = editor.positionAt(internal.touchPoint.x, internal.touchPoint.y);
+                touchTools.contextMenu.hide();
+                if (!editor.readOnly)
+                    editor.openSoftwareInputPanel()
+            }
+        }
+
+        function onDoubleTap() {
+            // assume that the 1st click positions the cursor
+            editor.selectWord();
+            touchTools.contextMenu.show();
+        }
+
+        function onTripleTap() {
+            editor.selectAll();
+            touchTools.contextMenu.show();
+        }
+
+        function onEditorTextChanged() {
+            if (touchTools && !internal.editorHasSelection)
+                touchTools.contextMenu.hide();
+        }
+
+        function handleGrabbed() {
+            mouseGrabDisabler.setKeepMouseGrab(root, true);
+            internal.hitTestPoint = {x:internal.currentTouchPoint.x, y:internal.currentTouchPoint.y};
+
+            internal.forcedSelection = internal.editorHasSelection;
+        }
+
+        function mapViewPortRectToHandle(handle) {
+            var position = editor.mapToItem(handle, root.editorScrolledX, root.editorScrolledY);
+            var rect = Qt.rect(position.x, position.y, root.width, root.height);
+            return rect;
+        }
+
+        // point is in Editor's coordinate system
+        function handleForPoint(point) {
+            var pressed = null;
+
+            if (!editor.readOnly || editorHasSelection) { // to avoid moving the cursor handle in read only editor
+                // Find out which handle is being moved
+                if (touchTools.handleBegin.visible &&
+                    touchTools.handleBegin.containsPoint(editor.mapToItem(touchTools.handleBegin, point.x, point.y))) {
+                    pressed = touchTools.handleBegin;
+                }
+                if (touchTools.handleEnd.containsPoint(editor.mapToItem(touchTools.handleEnd, point.x, point.y))) {
+                    var useArea = true;
+                    if (pressed != null) {
+                        var distance1 = touchTools.handleBegin.pointDistanceFromCenter(point);
+                        var distance2 = touchTools.handleEnd.pointDistanceFromCenter(point);
+
+                        if (distance1 < distance2)
+                            useArea = false;
+                    }
+                    if (useArea)
+                        pressed = touchTools.handleEnd;
+                }
+            }
+            return pressed;
+        }
+
+        function selectionVisible() {
+            var startRect = editor.positionToRectangle(editor.selectionStart);
+            var endRect = editor.positionToRectangle(editor.selectionEnd);
+            var selectionRect = Qt.rect(startRect.x, startRect.y, endRect.x - startRect.x + endRect.width, endRect.y - startRect.y + endRect.height);
+            var viewPortRect = Qt.rect(editorScrolledX, editorScrolledY, editor.width, editor.height);
+
+            return Utils.rectIntersectsRect(selectionRect, viewPortRect) ||
+                   Utils.rectContainsRect(viewPortRect, selectionRect) ||
+                   Utils.rectContainsRect(selectionRect, viewPortRect);
+        }
+    }
+
+    Loader {
+        id: touchToolsLoader
+    }
+
+    Component {
+        id: touchToolsComponent
+
+        Item {
+            id: touchTools
+            property alias handleBegin: selBegin
+            property alias handleEnd: selEnd
+            property alias contextMenu: cxtMenu
+            property alias magnifier: magnif
+
+            TextSelectionHandle {
+                id: selBegin
+
+                objectName: "SelectionBegin"
+                editor: root.editor
+                imageSource: privateStyle.imagePath("qtg_fr_textfield_handle_start", root.platformInverted)
+                editorPos: editor.selectionStart
+                visible: editor.selectionStart != editor.selectionEnd
+            }
+
+            TextSelectionHandle { // also acts as the cursor handle when no selection
+                id: selEnd
+
+                objectName: "SelectionEnd"
+                editor: root.editor
+                imageSource: privateStyle.imagePath("qtg_fr_textfield_handle_end", root.platformInverted)
+                editorPos: editor.selectionEnd
+                visible: true
+                showImage: internal.hasSelection //show image only in selection mode
+            }
+
+            TextContextMenu {
+                id: cxtMenu
+
+                editor: root.editor
+                platformInverted: root.platformInverted
+                copyEnabled: root.copyEnabled
+                cutEnabled: root.cutEnabled
+            }
+
+            TextMagnifier {
+                id: magnif
+
+                editor: root.editor
+                contentCenter: internal.hitTestPoint
+                platformInverted: root.platformInverted
+
+            }
+        }
+
+    }
+
+    MouseGrabDisabler {
+        id: mouseGrabDisabler
+    }
+
+    Timer {
+        id: clickTimer
+
+        interval: 400; repeat: false
+        onTriggered: {
+            running = false;
+            internal.tapCounter = 0;
+        }
+    }
+
+    Connections {
+        target: root.editor
+        onActiveFocusChanged: {
+            // On focus loss dismiss menu, selection and VKB
+            if (!root.editor.activeFocus) {
+                if (touchTools)
+                    touchTools.contextMenu.hide()
+                root.editor.select(root.editor.cursorPosition, root.editor.cursorPosition)
+                root.editor.closeSoftwareInputPanel()
+            }
+        }
+    }
+
+    Keys.onPressed: {
+        if (!touchTools)
+            touchToolsLoader.sourceComponent = touchToolsComponent
+
+        if (internal.editorHasSelection && event.modifiers & Qt.ShiftModifier
+            && (event.key == Qt.Key_Left || event.key == Qt.Key_Right
+            || event.key == Qt.Key_Up || event.key == Qt.Key_Down))
+            touchTools.contextMenu.show()
+    }
+}