added android components
authorKate Alhola <kate.alhola@gmail.com>
Mon, 3 Sep 2012 18:10:08 +0000 (21:10 +0300)
committerKate Alhola <kate.alhola@gmail.com>
Mon, 3 Sep 2012 18:10:08 +0000 (21:10 +0300)
56 files changed:
mardrone/imports/com/nokia/android.1.1/AppManager.js [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ApplicationWindow.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/BusyIndicator.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Button.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ButtonColumn.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ButtonGroup.js [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ButtonRow.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/CheckBox.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/CommonDialog.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ContextMenu.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Dialog.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Fader.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Label.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ListHeading.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ListItem.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ListItemText.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Menu.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/MenuContent.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/MenuItem.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/MenuLayout.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Page.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/PageStack.js [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/PageStack.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/PageStackWindow.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Popup.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ProgressBar.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/QueryDialog.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/RadioButton.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/RectUtils.js [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ScrollBar.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ScrollDecorator.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/SectionScroller.js [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/SectionScroller.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/SelectionDialog.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/SelectionListItem.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Slider.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/StatusBar.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/StatusBar.qml~ [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Switch.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TabBar.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TabBarLayout.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TabButton.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TabGroup.js [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TabGroup.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TextArea.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TextContextMenu.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TextField.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TextMagnifier.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TextSelectionHandle.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/TextTouchController.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ToolBar.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ToolBarLayout.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ToolButton.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/ToolTip.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/Window.qml [new file with mode: 0644]
mardrone/imports/com/nokia/android.1.1/qmldir [new file with mode: 0644]

diff --git a/mardrone/imports/com/nokia/android.1.1/AppManager.js b/mardrone/imports/com/nokia/android.1.1/AppManager.js
new file mode 100644 (file)
index 0000000..daed77a
--- /dev/null
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+function rootObject() {
+    var next = parent
+    while (next && next.parent)
+        next = next.parent
+    return next
+}
+
+function findParent(child, propertyName) {
+    if (!child)
+        return null
+    var next = child.parent
+    while (next && !next.hasOwnProperty(propertyName))
+        next = next.parent
+    return next
+}
+
+function sceneX(item) {
+    // Binding may cause that this function is evaluated even when item is undefined,
+    // but in that case the Binding isn't active however so we can safely return 0
+    var x = 0
+    if (item) {
+        x = item.x
+        var p = item.parent
+        while (p) {
+            x += p.x
+            p = p.parent
+        }
+    }
+    return x
+}
+
+function sceneY(item) {
+    // Binding may cause that this function is evaluated even when item is undefined,
+    // but in that case the Binding isn't active however so we can safely return 0
+    var y = 0
+    if (item) {
+        y = item.y
+        var p = item.parent
+        while (p) {
+            y += p.y
+            p = p.parent
+        }
+    }
+    return y
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ApplicationWindow.qml b/mardrone/imports/com/nokia/android.1.1/ApplicationWindow.qml
new file mode 100644 (file)
index 0000000..9edc9a5
--- /dev/null
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** 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
+
+Window {
+    id: root
+
+    property bool fullScreen: false
+    default property alias content: contentItem.data
+    property alias pageStack: stack
+    property variant initialPage
+    property bool softwareInputPanelEnabled: false
+
+    onInitialPageChanged: {
+        if (initialPage && contentArea.initialized) {
+            if (stack.depth == 0)
+                stack.push(initialPage, null, true)
+            else if (stack.depth == 1)
+                stack.replace(initialPage, null, true)
+            else
+                console.log("Cannot update ApplicationWindow.initialPage")
+        }
+    }
+
+    Component.onCompleted: {
+        console.log("warning: ApplicationWindow is an experimental component. Use Window instead.")
+        contentArea.initialized = true
+        if (initialPage && stack.depth == 0)
+            stack.push(initialPage, null, true)
+    }
+
+    Item {
+        id: contentArea
+
+        property bool initialized: false
+
+        anchors.top: sbar.bottom
+        anchors.bottom: sip.top
+        anchors.left: parent.left
+        anchors.right: parent.right
+        Item {
+            id: contentItem
+            anchors.fill: parent
+            PageStack {
+                id: stack
+                anchors.fill: parent
+                toolBar: tbar
+            }
+        }
+    }
+
+    StatusBar {
+        id: sbar
+
+        width: parent.width
+        state: root.fullScreen ? "Hidden" : "Visible"
+        platformInverted: root.platformInverted
+
+        states: [
+            State {
+                name: "Visible"
+                PropertyChanges { target: sbar; y: 0; opacity: 1 }
+            },
+            State {
+                name: "Hidden"
+                PropertyChanges { target: sbar; y: -height; opacity: 0 }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                from: "Hidden"; to: "Visible"
+                ParallelAnimation {
+                    NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad }
+                    NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            },
+            Transition {
+                from: "Visible"; to: "Hidden"
+                ParallelAnimation {
+                    NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.Linear }
+                    NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            }
+        ]
+    }
+
+    Item {
+        id: sip
+
+        anchors { bottom: parent.bottom; left: parent.left; right: parent.right }
+
+        Behavior on height { PropertyAnimation { duration: 200 } }
+
+        states: [
+            State {
+                name: "Visible"; when: inputContext.visible && softwareInputPanelEnabled
+                PropertyChanges { target: sip; height: inputContext.height }
+            },
+
+            State {
+                name: "Hidden"; when: !root.fullScreen
+                PropertyChanges { target: sip; height: tbar.height }
+            },
+
+            State {
+                name: "HiddenInFullScreen"; when: root.fullScreen
+                PropertyChanges { target: sip; height: 0 }
+            }
+        ]
+    }
+       
+    ToolBar {
+        id: tbar
+
+        width: parent.width
+        state: root.fullScreen ? "Hidden" : "Visible"
+        platformInverted: root.platformInverted
+
+        states: [
+            State {
+                name: "Visible"
+                PropertyChanges { target: tbar; y: parent.height - height; opacity: 1 }
+            },
+            State {
+                name: "Hidden"
+                PropertyChanges { target: tbar; y: parent.height; opacity: 0 }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                from: "Hidden"; to: "Visible"
+                ParallelAnimation {
+                    NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad }
+                    NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            },
+            Transition {
+                from: "Visible"; to: "Hidden"
+                ParallelAnimation {
+                    NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.Linear }
+                    NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            }
+        ]
+    }
+
+    // event preventer when page transition is active
+    MouseArea {
+        anchors.fill: parent
+        enabled: pageStack.busy
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/BusyIndicator.qml b/mardrone/imports/com/nokia/android.1.1/BusyIndicator.qml
new file mode 100644 (file)
index 0000000..cb75863
--- /dev/null
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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 com.nokia.android 1.1
+import "." 1.1
+
+Item {
+    id: root
+
+    property bool running: false
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    implicitWidth: platformStyle.graphicSizeSmall
+    implicitHeight: platformStyle.graphicSizeSmall
+
+    Image {
+        id: spinner
+        property int index: 1
+
+        // cannot use anchors.fill here because the size will be 0 during
+        // construction and that gives out nasty debug warnings
+        width: parent.width
+        height: parent.height
+        sourceSize.width: width
+        sourceSize.height: height
+        source: privateStyle.imagePath("qtg_anim_spinner_large_" + index,
+                                       root.platformInverted)
+        smooth: true
+
+        NumberAnimation on index {
+            id: numAni
+            from: 1; to: 10
+            duration: 1000
+            running: root.visible
+            // QTBUG-19080 is preventing the following line from working
+            // We will have to use workaround for now
+            // http://bugreports.qt.nokia.com/browse/QTBUG-19080
+            // paused: !root.running || !android.foreground
+            loops: Animation.Infinite
+        }
+
+        // START workaround for QTBUG-19080
+        Component {
+            id: bindingCom
+            Binding {
+                target: numAni
+                property: "paused"
+                value: numAni.running ? (!root.running || !android.foreground) : false
+            }
+        }
+        Component.onCompleted: bindingCom.createObject(numAni)
+        // END workaround
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Button.qml b/mardrone/imports/com/nokia/android.1.1/Button.qml
new file mode 100644 (file)
index 0000000..de6d101
--- /dev/null
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: button
+
+    // Common Public API
+    property bool checked: false
+    property bool checkable: false
+    property bool pressed: (stateGroup.state == "Pressed" || stateGroup.state == "AutoRepeating") && mouseArea.containsMouse
+    property alias text: label.text
+    property alias iconSource: icon.source
+    property alias font: label.font
+
+    signal clicked
+
+    // Symbian specific signals and properties
+    signal platformReleased
+    signal platformPressAndHold
+
+    // specific API
+    property bool platformInverted: false
+    property bool platformAutoRepeat: true
+
+    implicitWidth: Math.max(container.contentWidth + 2 * internal.horizontalPadding, privateStyle.buttonSize)
+    implicitHeight: Math.max(container.contentHeight + 2 * internal.verticalPadding, privateStyle.buttonSize)
+
+    QtObject {
+        id: internal
+        objectName: "internal"
+
+        property int autoRepeatInterval: 60
+        property int verticalPadding: (privateStyle.buttonSize - platformStyle.graphicSizeSmall) / 2
+        property int horizontalPadding: label.text ? platformStyle.paddingLarge : verticalPadding
+
+        // "pressed" is a transient state, see press() function
+        function modeName() {
+            if (belongsToButtonRow())
+                return parent.privateModeName(button, 0)
+            else if (!button.enabled)
+                return "disabled"
+            else if (button.checked)
+                return "latched"
+            else
+                return "normal"
+        }
+
+        function toggleChecked() {
+            if (checkable)
+                checked = !checked
+        }
+
+        function press() {
+            if (!belongsToButtonGroup()) {
+                if (checkable && checked)
+                    privateStyle.play(Android.SensitiveButton)
+                else
+                    privateStyle.play(Android.BasicButton)
+            } else if (checkable && !checked) {
+                privateStyle.play(Android.BasicButton)
+            }
+            highlight.source = privateStyle.imagePath(internal.imageName() + "pressed",
+                                                      button.platformInverted)
+            container.scale = 0.95
+            highlight.opacity = 1
+        }
+
+        function release() {
+            container.scale = 1
+            highlight.opacity = 0
+            if (tapRepeatTimer.running)
+                tapRepeatTimer.stop()
+            button.platformReleased()
+        }
+
+        function click() {
+            if ((checkable && checked && !belongsToButtonGroup()) || !checkable)
+                privateStyle.play(Android.BasicButton)
+            internal.toggleChecked()
+            clickedEffect.restart()
+            button.clicked()
+        }
+
+        function repeat() {
+            if (!checkable)
+                privateStyle.play(Android.SensitiveButton)
+            button.clicked()
+        }
+
+        // The function imageName() handles fetching correct graphics for the Button.
+        // If the parent of a Button is ButtonRow, segmented-style graphics are used to create a
+        // seamless row of buttons. Otherwise normal Button graphics are utilized.
+        function imageName() {
+            var mirror = button.LayoutMirroring.enabled // To create binding
+            if (belongsToButtonRow())
+                return parent.privateGraphicsName(button, 0)
+            return "qtg_fr_pushbutton_"
+        }
+
+        function belongsToButtonGroup() {
+            return button.parent
+                   && button.parent.hasOwnProperty("checkedButton")
+                   && button.parent.exclusive
+        }
+
+        function belongsToButtonRow() {
+            return button.parent
+                    && button.parent.hasOwnProperty("checkedButton")
+                    && button.parent.hasOwnProperty("privateDirection")
+                    && button.parent.privateDirection == Qt.Horizontal
+                    && button.parent.children.length > 1
+        }
+    }
+
+    StateGroup {
+        id: stateGroup
+
+        states: [
+            State { name: "Pressed" },
+            State { name: "AutoRepeating" },
+            State { name: "Canceled" }
+        ]
+
+        transitions: [
+            Transition {
+                to: "Pressed"
+                ScriptAction { script: internal.press() }
+            },
+            Transition {
+                from: "Pressed"
+                to: "AutoRepeating"
+                ScriptAction { script: tapRepeatTimer.start() }
+            },
+            Transition {
+                from: "Pressed"
+                to: ""
+                ScriptAction { script: internal.release() }
+                ScriptAction { script: internal.click() }
+            },
+            Transition {
+                from: "Pressed"
+                to: "Canceled"
+                ScriptAction { script: internal.release() }
+            },
+            Transition {
+                from: "AutoRepeating"
+                ScriptAction { script: internal.release() }
+            }
+        ]
+    }
+
+    BorderImage {
+        source: privateStyle.imagePath(internal.imageName() + internal.modeName(),
+                                       button.platformInverted)
+        border { left: 20; top: 20; right: 20; bottom: 20 }
+        anchors.fill: parent
+
+        BorderImage {
+            id: highlight
+            border { left: 20; top: 20; right: 20; bottom: 20 }
+            opacity: 0
+            anchors.fill: parent
+        }
+    }
+
+    Item {
+        id: container
+
+        // Having both icon and text simultaneously is unspecified but supported by implementation
+        property int spacing: (icon.height && label.text) ? platformStyle.paddingSmall : 0
+        property int contentWidth: Math.max(icon.width, label.textWidth)
+        property int contentHeight: icon.height + spacing + label.height
+
+        width: Math.min(contentWidth, button.width - 2 * internal.horizontalPadding)
+        height: Math.min(contentHeight, button.height - 2 * internal.verticalPadding)
+        clip: true
+        anchors.centerIn: parent
+
+        Image {
+            id: icon
+            sourceSize.width: platformStyle.graphicSizeSmall
+            sourceSize.height: platformStyle.graphicSizeSmall
+            smooth: true
+            anchors.top: parent.top
+            anchors.horizontalCenter: parent.horizontalCenter
+        }
+        Text {
+            id: label
+            elide: Text.ElideRight
+            property int textWidth: text ? privateStyle.textWidth(text, font) : 0
+            anchors {
+                top: icon.bottom
+                topMargin: parent.spacing
+                left: parent.left
+                right: parent.right
+            }
+            height: text ? privateStyle.fontHeight(font) : 0
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeLarge }
+            color: {
+                if (!button.enabled)
+                    return button.platformInverted ? platformStyle.colorDisabledLightInverted
+                                                   : platformStyle.colorDisabledLight
+                else if (button.pressed)
+                    return button.platformInverted ? platformStyle.colorPressedInverted
+                                                   : platformStyle.colorPressed
+                else if (button.checked)
+                    return button.platformInverted ? platformStyle.colorLatchedInverted
+                                                   : platformStyle.colorLatched
+                else
+                    return button.platformInverted ? platformStyle.colorNormalLightInverted
+                                                   : platformStyle.colorNormalLight
+            }
+        }
+    }
+
+    MouseArea {
+        id: mouseArea
+
+        anchors.fill: parent
+
+        onPressed: stateGroup.state = "Pressed"
+
+        onReleased: stateGroup.state = ""
+
+        onCanceled: {
+            // Mark as canceled
+            stateGroup.state = "Canceled"
+            // Reset state. Can't expect a release since mouse was ungrabbed
+            stateGroup.state = ""
+        }
+
+        onPressAndHold: {
+            if (stateGroup.state != "Canceled" && platformAutoRepeat)
+                stateGroup.state = "AutoRepeating"
+            button.platformPressAndHold()
+        }
+
+        onExited: stateGroup.state = "Canceled"
+    }
+
+    Timer {
+        id: tapRepeatTimer
+
+        interval: internal.autoRepeatInterval; running: false; repeat: true
+        onTriggered: internal.repeat()
+    }
+
+    ParallelAnimation {
+        id: clickedEffect
+        PropertyAnimation {
+            target: container
+            property: "scale"
+            from: 0.95
+            to: 1.0
+            easing.type: Easing.Linear
+            duration: 100
+        }
+        PropertyAnimation {
+            target: highlight
+            property: "opacity"
+            from: 1
+            to: 0
+            easing.type: Easing.Linear
+            duration: 150
+        }
+    }
+
+    Keys.onPressed: {
+        if ((!event.isAutoRepeat || platformAutoRepeat)
+            && (event.key == Qt.Key_Select || event.key == Qt.Key_Return || event.key == Qt.Key_Enter)) {
+            stateGroup.state = "Pressed"
+            event.accepted = true
+        }
+    }
+
+    Keys.onReleased: {
+        if ((!event.isAutoRepeat || platformAutoRepeat)
+            && (event.key == Qt.Key_Select || event.key == Qt.Key_Return || event.key == Qt.Key_Enter)) {
+            stateGroup.state = ""
+            event.accepted = true
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ButtonColumn.qml b/mardrone/imports/com/nokia/android.1.1/ButtonColumn.qml
new file mode 100644 (file)
index 0000000..e545cee
--- /dev/null
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** 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 "ButtonGroup.js" as Engine
+
+Column {
+    id: root
+
+    // Common public API
+    property bool exclusive: true
+    property Item checkedButton
+
+    property int privateDirection: Qt.Vertical
+
+    Component.onCompleted: Engine.create(root)
+    Component.onDestruction: Engine.destroy()
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ButtonGroup.js b/mardrone/imports/com/nokia/android.1.1/ButtonGroup.js
new file mode 100644 (file)
index 0000000..3843719
--- /dev/null
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+var self
+var clickHandlers = []
+var visibleButtons = []
+var resizing = false
+var checkingOverallExclusivity = false
+var checkedBtn
+
+function create(that) {
+    destroy()
+    self = that
+
+    // If the item is not visible at this stage, we store the value of the property
+    // checkedButton to ensure a proper initialization. The value is restored in
+    // initCheckedButton().
+    if (!self.visible)
+        checkedBtn = self.checkedButton
+
+    buildItems()
+    self.childrenChanged.connect(buildItems)
+    self.widthChanged.connect(resizeButtons)
+    self.exclusiveChanged.connect(checkOverallExclusivity)
+    self.checkedButtonChanged.connect(checkOverallExclusivity)
+    self.visibleChanged.connect(initCheckedButton)
+}
+
+function destroy() {
+    if (self !== undefined) {
+        self.childrenChanged.disconnect(buildItems)
+        self.widthChanged.disconnect(resizeButtons)
+        self.exclusiveChanged.disconnect(checkOverallExclusivity)
+        self.checkedButtonChanged.disconnect(checkOverallExclusivity)
+        self.visibleChanged.disconnect(initCheckedButton)
+        releaseItemConnections()
+        self = undefined
+    }
+}
+
+function initCheckedButton() {
+    // When the item becomes visible, restore the value of checkedButton property
+    // that was stored in the create function.
+    if (self.visible && checkedBtn !== null) {
+        self.checkedButton = checkedBtn
+        checkedBtn = null
+    }
+}
+
+function buildItems() {
+    releaseItemConnections()
+    visibleButtons = []
+    for (var i = 0; i < self.children.length; i++) {
+        var item = self.children[i]
+
+        // set up item connections
+        clickHandlers[i] = checkExclusive(item)
+        item.clicked.connect(clickHandlers[i])
+        item.visibleChanged.connect(buildItems)
+
+        // update visibleButtons array
+        if (item.visible)
+            visibleButtons.push(item)
+    }
+    checkOverallExclusivity()
+    resizeButtons()
+}
+
+function releaseItemConnections() {
+    for (var i = 0; i < self.children.length; i++) {
+        self.children[i].clicked.disconnect(clickHandlers[i])
+        self.children[i].visibleChanged.disconnect(buildItems)
+    }
+    clickHandlers = []
+}
+
+function checkOverallExclusivity() {
+    if (!checkingOverallExclusivity && self.exclusive) {
+        // prevent re-entrant calls
+        checkingOverallExclusivity = true
+        if (visibleButtons.length > 0) {
+            if ((self.checkedButton === null || !self.checkedButton.visible))
+                self.checkedButton = visibleButtons[0]
+            self.checkedButton.checked = true
+        }
+        else {
+            self.checkedButton = null
+        }
+
+        for (var i = 0; i < self.children.length; i++) {
+            var item = self.children[i]
+            // e.g CheckBox can be added to ButtonGroup but doesn't have "checkable" property
+            if (self.exclusive && item.hasOwnProperty("checkable"))
+                item.checkable = true
+            if (item !== self.checkedButton)
+                item.checked = false
+        }
+        checkingOverallExclusivity = false
+    }
+}
+
+function checkExclusive(item) {
+    var button = item
+    return function() {
+        if (self.exclusive) {
+            for (var i = 0, ref; (ref = visibleButtons[i]); i++)
+                ref.checked = button === ref
+        }
+        self.checkedButton = button
+    }
+}
+
+function resizeButtons() {
+    if (!resizing && visibleButtons.length && self.privateDirection == Qt.Horizontal) {
+        // if ButtonRow has implicit size, a loop may occur where resizing individual
+        // Button affects ButtonRow size, which triggers again resizing...
+        // therefore we prevent re-entrant resizing attempts
+        resizing = true
+        var buttonWidth = self.width / visibleButtons.length
+        for (var i = 0; i < self.children.length; i++) {
+            self.children[i].width = self.children[i].visible ? buttonWidth : 0
+        }
+        resizing = false
+    }
+}
+
+// Binding would not work properly if visibleButtons would just be returned,
+// it would miss visibility changes. In the long run ButtonGroup.js could be
+// refactored.
+function visibleItems(item) {
+    var visibleChildren = []
+    for (var i = 0; i < item.children.length; i++) {
+        if (item.children[i].visible)
+            visibleChildren.push(item.children[i])
+    }
+    return visibleChildren
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ButtonRow.qml b/mardrone/imports/com/nokia/android.1.1/ButtonRow.qml
new file mode 100644 (file)
index 0000000..df75132
--- /dev/null
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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 "ButtonGroup.js" as Engine
+
+Row {
+    id: root
+
+    // Common public API
+    property bool exclusive: true
+    property Item checkedButton
+
+    property int privateDirection: Qt.Horizontal
+
+    function privateGraphicsName(button, buttonType) {
+        var siblingButtons = Engine.visibleItems(root)
+        var imageName = ""
+        var rightToLeft = root.LayoutMirroring.enabled
+            ? (root.layoutDirection == Qt.LeftToRight)
+            : (root.layoutDirection == Qt.RightToLeft)
+        if (siblingButtons.length > 1) {
+            if (buttonType == 0)
+                imageName = "qtg_fr_pushbutton_segmented"
+            else if (buttonType == 1)
+                imageName = "qtg_fr_toolbutton_segmented"
+
+            if (button === siblingButtons[0])
+                imageName += rightToLeft ? "_r_" : "_l_"
+            else if (button === siblingButtons[siblingButtons.length - 1])
+                imageName += rightToLeft ? "_l_" : "_r_"
+            else
+                imageName += "_c_"
+        }
+        else if (buttonType == 0)
+            imageName = "qtg_fr_pushbutton_"
+        else if (buttonType == 1)
+            imageName = "qtg_fr_toolbutton_text_"
+
+        return imageName
+    }
+
+    function privateModeName(button, buttonType) {
+        var siblingButtons = Engine.visibleItems(root)
+        if (!button.enabled)
+            return "disabled"
+        else if (button.checked && siblingButtons.length > 1)
+            return "latched"
+        else
+            return "normal"
+    }
+
+    Component.onCompleted: Engine.create(root)
+    Component.onDestruction: Engine.destroy()
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/CheckBox.qml b/mardrone/imports/com/nokia/android.1.1/CheckBox.qml
new file mode 100644 (file)
index 0000000..e42e8b6
--- /dev/null
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    // Common Public API
+    property bool checked: false
+    property bool pressed: stateGroup.state == "Pressed" || stateGroup.state == "KeyPressed"
+
+    signal clicked
+
+    property alias text: label.text
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    // Performance optimization:
+    // Use value assignment when property changes instead of binding to js function
+    onCheckedChanged: { contentIcon.source = internal.iconSource() }
+    onPressedChanged: { contentIcon.source = internal.iconSource() }
+    onEnabledChanged: { contentIcon.source = internal.iconSource() }
+    onPlatformInvertedChanged: { contentIcon.source = internal.iconSource() }
+
+    QtObject {
+        id: internal
+        objectName: "internal"
+        property color disabledColor: root.platformInverted ? platformStyle.colorDisabledLightInverted
+                                                            : platformStyle.colorDisabledLight
+        property color pressedColor: root.platformInverted ? platformStyle.colorPressedInverted
+                                                           : platformStyle.colorPressed
+        property color normalColor: root.platformInverted ? platformStyle.colorNormalLightInverted
+                                                          : platformStyle.colorNormalLight
+
+        function iconSource() {
+            var id
+            if (!root.enabled) {
+                if (root.checked)
+                    id = "disabled_selected"
+                else
+                    id = "disabled_unselected"
+            } else {
+                if (root.pressed)
+                    id = "pressed"
+                else if (root.checked)
+                    id = "normal_selected"
+                else
+                    id = "normal_unselected"
+            }
+            return privateStyle.imagePath("qtg_graf_checkbox_" + id, root.platformInverted)
+        }
+
+        function toggle() {
+            clickedEffect.restart()
+            root.checked = !root.checked
+            root.clicked()
+        }
+    }
+
+    StateGroup {
+        id: stateGroup
+
+        states: [
+            State { name: "Pressed" },
+            State { name: "KeyPressed" },
+            State { name: "Canceled" }
+        ]
+
+        transitions: [
+            Transition {
+                to: "Pressed"
+                ScriptAction { script:  privateStyle.play(Android.BasicItem) }
+            },
+            Transition {
+                from: "Pressed"
+                to: ""
+                ScriptAction { script: privateStyle.play(Android.CheckBox) }
+                ScriptAction { script: internal.toggle() }
+            },
+            Transition {
+                from: "KeyPressed"
+                to: ""
+                ScriptAction { script: internal.toggle() }
+            }
+
+        ]
+    }
+
+    implicitWidth: label.text ? privateStyle.buttonSize + platformStyle.paddingMedium + privateStyle.textWidth(label.text, label.font)
+                              : privateStyle.buttonSize
+    implicitHeight: privateStyle.buttonSize
+
+    Image {
+        id: contentIcon
+        source: privateStyle.imagePath("qtg_graf_checkbox_normal_unselected", root.platformInverted);
+        anchors.left: parent.left
+        anchors.verticalCenter: parent.verticalCenter
+        sourceSize.width: privateStyle.buttonSize
+        sourceSize.height: privateStyle.buttonSize
+        smooth: true
+
+        MouseArea {
+            id: mouseArea
+            anchors.fill: parent
+
+            onPressed: stateGroup.state = "Pressed"
+            onReleased: stateGroup.state = ""
+            onClicked: stateGroup.state = ""
+            onExited: stateGroup.state = "Canceled"
+            onCanceled: {
+                // Mark as canceled
+                stateGroup.state = "Canceled"
+                // Reset state. Can't expect a release since mouse was ungrabbed
+                stateGroup.state = ""
+            }
+        }
+    }
+
+    Text {
+        id: label
+        anchors.left: contentIcon.right
+        anchors.leftMargin: text ? platformStyle.paddingMedium : 0
+        anchors.right: parent.right
+        anchors.verticalCenter: parent.verticalCenter
+        elide: Text.ElideRight
+        horizontalAlignment: Text.AlignLeft
+        font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
+        color: root.enabled ? (root.pressed ? internal.pressedColor : internal.normalColor)
+                            : internal.disabledColor
+    }
+
+    SequentialAnimation {
+        id: clickedEffect
+        PropertyAnimation {
+            target: contentIcon
+            property: "scale"
+            from: 1.0
+            to: 0.8
+            easing.type: Easing.Linear
+            duration: 50
+        }
+        PropertyAnimation {
+            target: contentIcon
+            property: "scale"
+            from: 0.8
+            to: 1.0
+            easing.type: Easing.OutQuad
+            duration: 170
+        }
+    }
+
+    Keys.onPressed: {
+        if (!event.isAutoRepeat && (event.key == Qt.Key_Select
+                                    || event.key == Qt.Key_Return
+                                    || event.key == Qt.Key_Enter)) {
+            stateGroup.state = "KeyPressed"
+            event.accepted = true
+        }
+    }
+
+    Keys.onReleased: {
+        if (!event.isAutoRepeat && (event.key == Qt.Key_Select
+                                    || event.key == Qt.Key_Return
+                                    || event.key == Qt.Key_Enter)) {
+            stateGroup.state = ""
+            event.accepted = true
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/CommonDialog.qml b/mardrone/imports/com/nokia/android.1.1/CommonDialog.qml
new file mode 100644 (file)
index 0000000..045c74d
--- /dev/null
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** 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
+
+Dialog {
+    id: root
+
+    property alias titleText: titleAreaText.text
+    property url titleIcon
+    property variant buttonTexts: []
+    property bool privateCloseIcon: false
+
+    signal buttonClicked(int index)
+
+    onButtonTextsChanged: {
+        for (var i = buttonRow.children.length; i > 0; --i) {
+            buttonRow.children[i - 1].destroy()
+        }
+        for (var j = 0; j < buttonTexts.length; ++j) {
+            var button = buttonComponent.createObject(buttonRow)
+            button.text = buttonTexts[j]
+            button.index = j
+        }
+    }
+
+    Component {
+        id: buttonComponent
+        ToolButton {
+            property int index
+
+            width: internal.buttonWidth()
+            height: privateStyle.toolBarHeightLandscape
+            platformInverted: root.platformInverted
+
+            onClicked: {
+                if (root.status == DialogStatus.Open) {
+                    root.buttonClicked(index)
+                    root.close()
+                }
+            }
+        }
+    }
+
+    QtObject {
+        id: internal
+
+        function buttonWidth() {
+            switch (buttonTexts.length) {
+                case 0: return 0
+                case 1: return Math.round((privateStyle.dialogMaxSize - 3 * platformStyle.paddingMedium) / 2)
+                default: return (buttonContainer.width - (buttonTexts.length + 1) *
+                    platformStyle.paddingMedium) / buttonTexts.length
+            }
+        }
+
+        function iconSource() {
+            if (privateCloseIcon) {
+                return privateStyle.imagePath((iconMouseArea.pressed && !iconMouseArea.pressCancelled
+                    ? "qtg_graf_popup_close_pressed"
+                    : "qtg_graf_popup_close_normal"),
+                    root.platformInverted)
+            } else {
+                return root.titleIcon
+            }
+        }
+    }
+
+    title: Item {
+        anchors.left: parent.left
+        anchors.right: parent.right
+        height: platformStyle.graphicSizeSmall + 2 * platformStyle.paddingLarge
+
+        LayoutMirroring.enabled: privateCloseIcon ? false : undefined
+        LayoutMirroring.childrenInherit: true
+
+        Item {
+            id: titleLayoutHelper // needed to make the text mirror correctly
+
+            anchors.left: parent.left
+            anchors.right: titleAreaIcon.source == "" ? parent.right : titleAreaIcon.left
+            anchors.top: parent.top
+            anchors.bottom: parent.bottom
+            anchors.margins: platformStyle.paddingLarge
+
+            Text {
+                id: titleAreaText
+
+                LayoutMirroring.enabled: root.LayoutMirroring.enabled
+
+                anchors.fill: parent
+                font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeLarge }
+                color: root.platformInverted ? platformStyle.colorNormalLinkInverted
+                                             : platformStyle.colorNormalLink
+                elide: Text.ElideRight
+                horizontalAlignment: Text.AlignLeft
+                verticalAlignment: Text.AlignVCenter
+            }
+        }
+
+        Image {
+            id: titleAreaIcon
+
+            anchors.right: parent.right
+            anchors.rightMargin: platformStyle.paddingLarge
+            anchors.verticalCenter: parent.verticalCenter
+            source: internal.iconSource()
+            sourceSize.height: platformStyle.graphicSizeSmall
+            sourceSize.width: platformStyle.graphicSizeSmall
+
+            MouseArea {
+                id: iconMouseArea
+
+                property bool pressCancelled
+
+                anchors.centerIn: parent
+                width: parent.width + 2 * platformStyle.paddingLarge
+                height: parent.height + 2 * platformStyle.paddingLarge
+                enabled: privateCloseIcon && root.status == DialogStatus.Open
+
+                onPressed: {
+                    pressCancelled = false
+                    privateStyle.play(Android.BasicButton)
+                }
+                onClicked: {
+                    if (!pressCancelled)
+                        root.reject()
+                }
+                onReleased: {
+                    if (!pressCancelled)
+                        privateStyle.play(Android.PopupClose)
+                }
+                onExited: pressCancelled = true
+            }
+        }
+    }
+
+    buttons: Item {
+        id: buttonContainer
+
+        LayoutMirroring.enabled: false
+        LayoutMirroring.childrenInherit: true
+
+        width: parent.width
+        height: buttonTexts.length ? privateStyle.toolBarHeightLandscape + 2 * platformStyle.paddingSmall : 0
+
+        Row {
+            id: buttonRow
+            objectName: "buttonRow"
+            anchors.centerIn: parent
+            spacing: platformStyle.paddingMedium
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ContextMenu.qml b/mardrone/imports/com/nokia/android.1.1/ContextMenu.qml
new file mode 100644 (file)
index 0000000..508ca5c
--- /dev/null
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    default property alias content: menu.content
+    property Item visualParent: null
+    property alias status: popup.status
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    function open() {
+        popup.open()
+    }
+
+    function close() {
+        popup.close()
+    }
+
+    visible: false
+    implicitWidth: {
+        if (screen.width < screen.height)
+            screen.width - 2 * platformStyle.paddingLarge
+        else
+            privateStyle.dialogMaxSize
+    }
+
+    Popup {
+        id: popup
+
+        animationDuration: 250
+        state: "Hidden"
+        anchors.centerIn: parent
+        visible: status != DialogStatus.Closed
+        enabled: status == DialogStatus.Open
+        width: root.width
+        height: menu.height
+        platformInverted: root.platformInverted
+
+        onFaderClicked: {
+            privateStyle.play(Android.PopupClose)
+            close()
+        }
+
+        MenuContent {
+            id: menu
+            containingPopup: popup
+            width: parent.width
+            platformInverted: root.platformInverted
+            onItemClicked: popup.close()
+        }
+
+        states: [
+            State {
+                name: "Hidden"
+                when: status == DialogStatus.Closing || status == DialogStatus.Closed
+                PropertyChanges { target: popup; opacity: 0 }
+            },
+            State {
+                name: "Visible"
+                when: status == DialogStatus.Opening || status == DialogStatus.Open
+                PropertyChanges { target: popup; opacity: 1 }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                from: "Visible"; to: "Hidden"
+                SequentialAnimation {
+                    NumberAnimation { properties: "opacity"; duration: popup.animationDuration; easing.type: Easing.Linear }
+                    PropertyAction { target: popup; property: "status"; value: DialogStatus.Closed }
+                }
+            },
+            Transition {
+                from: "Hidden"; to: "Visible"
+                SequentialAnimation {
+                    NumberAnimation { properties: "opacity"; duration: popup.animationDuration; easing.type: Easing.Linear }
+                    PropertyAction { target: popup; property: "status"; value: DialogStatus.Open }
+                }
+            }
+        ]
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Dialog.qml b/mardrone/imports/com/nokia/android.1.1/Dialog.qml
new file mode 100644 (file)
index 0000000..bb0da32
--- /dev/null
@@ -0,0 +1,270 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    property alias title: titleItem.children
+    property alias content: contentItem.children
+    property alias buttons: buttonItem.children
+    property alias visualParent: dialog.visualParent
+    property alias status: dialog.status
+
+    // Symbian specific API
+    property bool platformInverted: false
+    // read-only
+    property int platformContentMaximumWidth: dialog.width
+    // read-only
+    property int platformContentMaximumHeight:
+        dialog.maxHeight() - titleBar.height - buttonItem.height
+
+    property alias privateTitleHeight: titleBar.height
+    property alias privateButtonsHeight: buttonItem.height
+
+    signal accepted
+    signal rejected
+    signal clickedOutside
+
+    function open() {
+        dialog.open()
+        dialog.focus = true
+    }
+
+    function accept() {
+        if (status == DialogStatus.Open) {
+            dialog.close()
+            accepted()
+        }
+    }
+
+    function reject() {
+        if (status == DialogStatus.Open) {
+            dialog.close()
+            rejected()
+        }
+    }
+
+    function close() {
+        dialog.close()
+    }
+
+    visible: false
+
+    Popup {
+        id: dialog
+
+        function defaultHeight() {
+            if (root.height > 0)
+                return root.height
+            else
+                return titleBar.height + contentItem.childrenRect.height + buttonItem.height
+        }
+
+        function defaultWidth() {
+            return root.width > 0 ? root.width : maxWidth()
+        }
+
+        function maxWidth() {
+            return Math.min(screen.width - 2 * platformStyle.paddingMedium, privateStyle.dialogMaxSize)
+        }
+
+        function maxHeight() {
+            return inputContext.visible
+                    ? Math.min(privateStyle.dialogMaxSize, screen.height-inputContext.height)
+                    : Math.min(privateStyle.dialogMaxSize, screen.height-2*platformStyle.paddingMedium);
+        }
+
+        function minWidth() {
+            return Math.min(screen.displayWidth, screen.displayHeight) - 2 * platformStyle.paddingMedium
+        }
+
+        function minHeight() {
+            return inputContext.visible
+                    ? Math.min(privateStyle.dialogMinSize, screen.height-inputContext.height)
+                    : Math.min(privateStyle.dialogMinSize, screen.height-2*platformStyle.paddingMedium);
+        }
+
+        function updateSize() {
+            width = Math.max(Math.min(defaultWidth(), maxWidth()), minWidth())
+            height = Math.max(Math.min(defaultHeight(), maxHeight()), minHeight())
+        }
+
+        Connections { target: inputContext; onVisibleChanged: updateTimer.start() }
+        Connections { target: screen; onHeightChanged: updateTimer.start(); onWidthChanged: updateTimer.start() }
+        Connections { target: root; onHeightChanged: updateTimer.start(); onWidthChanged: updateTimer.start() }
+
+        Component.onCompleted: updateTimer.start()
+
+        Timer {
+            id: updateTimer
+            interval: 1; repeat: false
+            onTriggered: dialog.updateSize()
+        }
+
+        onFaderClicked: root.clickedOutside()
+
+        width: 0 // Defined in updateSize()
+        height: 0 // Defined in updateSize()
+
+        state: "Hidden"
+        visible: true
+        anchors.centerIn: parent
+        animationDuration: 250
+        platformInverted: root.platformInverted
+
+        // Consume all key events that are not processed by children
+        Keys.onPressed: event.accepted = true
+        Keys.onReleased: event.accepted = true
+
+        BorderImage {
+            source: privateStyle.imagePath("qtg_fr_popup", root.platformInverted)
+            border { left: 20; top: 20; right: 20; bottom: 20 }
+            anchors.fill: parent
+        }
+
+        Item {
+            id: titleBar
+
+            height: platformStyle.graphicSizeSmall + 2 * platformStyle.paddingLarge
+            anchors {
+                top: parent.top
+                left: parent.left
+                right: parent.right
+            }
+
+            BorderImage {
+                source: privateStyle.imagePath("qtg_fr_popup_heading", root.platformInverted)
+                border { left: 40; top: 0; right: 40; bottom: 0 }
+                anchors.fill: parent
+            }
+
+            Item {
+                id: titleItem
+
+                clip: true
+                anchors {
+                    fill: parent
+                }
+            }
+        }
+
+        Item {
+            id: contentItem
+
+            clip: true
+            anchors {
+                top: titleBar.bottom
+                left: parent.left
+                right: parent.right
+                bottom: buttonItem.top
+            }
+        }
+
+        Item {
+            id: buttonItem
+
+            height: childrenRect.height
+            clip: true
+            anchors {
+                left: parent.left
+                right: parent.right
+                bottom: parent.bottom
+            }
+        }
+
+        states: [
+            State {
+                name: "Visible"
+                when: status == DialogStatus.Opening || status == DialogStatus.Open
+                PropertyChanges { target: dialog; opacity: 1.0; scale: 1; }
+            },
+            State {
+                name: "Hidden"
+                when: status == DialogStatus.Closing || status == DialogStatus.Closed
+                PropertyChanges { target: dialog; opacity: 0.0; scale: 0.9; }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                from: "Visible"; to: "Hidden"
+                SequentialAnimation {
+                    ScriptAction {script: status = DialogStatus.Closing }
+                    ParallelAnimation {
+                        NumberAnimation {
+                            properties: "opacity"
+                            duration: dialog.animationDuration
+                            easing.type: Easing.Linear
+                        }
+                        NumberAnimation {
+                            property: "scale"
+                            duration: dialog.animationDuration
+                            easing.type: Easing.InQuad
+                        }
+                    }
+                    ScriptAction {script: status = DialogStatus.Closed }
+                }
+            },
+            Transition {
+                from: "Hidden"; to: "Visible"
+                SequentialAnimation {
+                    ScriptAction { script: status = DialogStatus.Opening }
+                    PropertyAction { target: dialog; property: "visible"; value: true}
+                    ParallelAnimation {
+                        NumberAnimation {
+                            properties: "opacity"
+                            duration: dialog.animationDuration
+                            easing.type: Easing.Linear
+                        }
+                        NumberAnimation {
+                            property: "scale"
+                            duration: dialog.animationDuration
+                            easing.type: Easing.OutQuad
+                        }
+                    }
+                    ScriptAction { script: status = DialogStatus.Open }
+                }
+            }
+        ]
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Fader.qml b/mardrone/imports/com/nokia/android.1.1/Fader.qml
new file mode 100644 (file)
index 0000000..0f01fac
--- /dev/null
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    property real dimm: 0.8
+    property int animationDuration: 500
+    property Item visualParent: null
+    property bool platformInverted: false // supports inverted, but looks the same
+
+    signal clicked
+
+    anchors.fill: parent
+    anchors.bottomMargin: inputContext.visible ? inputContext.height : 0
+    state: "Hidden"
+
+    Rectangle {
+        id: fader
+
+        anchors { top: parent.top; left: parent.left; right: parent.right }
+        height: parent.height + parent.anchors.bottomMargin
+        opacity: 0.0
+        color: "black"
+
+        //eat mouse events
+        MouseArea {
+            id: mouseEventEater
+            anchors.fill: parent
+            enabled: fader.opacity != 0.0
+            onClicked: root.clicked()
+        }
+    }
+
+    states: [
+        State {
+            name: "Visible"
+            PropertyChanges { target: fader; opacity: dimm }
+        },
+        State {
+            name: "Hidden"
+            PropertyChanges { target: fader; opacity: 0.0 }
+        }
+    ]
+
+    transitions: [
+        Transition {
+            from: "Hidden"; to: "Visible"
+            NumberAnimation { properties: "opacity"; duration: animationDuration; easing.type: Easing.Linear }
+        },
+        Transition {
+            from: "Visible"; to: "Hidden"
+            NumberAnimation { properties: "opacity"; duration: animationDuration; easing.type: Easing.Linear }
+        }
+    ]
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Label.qml b/mardrone/imports/com/nokia/android.1.1/Label.qml
new file mode 100644 (file)
index 0000000..ada093a
--- /dev/null
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** 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
+
+Text {
+    property bool platformInverted: false
+    color: platformInverted ? platformStyle.colorNormalLightInverted
+                            : platformStyle.colorNormalLight
+    font.family: platformStyle.fontFamilyRegular
+    font.pixelSize: platformStyle.fontSizeMedium
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ListHeading.qml b/mardrone/imports/com/nokia/android.1.1/ListHeading.qml
new file mode 100644 (file)
index 0000000..e27080e
--- /dev/null
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+    property alias paddingItem: paddingItem // Read-only
+    property bool platformInverted: false
+
+    implicitWidth: ListView.view ? ListView.view.width : screen.width
+    implicitHeight: platformStyle.graphicSizeSmall
+
+    BorderImage {
+        source: privateStyle.imagePath("qtg_fr_list_heading_normal", root.platformInverted)
+        border { left: 28; top: 0; right: 28; bottom: 0 }
+        smooth: true
+        anchors.fill: parent
+    }
+
+    Item {
+        // non-visible item to create a padding boundary that content items can bind to
+        id: paddingItem
+        anchors {
+            fill: parent
+            leftMargin: platformStyle.paddingLarge
+            rightMargin: privateStyle.scrollBarThickness
+            topMargin: platformStyle.paddingSmall
+            bottomMargin: platformStyle.paddingSmall
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ListItem.qml b/mardrone/imports/com/nokia/android.1.1/ListItem.qml
new file mode 100644 (file)
index 0000000..072e6a4
--- /dev/null
@@ -0,0 +1,384 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+    property string mode: "normal" // Read-only
+    property alias paddingItem: paddingItem // Read-only
+
+    property bool enabled: true
+    property bool subItemIndicator: false
+    property bool platformInverted: false
+
+    signal clicked
+    signal pressAndHold
+
+    implicitWidth: ListView.view ? ListView.view.width : screen.width
+    implicitHeight: platformStyle.graphicSizeLarge
+
+    Item {
+        id: background
+        anchors.fill: parent
+
+        Rectangle {
+            height: 1
+            color: root.platformInverted ? platformStyle.colorDisabledLightInverted
+                                         : platformStyle.colorDisabledMid
+            anchors {
+                bottom: parent.bottom
+                left: parent.left
+                right: parent.right
+            }
+        }
+        Loader {
+            id: faderLoader
+            opacity: 0
+            anchors.fill: background
+            sourceComponent: root.mode != "normal" && root.mode != "pressed" ? fader : undefined
+        }
+
+        BorderImage {
+            id: highlight
+            border {
+                left: platformStyle.borderSizeMedium
+                top: platformStyle.borderSizeMedium
+                right: platformStyle.borderSizeMedium
+                bottom: platformStyle.borderSizeMedium
+            }
+            opacity: 0
+            anchors.fill: background
+        }
+    }
+
+    Component {
+        id: fader
+
+        BorderImage {
+            source: privateStyle.imagePath("qtg_fr_list_" + mode, root.platformInverted)
+            border {
+                left: platformStyle.borderSizeMedium
+                top: platformStyle.borderSizeMedium
+                right: platformStyle.borderSizeMedium
+                bottom: platformStyle.borderSizeMedium
+            }
+        }
+    }
+
+    MouseArea {
+        id: mouseArea
+        anchors.fill: parent
+        enabled: root.enabled
+        onPressed: {
+            android.listInteractionMode = Android.TouchInteraction
+            internal.state = "Pressed"
+        }
+        onClicked: {
+            internal.state = ""
+            root.clicked()
+        }
+        onCanceled: {
+            internal.state = "Canceled"
+        }
+        onPressAndHold: {
+            internal.state = "PressAndHold"
+        }
+        onReleased: {
+            internal.state = ""
+        }
+        onExited: {
+            internal.state = ""
+        }
+    }
+
+    Loader {
+        id: iconLoader
+        sourceComponent: root.subItemIndicator ? subItemIcon : undefined
+        anchors {
+            right: parent.right
+            rightMargin: privateStyle.scrollBarThickness
+            verticalCenter: parent.verticalCenter
+        }
+    }
+
+    Component {
+        id: subItemIcon
+
+        Image {
+            source: privateStyle.imagePath(
+                root.enabled ? "qtg_graf_drill_down_indicator"
+                             : "qtg_graf_drill_down_indicator_disabled",
+                root.platformInverted)
+            mirror: LayoutMirroring.enabled
+            sourceSize.width: platformStyle.graphicSizeSmall
+            sourceSize.height: platformStyle.graphicSizeSmall
+        }
+    }
+
+    Keys.onReleased: {
+        if (!event.isAutoRepeat && root.enabled) {
+            if (event.key == Qt.Key_Select || event.key == Qt.Key_Return || event.key == Qt.Key_Enter) {
+                event.accepted = true
+                internal.state = "Focused"
+            }
+        }
+    }
+
+    Keys.onPressed: {
+        if (!event.isAutoRepeat) {
+            switch (event.key) {
+                case Qt.Key_Select:
+                case Qt.Key_Enter:
+                case Qt.Key_Return: {
+                    if (android.listInteractionMode != Android.KeyNavigation)
+                        android.listInteractionMode = Android.KeyNavigation
+                    else
+                        if (root.enabled) {
+                            highlight.source = privateStyle.imagePath("qtg_fr_list_pressed",
+                                                                      root.platformInverted)
+                            highlight.opacity = 1
+                            releasedEffect.restart()
+                            root.clicked()
+                        }
+                    event.accepted = true
+                    break
+                }
+
+                case Qt.Key_Up: {
+                    if (android.listInteractionMode != Android.KeyNavigation) {
+                        android.listInteractionMode = Android.KeyNavigation
+                        internal.state = "Focused"
+                        ListView.view.positionViewAtIndex(index, ListView.Beginning)
+                    } else
+                        ListView.view.decrementCurrentIndex()
+                    event.accepted = true
+                    break
+                }
+
+                case Qt.Key_Down: {
+                    if (android.listInteractionMode != Android.KeyNavigation) {
+                        android.listInteractionMode = Android.KeyNavigation
+                        ListView.view.positionViewAtIndex(index, ListView.Beginning)
+                        internal.state = "Focused"
+                    } else
+                        ListView.view.incrementCurrentIndex()
+                    event.accepted = true
+                    break
+                }
+                default: {
+                    event.accepted = false
+                    break
+                }
+            }
+        }
+
+        if (event.key == Qt.Key_Up || event.key == Qt.Key_Down)
+            android.privateListItemKeyNavigation(ListView.view)
+    }
+
+    ListView.onRemove: SequentialAnimation {
+        PropertyAction { target: root; property: "ListView.delayRemove"; value: true }
+        ParallelAnimation {
+            SequentialAnimation {
+                PauseAnimation { duration: 50 }
+                NumberAnimation {
+                    target: root
+                    property: "height"
+                    to: 0
+                    duration: 200
+                    easing.type: Easing.OutQuad
+                }
+            }
+            NumberAnimation {
+                target: root
+                property: "opacity"
+                from: 1
+                to: 0
+                duration: 200
+                easing.type: Easing.Linear
+            }
+        }
+        PropertyAction { target: root; property: "ListView.delayRemove"; value: false }
+    }
+
+    ListView.onAdd: SequentialAnimation {
+        PropertyAction { target: root; property: "height"; value: 0 }
+        ParallelAnimation {
+            NumberAnimation {
+                target: root
+                property: "height"
+                to: root.height
+                duration: 250
+                easing.type: Easing.OutQuad
+            }
+            NumberAnimation {
+                target: root
+                property: "opacity"
+                from: 0
+                to: 1
+                duration: 250
+                easing.type: Easing.Linear
+            }
+        }
+    }
+
+    SequentialAnimation {
+        id: releasedEffect
+        PropertyAnimation {
+            target: highlight
+            property: "opacity"
+            to: 0
+            easing.type: Easing.Linear
+            duration: 150
+        }
+    }
+
+    Item {
+        // non-visible item to create a padding boundary that content items can bind to
+        id: paddingItem
+        anchors {
+            fill: parent
+            leftMargin: platformStyle.paddingLarge
+            rightMargin: iconLoader.status == Loader.Ready ?
+                    privateStyle.scrollBarThickness + iconLoader.width + platformStyle.paddingMedium :
+                    privateStyle.scrollBarThickness
+            topMargin: platformStyle.paddingLarge
+            bottomMargin: platformStyle.paddingLarge
+        }
+    }
+
+    StateGroup {
+        id: internal
+
+        function getMode() {
+            if (internal.state == "Pressed" || internal.state == "PressAndHold")
+                return "pressed"
+            else if (internal.state == "Focused")
+                return "highlighted"
+            else if (internal.state == "Disabled")
+                return "disabled"
+            else
+                return "normal"
+        }
+
+        // Performance optimization:
+        // Use value assignment when property changes instead of binding to js function
+        onStateChanged: { root.mode = internal.getMode() }
+
+        function press() {
+            privateStyle.play(Android.BasicItem)
+            highlight.source = privateStyle.imagePath("qtg_fr_list_pressed", root.platformInverted)
+            highlight.opacity = 1
+            if (root.ListView.view)
+                root.ListView.view.currentIndex = index
+        }
+
+        function release() {
+            if (android.listInteractionMode != Android.KeyNavigation)
+                privateStyle.play(Android.BasicItem)
+            releasedEffect.restart()
+        }
+
+        function releaseHold() {
+            releasedEffect.restart()
+        }
+
+        function hold() {
+            root.pressAndHold()
+        }
+
+        function disable() {
+            faderLoader.opacity = 1
+        }
+
+        function focus() {
+            faderLoader.opacity = 1
+        }
+
+        function canceled() {
+            releasedEffect.restart()
+        }
+
+        states: [
+            State { name: "Pressed" },
+            State { name: "PressAndHold" },
+            State { name: "Disabled"; when: !root.enabled },
+            State { name: "Focused"; when: (root.ListView.isCurrentItem &&
+                android.listInteractionMode == Android.KeyNavigation) },
+            State { name: "Canceled" },
+            State { name: "" }
+        ]
+
+        transitions: [
+            Transition {
+                to: "Pressed"
+                ScriptAction { script: internal.press() }
+            },
+            Transition {
+                from: "Pressed"
+                to: "PressAndHold"
+                ScriptAction { script: internal.hold() }
+            },
+            Transition {
+                from: "PressAndHold"
+                to: ""
+                ScriptAction { script: internal.releaseHold() }
+            },
+            Transition {
+                to: ""
+                ScriptAction { script: internal.release() }
+            },
+            Transition {
+                to: "Disabled"
+                ScriptAction { script: internal.disable() }
+            },
+            Transition {
+                to: "Focused"
+                ScriptAction { script: internal.focus() }
+            },
+            Transition {
+                to: "Canceled"
+                ScriptAction { script: internal.canceled() }
+            }
+        ]
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ListItemText.qml b/mardrone/imports/com/nokia/android.1.1/ListItemText.qml
new file mode 100644 (file)
index 0000000..2469139
--- /dev/null
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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
+
+Text {
+    id: root
+    property string mode: "normal"
+    property string role: "Title"
+    property bool platformInverted: false
+
+    // Also role "Heading" taken into account although not explicitely used in evaluations below
+    font {
+        family: platformStyle.fontFamilyRegular
+        pixelSize: (role == "Title" || role == "SelectionTitle") ? platformStyle.fontSizeLarge : platformStyle.fontSizeSmall
+        weight: (role == "SubTitle" || role == "SelectionSubTitle") ? Font.Light : Font.Normal
+    }
+    color: internal.normalColor
+    elide: Text.ElideRight
+    horizontalAlignment: root.role != "Heading" ? Text.AlignLeft : Text.AlignRight
+
+    // Performance optimization:
+    // Use value assignment when property changes instead of binding to js function
+    onModeChanged: { color = internal.getColor() }
+
+    QtObject {
+        id: internal
+
+        // Performance optmization:
+        // Use tertiary operations even though it doesn't look that good
+        property color colorMid: root.platformInverted ? platformStyle.colorNormalMidInverted
+                                                       : platformStyle.colorNormalMid
+        property color colorLight: root.platformInverted ? platformStyle.colorNormalLightInverted
+                                                         : platformStyle.colorNormalLight
+        property color normalColor: (root.role == "SelectionSubTitle" || root.role == "SubTitle")
+                                    ? colorMid : colorLight
+
+        function getColor() {
+            if (root.mode == "pressed")
+                return root.platformInverted ? platformStyle.colorPressedInverted
+                                             : platformStyle.colorPressed
+            else if (root.mode == "highlighted")
+                return root.platformInverted ? platformStyle.colorHighlightedInverted
+                                             : platformStyle.colorHighlighted
+            else if (root.mode == "disabled")
+                return root.platformInverted ? platformStyle.colorDisabledLightInverted
+                                             : platformStyle.colorDisabledLight
+            else 
+                return normalColor
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Menu.qml b/mardrone/imports/com/nokia/android.1.1/Menu.qml
new file mode 100644 (file)
index 0000000..6732e4b
--- /dev/null
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    default property alias content: menu.content
+    property Item visualParent: null
+    property alias status: popup.status
+    property bool platformInverted: false
+
+    function open() {
+        popup.open()
+    }
+
+    function close() {
+        popup.close()
+    }
+
+    visible: false
+
+    Popup {
+        id: popup
+        objectName: "OptionsMenu"
+
+        y: screen.height - popup.height
+        animationDuration: 200
+        state: "Hidden"
+        visible: status != DialogStatus.Closed
+        enabled: status == DialogStatus.Open
+        width: screen.width
+        height: menuContainer.height
+        clip: true
+        platformInverted: root.platformInverted
+
+        onFaderClicked: {
+            privateStyle.play(Android.PopupClose)
+            close()
+        }
+
+        BorderImage {
+            id: menuContainer
+
+            property int borderSize: Math.round(platformStyle.borderSizeMedium * 1.5)
+
+            source: privateStyle.imagePath("qtg_fr_popup_options", root.platformInverted)
+            border { left: borderSize; top: borderSize; right: borderSize; bottom: borderSize }
+
+            width: parent.width
+            height: menu.height + 2 * platformStyle.paddingLarge
+
+            MenuContent {
+                id: menu
+                containingPopup: popup
+                anchors { top: parent.top; left: parent.left; right: parent.right
+                          topMargin: platformStyle.paddingLarge
+                          leftMargin: platformStyle.paddingLarge
+                          rightMargin: platformStyle.paddingLarge }
+                platformInverted: root.platformInverted
+                onItemClicked: popup.close()
+            }
+
+            BorderImage {
+                source: privateStyle.imagePath("qtg_fr_popup_options_overlay", root.platformInverted)
+                anchors.fill: parent
+                border { left: menuContainer.borderSize; top: menuContainer.borderSize
+                         right: menuContainer.borderSize; bottom: menuContainer.borderSize }
+            }
+        }
+
+        states: [
+            State {
+                name: "Hidden"
+                when: status == DialogStatus.Closing || status == DialogStatus.Closed
+                PropertyChanges { target: menuContainer; y: menuContainer.height; opacity: 0 }
+            },
+            State {
+                name: "Visible"
+                when: status == DialogStatus.Opening || status == DialogStatus.Open
+                PropertyChanges { target: menuContainer; y: 0; opacity: 1 }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                from: "Visible"; to: "Hidden"
+                SequentialAnimation {
+                    ParallelAnimation {
+                        NumberAnimation {
+                            target: menuContainer
+                            property: "y"
+                            duration: popup.animationDuration
+                            easing.type: Easing.Linear
+                        }
+                        NumberAnimation {
+                            target: menuContainer
+                            property: "opacity"
+                            duration: popup.animationDuration
+                            easing.type: Easing.Linear
+                        }
+                    }
+                    PropertyAction { target: popup; property: "status"; value: DialogStatus.Closed }
+                }
+            },
+            Transition {
+                from: "Hidden"; to: "Visible"
+                SequentialAnimation {
+                    ParallelAnimation {
+                        NumberAnimation {
+                            target: menuContainer
+                            property: "y"
+                            duration: popup.animationDuration
+                            easing.type: Easing.OutQuad
+                        }
+                        NumberAnimation {
+                            target: menuContainer
+                            property: "opacity"
+                            duration: popup.animationDuration
+                            easing.type: Easing.Linear
+                        }
+                    }
+                    PropertyAction { target: popup; property: "status"; value: DialogStatus.Open }
+                }
+            }
+        ]
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/MenuContent.qml b/mardrone/imports/com/nokia/android.1.1/MenuContent.qml
new file mode 100644 (file)
index 0000000..5e14c40
--- /dev/null
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    property alias content: contentArea.children
+    property Popup containingPopup: null
+    property bool platformInverted: false
+
+    signal itemClicked()
+
+    height: flickableArea.height
+
+    QtObject {
+        id: internal
+        // Add padding to align content area top and bottom with rounded background graphics.
+        // Optionsmenu uses mask, thus no padding needed
+        property int clipMargin: containingPopup.objectName == "OptionsMenu" ? 0 : platformStyle.paddingSmall
+        property int preferredHeight: privateStyle.menuItemHeight * ((screen.width < screen.height) ? 5 : 3)
+    }
+
+    BorderImage {
+        source: containingPopup.objectName == "OptionsMenu"
+                ? privateStyle.imagePath("qtg_fr_popup_options", root.platformInverted)
+                : privateStyle.imagePath("qtg_fr_popup", root.platformInverted)
+        border { left: 20; top: 20; right: 20; bottom: 20 }
+        anchors.fill: parent
+    }
+
+    Item {
+        id: clipItem
+        y: internal.clipMargin
+        height: flickableArea.height - 2 * internal.clipMargin
+        width: root.width
+        clip: true
+        Flickable {
+            id: flickableArea
+
+            property int index: 0
+            property bool itemAvailable: (contentArea.children[0] != undefined) && (contentArea.children[0].children[0] != undefined)
+            property int itemHeight: itemAvailable ? Math.max(1, contentArea.children[0].children[0].height) : 1
+            property int itemsHidden: Math.floor(contentY / itemHeight)
+            property int interactionMode: android.listInteractionMode
+
+            y: -internal.clipMargin
+            height: contentArea.height; width: root.width
+            clip: true
+            contentHeight: (contentArea.children[0] != undefined) ? contentArea.children[0].height : 0
+            contentWidth: width
+
+            Item {
+                id: contentArea
+
+                width: flickableArea.width
+                height: flickableArea.contentHeight > internal.preferredHeight
+                    ? internal.preferredHeight - (internal.preferredHeight % flickableArea.itemHeight)
+                    : flickableArea.contentHeight
+
+                function setChildrenWidths() {
+                    for (var i = 0; i < children.length; ++i)
+                        children[i].width = width
+                }
+
+                function connectChildren() {
+                    for (var i = 0; i < children.length; ++i) {
+                        if (children[i].clicked != undefined)
+                            children[i].clicked.connect(root.itemClicked)
+                    }
+                }
+
+                onWidthChanged: setChildrenWidths()
+                onChildrenChanged: {
+                    setChildrenWidths()
+                    connectChildren()
+                }
+            }
+
+            onVisibleChanged: {
+                enabled = visible
+                if (itemAvailable)
+                    contentArea.children[0].children[0].focus = visible
+                contentY = 0
+                index = 0
+            }
+
+            onItemsHiddenChanged: {
+                // Check that popup is really open in order to prevent unnecessary feedback
+                if (containingPopup.status == DialogStatus.Open
+                    && android.listInteractionMode == Android.TouchInteraction)
+                    privateStyle.play(Android.ItemScroll)
+            }
+
+            onInteractionModeChanged: {
+                if (android.listInteractionMode == Android.KeyNavigation) {
+                    contentY = 0
+                    scrollBar.flash(Android.FadeOut)
+                    if (itemAvailable)
+                        contentArea.children[0].children[index].focus = true
+                } else if (android.listInteractionMode == Android.TouchInteraction) {
+                    index = 0
+                }
+            }
+
+            Keys.onPressed: {
+                if (itemAvailable && (event.key == Qt.Key_Down || event.key == Qt.Key_Up)) {
+                    if (event.key == Qt.Key_Down && index < contentArea.children[0].children.length - 1) {
+                        index++
+                        if (index * itemHeight > contentY + height - itemHeight) {
+                            contentY = index * itemHeight - height + itemHeight
+                        }
+                    } else if (event.key == Qt.Key_Up && index > 0) {
+                        index--
+                        if (index * itemHeight < contentY) {
+                            contentY = index * itemHeight
+                        }
+                    }
+                    scrollBar.flash(Android.FadeOut)
+                    contentArea.children[0].children[index].focus = true
+                    event.accepted = true
+                }
+            }
+        }
+    }
+
+    ScrollBar {
+        id: scrollBar
+        objectName: "scrollBar"
+        flickableItem: flickableArea
+        interactive: false
+        visible: flickableArea.height < flickableArea.contentHeight
+        anchors {
+            top: clipItem.top
+            bottom: clipItem.bottom
+            right: clipItem.right
+        }
+        platformInverted: root.platformInverted
+    }
+
+    Connections {
+        target: containingPopup
+        onStatusChanged: {
+            if (containingPopup.status == DialogStatus.Open)
+                scrollBar.flash(Android.FadeInFadeOut)
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/MenuItem.qml b/mardrone/imports/com/nokia/android.1.1/MenuItem.qml
new file mode 100644 (file)
index 0000000..d93018c
--- /dev/null
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    property alias text: textArea.text
+
+    // Symbian specific API
+    property bool platformInverted: false
+    property bool platformSubItemIndicator: false
+    property real platformLeftMargin: platformStyle.paddingLarge
+
+    signal clicked
+
+    width: parent.width; height: privateStyle.menuItemHeight
+
+    QtObject {
+        id: internal
+
+        function bg_postfix() {
+            if (activeFocus && android.listInteractionMode == Android.KeyNavigation)
+                return "highlighted"
+            else if (mouseArea.pressed && mouseArea.containsMouse && !mouseArea.canceled)
+                return "pressed"
+            else
+                return "popup_normal"
+        }
+
+        function textColor() {
+            if (!enabled)
+                return root.platformInverted ? platformStyle.colorDisabledLightInverted
+                                             : platformStyle.colorDisabledLight
+            else if (activeFocus && android.listInteractionMode == Android.KeyNavigation)
+                return root.platformInverted ? platformStyle.colorHighlightedInverted
+                                             : platformStyle.colorHighlighted
+            else if (mouseArea.pressed && mouseArea.containsMouse)
+                return root.platformInverted ? platformStyle.colorPressedInverted
+                                             : platformStyle.colorPressed
+            else
+                return root.platformInverted ? platformStyle.colorNormalLightInverted
+                                             : platformStyle.colorNormalLight
+        }
+    }
+
+    BorderImage {
+        source: privateStyle.imagePath("qtg_fr_list_" + internal.bg_postfix(), root.platformInverted)
+        border { left: 20; top: 20; right: 20; bottom: 20 }
+        anchors.fill: parent
+    }
+
+    Text {
+        id: textArea
+        anchors {
+            verticalCenter: parent.verticalCenter
+            left: parent.left
+            leftMargin: platformLeftMargin
+            right: iconLoader.status == Loader.Ready ? iconLoader.left : parent.right
+            rightMargin: iconLoader.status == Loader.Ready ? platformStyle.paddingMedium : privateStyle.scrollBarThickness
+        }
+        font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
+        color: internal.textColor()
+        horizontalAlignment: Text.AlignLeft
+        elide: Text.ElideRight
+    }
+
+    Loader {
+        id: iconLoader
+        sourceComponent: root.platformSubItemIndicator ? subItemIcon : undefined
+        anchors {
+            right: parent.right
+            rightMargin: privateStyle.scrollBarThickness
+            verticalCenter: parent.verticalCenter
+        }
+    }
+
+    Component {
+        id: subItemIcon
+
+        Image {
+            source: privateStyle.imagePath("qtg_graf_drill_down_indicator", platformInverted)
+            sourceSize.width: platformStyle.graphicSizeSmall
+            sourceSize.height: platformStyle.graphicSizeSmall
+            mirror: LayoutMirroring.enabled
+        }
+    }
+
+
+    MouseArea {
+        id: mouseArea
+
+        property bool canceled: false
+
+        anchors.fill: parent
+
+        onPressed: {
+            canceled = false
+            android.listInteractionMode = Android.TouchInteraction
+            privateStyle.play(Android.BasicItem)
+        }
+        onClicked: {
+            if (!canceled)
+                root.clicked()
+        }
+        onReleased: {
+            if (!canceled)
+                privateStyle.play(Android.PopupClose)
+        }
+        onExited: canceled = true
+    }
+
+    Keys.onPressed: {
+        event.accepted = true
+        switch (event.key) {
+            case Qt.Key_Select:
+            case Qt.Key_Enter:
+            case Qt.Key_Return: {
+                if (!event.isAutoRepeat) {
+                    if (android.listInteractionMode != Android.KeyNavigation)
+                        android.listInteractionMode = Android.KeyNavigation
+                    else
+                        root.clicked()
+                }
+                break
+            }
+
+            case Qt.Key_Up: {
+                if (android.listInteractionMode != Android.KeyNavigation) {
+                    android.listInteractionMode = Android.KeyNavigation
+                    if (ListView.view != null)
+                        ListView.view.positionViewAtIndex(index, ListView.Beginning)
+                } else {
+                    if (ListView.view != null)
+                        ListView.view.decrementCurrentIndex()
+                    else
+                        event.accepted = false
+                }
+                break
+            }
+
+            case Qt.Key_Down: {
+                if (android.listInteractionMode != Android.KeyNavigation) {
+                    android.listInteractionMode = Android.KeyNavigation
+                    if (ListView.view != null)
+                        ListView.view.positionViewAtIndex(index, ListView.Beginning)
+                } else {
+                    if (ListView.view != null)
+                        ListView.view.incrementCurrentIndex()
+                    else
+                        event.accepted = false
+                }
+                break
+            }
+            case Qt.Key_Left:
+            case Qt.Key_Right: {
+                // If this MenuItem belongs to Menu invoke highlight
+                // in other cases consume event but do nothing
+                if (ListView.view == null && android.listInteractionMode != Android.KeyNavigation)
+                    android.listInteractionMode = Android.KeyNavigation
+                break
+            }
+            default: {
+                event.accepted = false
+                break
+            }
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/MenuLayout.qml b/mardrone/imports/com/nokia/android.1.1/MenuLayout.qml
new file mode 100644 (file)
index 0000000..87df7a3
--- /dev/null
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** 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
+
+Column {
+    signal clicked
+
+    Component.onCompleted: {
+        for (var i = 0; i < children.length; ++i) {
+            if (children[i].clicked != undefined)
+                children[i].clicked.connect(clicked)
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Page.qml b/mardrone/imports/com/nokia/android.1.1/Page.qml
new file mode 100644 (file)
index 0000000..af2dec4
--- /dev/null
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    // The status of the page. One of the following:
+    //      PageStatus.Inactive - the page is not visible
+    //      PageStatus.Activating - the page is transitioning into becoming the active page
+    //      PageStatus.Active - the page is the current active page
+    //      PageStatus.Deactivating - the page is transitioning into becoming inactive
+    property int status: PageStatus.Inactive
+
+    property PageStack pageStack
+
+    // Defines orientation lock for a page
+    property int orientationLock: PageOrientation.Automatic
+
+    property Item tools: null
+
+    visible: false
+
+    width: visible && parent ? parent.width : internal.previousWidth
+    height: visible && parent ? parent.height : internal.previousHeight
+
+    onWidthChanged: internal.previousWidth = visible ? width : internal.previousWidth
+    onHeightChanged: internal.previousHeight = visible ? height : internal.previousHeight
+
+    onStatusChanged: {
+        if (status == PageStatus.Activating)
+            internal.orientationLockCheck();
+    }
+
+    onOrientationLockChanged: {
+        if (status == PageStatus.Activating || status == PageStatus.Active)
+            internal.orientationLockCheck();
+    }
+
+    // This is needed to make a parentless Page component visible in the Designer of QtCreator.
+    // It's done here instead of binding the visible property because binding it to a script
+    // block returning false would cause an element on the Page not to end up focused despite
+    // specifying focus=true inside the active focus scope. The focus would be gained and lost
+    // in createObject.
+    Component.onCompleted: if (!parent) visible = true
+
+    QtObject {
+        id: internal
+        property int previousWidth: 0
+        property int previousHeight: 0
+
+        function isScreenInPortrait() {
+            return screen.currentOrientation == Screen.Portrait || screen.currentOrientation == Screen.PortraitInverted;
+        }
+
+        function isScreenInLandscape() {
+            return screen.currentOrientation == Screen.Landscape || screen.currentOrientation == Screen.LandscapeInverted;
+        }
+
+        function orientationLockCheck() {
+            switch (orientationLock) {
+            case PageOrientation.Automatic:
+                screen.allowedOrientations = Screen.Default
+                break
+            case PageOrientation.LockPortrait:
+                screen.allowedOrientations = Screen.Portrait
+                break
+            case PageOrientation.LockLandscape:
+                screen.allowedOrientations = Screen.Landscape
+                break
+            case PageOrientation.LockPrevious:
+                screen.allowedOrientations = screen.currentOrientation
+                break
+            case PageOrientation.Manual:
+            default:
+                // Do nothing
+                // In manual mode it is expected that orientation is set
+                // explicitly to "screen.allowedOrientations" by the user.
+                break
+            }
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/PageStack.js b/mardrone/imports/com/nokia/android.1.1/PageStack.js
new file mode 100644 (file)
index 0000000..32f3136
--- /dev/null
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+// Page stack - items are page containers.
+var pageStack = [];
+
+// Returns the page stack depth.
+function getDepth() {
+    return pageStack.length;
+}
+
+// Pushes a page on the stack.
+function push(page, properties, replace, immediate) {
+    // page order sanity check
+    if ((!replace && page == currentPage)
+        || (replace && pageStack.length > 1
+        && page == pageStack[pageStack.length - 2].page)) {
+        throw new Error("Cannot navigate so that the resulting page stack has two consecutive entries of the same page instance.");
+    }
+
+    // figure out if more than one page is being pushed
+    var pages;
+    if (page instanceof Array) {
+        pages = page;
+        page = pages.pop();
+        if (page.createObject === undefined && page.parent === undefined && typeof page != "string") {
+            properties = properties || page.properties;
+            page = page.page;
+        }
+    }
+
+    // get the current container
+    var oldContainer = pageStack[pageStack.length - 1];
+
+    // pop the old container off the stack if this is a replace
+    if (oldContainer && replace) {
+        pageStack.pop();
+    }
+
+    // push any extra defined pages onto the stack
+    if (pages) {
+        var i;
+        for (i = 0; i < pages.length; i++) {
+            var tPage = pages[i];
+            var tProps;
+            if (tPage.createObject === undefined && tPage.parent === undefined && typeof tPage != "string") {
+                tProps = tPage.properties;
+                tPage = tPage.page;
+            }
+            pageStack.push(initPage(tPage, tProps));
+        }
+    }
+
+    // initialize the page
+    var container = initPage(page, properties);
+
+    // push the page container onto the stack
+    pageStack.push(container);
+
+    depth = pageStack.length;
+    currentPage = container.page;
+
+    // perform page transition
+    immediate = immediate || !oldContainer;
+    var orientationChange = false;
+    if (oldContainer) {
+        orientationChange = orientationChanges(oldContainer.page, container.page);
+        oldContainer.pushExit(replace, immediate, orientationChange);
+    }
+
+     // sync tool bar
+    var tools = container.page.tools || null;
+    if (toolBar) {
+        toolBar.setTools(tools, immediate ? "set" : replace ? "replace" : "push");
+    }
+
+    container.pushEnter(immediate, orientationChange);
+    return container.page;
+}
+
+// Initializes a page and its container.
+function initPage(page, properties) {
+    var container = containerComponent.createObject(root);
+
+    var pageComp;
+    if (page.createObject) {
+        // page defined as component
+        pageComp = page;
+    } else if (typeof page == "string") {
+        // page defined as string (a url)
+        pageComp = Qt.createComponent(page);
+    }
+    if (pageComp) {
+        if (pageComp.status == Component.Error) {
+            throw new Error("Error while loading page: " + pageComp.errorString());
+        } else {
+            // instantiate page from component
+            page = pageComp.createObject(container);
+        }
+    }
+
+    container.page = page;
+    container.owner = page.parent;
+
+    // the page has to be reparented if
+    if (page.parent != container) {
+        page.parent = container;
+    }
+
+    if (page.pageStack !== undefined) {
+        page.pageStack = root;
+    }
+
+    // copy properties to the page
+    for (var prop in properties) {
+        page[prop] = properties[prop];
+    }
+
+    return container;
+}
+
+// Pops a page off the stack.
+function pop(page, immediate) {
+    // make sure there are enough pages in the stack to pop
+    if (pageStack.length > 1) {
+        // pop the current container off the stack and get the next container
+        var oldContainer = pageStack.pop();
+        var container = pageStack[pageStack.length - 1];
+        if (page !== undefined) {
+            // an unwind target has been specified - pop until we find it
+            while (page != container.page && pageStack.length > 1) {
+                container.cleanup();
+                pageStack.pop();
+                container = pageStack[pageStack.length - 1];
+            }
+        }
+
+        depth = pageStack.length;
+        currentPage = container.page;
+
+        // perform page transition
+        var orientationChange = orientationChanges(oldContainer.page, container.page);
+        oldContainer.popExit(immediate, orientationChange);
+        container.popEnter(immediate, orientationChange);
+
+        // sync tool bar
+        var tools = container.page.tools || null;
+        if (toolBar) {
+            toolBar.setTools(tools, immediate ? "set" : "pop");
+        }
+        return oldContainer.page;
+    } else {
+        return null;
+    }
+}
+
+// Checks if the orientation changes between oldPage and newPage
+function orientationChanges(oldPage, newPage) {
+    return newPage.orientationLock != PageOrientation.Automatic
+           && newPage.orientationLock != PageOrientation.LockPrevious
+           && newPage.orientationLock != oldPage.orientationLock
+}
+
+// Clears the page stack.
+function clear() {
+    var container;
+    while (container = pageStack.pop()) {
+        container.cleanup();
+    }
+    depth = 0;
+    currentPage = null;
+}
+
+// Iterates through all pages in the stack (top to bottom) to find a page.
+function find(func) {
+    for (var i = pageStack.length - 1; i >= 0; i--) {
+        var page = pageStack[i].page;
+        if (func(page)) {
+            return page;
+        }
+    }
+    return null;
+}
+
diff --git a/mardrone/imports/com/nokia/android.1.1/PageStack.qml b/mardrone/imports/com/nokia/android.1.1/PageStack.qml
new file mode 100644 (file)
index 0000000..17e107b
--- /dev/null
@@ -0,0 +1,444 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+// The PageStack item defines a container for pages and a stack-based
+// navigation model. Pages can be defined as QML items or components.
+
+import QtQuick 1.1
+import "." 1.1
+
+import "PageStack.js" as Engine
+
+Item {
+    id: root
+
+    width: parent ? parent.width : 0
+    height: parent ? parent.height : 0
+
+    property int depth: Engine.getDepth()
+    property Item currentPage: null
+    property ToolBar toolBar
+    property variant initialPage
+
+    // Indicates whether there is an ongoing page transition.
+    property bool busy: internal.ongoingTransitionCount > 0
+
+    // Pushes a page on the stack.
+    // The page can be defined as a component, item or string.
+    // If an item is used then the page will get re-parented.
+    // If a string is used then it is interpreted as a url that is used to load a page component.
+    //
+    // The page can also be given as an array of pages. In this case all those pages will be pushed
+    // onto the stack. The items in the stack can be components, items or strings just like for single
+    // pages. Additionally an object can be used, which specifies a page and an optional properties
+    // property. This can be used to push multiple pages while still giving each of them properties.
+    // When an array is used the transition animation will only be to the last page.
+    //
+    // The properties argument is optional and allows defining a map of properties to set on the page.
+    // If the immediate argument is true then no transition animation is performed.
+    // Returns the page instance.
+    function push(page, properties, immediate) {
+        return Engine.push(page, properties, false, immediate);
+    }
+
+    // Pops a page off the stack.
+    // If page is specified then the stack is unwound to that page, to unwind to the first page specify
+    // page as null. If the immediate argument is true then no transition animation is performed.
+    // Returns the page instance that was popped off the stack.
+    function pop(page, immediate) {
+        return Engine.pop(page, immediate);
+    }
+
+    // Replaces a page on the stack.
+    // See push() for details.
+    function replace(page, properties, immediate) {
+        return Engine.push(page, properties, true, immediate);
+    }
+
+    // Clears the page stack.
+    function clear() {
+        return Engine.clear();
+    }
+
+    // Iterates through all pages (top to bottom) and invokes the specified function.
+    // If the specified function returns true the search stops and the find function
+    // returns the page that the iteration stopped at. If the search doesn't result
+    // in any page being found then null is returned.
+    function find(func) {
+        return Engine.find(func);
+    }
+
+    // Called when the page stack visibility changes.
+    onVisibleChanged: {
+        if (currentPage) {
+            internal.setPageStatus(currentPage, visible ? PageStatus.Active : PageStatus.Inactive);
+            if (visible)
+                currentPage.visible = currentPage.parent.visible = true;
+        }
+    }
+
+    onInitialPageChanged: {
+        if (initialPage) {
+            if (depth == 0)
+                push(initialPage, null, true)
+            else if (depth == 1)
+                replace(initialPage, null, true)
+            else
+                console.log("Cannot update PageStack.initialPage")
+        }
+    }
+
+    Component.onCompleted: {
+        if (initialPage && depth == 0)
+            push(initialPage, null, true)
+    }
+
+    QtObject {
+        id: internal
+
+        // The number of ongoing transitions.
+        property int ongoingTransitionCount: 0
+
+        // Sets the page status.
+        function setPageStatus(page, status) {
+            if (page != null) {
+                if (page.status !== undefined) {
+                    if (status == PageStatus.Active && page.status == PageStatus.Inactive)
+                        page.status = PageStatus.Activating;
+                    else if (status == PageStatus.Inactive && page.status == PageStatus.Active)
+                        page.status = PageStatus.Deactivating;
+
+                    page.status = status;
+                }
+            }
+        }
+    }
+
+    // Component for page containers.
+    Component {
+        id: containerComponent
+
+        Item {
+            id: container
+
+            width: parent ? parent.width : 0
+            height: parent ? parent.height : 0
+
+            // The states correspond to the different possible positions of the container.
+            state: "Hidden"
+
+            // The page held by this container.
+            property Item page: null
+
+            // The owner of the page.
+            property Item owner: null
+
+            // The width of the longer screen dimension
+            property int screenWidth: Math.max(screen.width, screen.height)
+
+            // Duration of transition animation (in ms)
+            property int transitionDuration: 200
+
+            // Flag that indicates the container should be cleaned up after the transition has ended.
+            property bool cleanupAfterTransition: false
+
+            // Flag that indicates if page transition animation is running
+            property bool transitionAnimationRunning: false
+
+            // State to be set after previous state change animation has finished
+            property string pendingState: "none"
+
+            // Ensures that transition finish actions are executed
+            // in case the object is destroyed before reaching the
+            // end state of an ongoing transition
+            Component.onDestruction: {
+                if (transitionAnimationRunning)
+                    transitionEnded();
+            }
+
+            // Sets pending state as current if state change is delayed
+            onTransitionAnimationRunningChanged: {
+                if (!transitionAnimationRunning && pendingState != "none") {
+                    state = pendingState;
+                    pendingState = "none";
+                }
+            }
+
+            // Handles state change depening on transition animation status
+            function setState(newState) {
+                if (transitionAnimationRunning)
+                    pendingState = newState;
+                else
+                    state = newState;
+            }
+
+            // Performs a push enter transition.
+            function pushEnter(immediate, orientationChanges) {
+                if (!immediate) {
+                    if (orientationChanges)
+                        setState("LandscapeRight");
+                    else
+                        setState("Right");
+                }
+                setState("");
+                page.visible = true;
+                if (root.visible && immediate)
+                    internal.setPageStatus(page, PageStatus.Active);
+            }
+
+            // Performs a push exit transition.
+            function pushExit(replace, immediate, orientationChanges) {
+                if (orientationChanges)
+                    setState(immediate ? "Hidden" : "LandscapeLeft");
+                else
+                    setState(immediate ? "Hidden" : "Left");
+                if (root.visible && immediate)
+                    internal.setPageStatus(page, PageStatus.Inactive);
+                if (replace) {
+                    if (immediate)
+                        cleanup();
+                    else
+                        cleanupAfterTransition = true;
+                }
+            }
+
+            // Performs a pop enter transition.
+            function popEnter(immediate, orientationChanges) {
+                if (!immediate)
+                    state = orientationChanges ? "LandscapeLeft" : "Left";
+                setState("");
+                page.visible = true;
+                if (root.visible && immediate)
+                    internal.setPageStatus(page, PageStatus.Active);
+            }
+
+            // Performs a pop exit transition.
+            function popExit(immediate, orientationChanges) {
+                if (orientationChanges)
+                    setState(immediate ? "Hidden" : "LandscapeRight");
+                else
+                    setState(immediate ? "Hidden" : "Right");
+
+                if (root.visible && immediate)
+                    internal.setPageStatus(page, PageStatus.Inactive);
+                if (immediate)
+                    cleanup();
+                else
+                    cleanupAfterTransition = true;
+            }
+
+            // Called when a transition has started.
+            function transitionStarted() {
+                transitionAnimationRunning = true;
+                internal.ongoingTransitionCount++;
+                if (root.visible)
+                    internal.setPageStatus(page, (state == "") ? PageStatus.Activating : PageStatus.Deactivating);
+            }
+
+            // Called when a transition has ended.
+            function transitionEnded() {
+                if (state != "")
+                    state = "Hidden";
+                if (root.visible)
+                    internal.setPageStatus(page, (state == "") ? PageStatus.Active : PageStatus.Inactive);
+
+                internal.ongoingTransitionCount--;
+                transitionAnimationRunning = false;
+                if (cleanupAfterTransition)
+                    cleanup();
+            }
+
+            states: [
+                // Explicit properties for default state.
+                State {
+                    name: ""
+                    PropertyChanges { target: container; visible: true; opacity: 1 }
+                },
+                // Start state for pop entry, end state for push exit.
+                State {
+                    name: "Left"
+                    PropertyChanges { target: container; x: -width / 2; opacity: 0 }
+                },
+                // Start state for pop entry, end state for push exit
+                // when exiting portrait and entering landscape.
+                State {
+                    name: "LandscapeLeft"
+                    PropertyChanges { target: container; x: -screenWidth / 2; opacity: 0 }
+                },
+                // Start state for push entry, end state for pop exit.
+                State {
+                    name: "Right"
+                    PropertyChanges { target: container; x: width / 2; opacity: 0 }
+                },
+                // Start state for push entry, end state for pop exit
+                // when exiting portrait and entering landscape.
+                State {
+                    name: "LandscapeRight"
+                    PropertyChanges { target: container; x: screenWidth / 2; opacity: 0 }
+                },
+                // Inactive state.
+                State {
+                    name: "Hidden"
+                    PropertyChanges { target: container; visible: false }
+                }
+            ]
+
+            transitions: [
+                // Push exit transition
+                Transition {
+                    from: ""; to: "Left"
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        ScriptAction { script: transitionEnded() }
+                    }
+                },
+                // Pop entry transition
+                Transition {
+                    from: "Left"; to: ""
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        ScriptAction { script: transitionEnded() }
+                    }
+                },
+                // Push exit transition landscape
+                Transition {
+                    from: ""; to: "LandscapeLeft"
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        ScriptAction { script: transitionEnded() }
+                    }
+                },
+                // Pop entry transition landscape
+                Transition {
+                    from: "LandscapeLeft"; to: ""
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        ScriptAction { script: transitionEnded() }
+                    }
+                },
+                // Pop exit transition
+                Transition {
+                    from: ""; to: "Right"
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        // Workaround for transition animation bug causing ghost view with page pop transition animation
+                        // TODO: Root cause still unknown
+                        PropertyAnimation {}
+                        ScriptAction { script: transitionEnded() }
+                    }
+                },
+                // Push entry transition
+                Transition {
+                    from: "Right"; to: ""
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        ScriptAction { script: transitionEnded() }
+                    }
+                },
+                // Pop exit transition landscape
+                Transition {
+                    from: ""; to: "LandscapeRight"
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.InQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        // Workaround for transition animation bug causing ghost view with page pop transition animation
+                        // TODO: Root cause still unknown
+                        PropertyAnimation {}
+                        ScriptAction { script: transitionEnded() }
+                    }
+                },
+                // Push entry transition landscape
+                Transition {
+                    from: "LandscapeRight"; to: ""
+                    SequentialAnimation {
+                        ScriptAction { script: transitionStarted() }
+                        ParallelAnimation {
+                            PropertyAnimation { properties: "x"; easing.type: Easing.OutQuad; duration: transitionDuration }
+                            PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: transitionDuration }
+                        }
+                        ScriptAction { script: transitionEnded() }
+                    }
+                }
+            ]
+
+            // Cleans up the container and then destroys it.
+            function cleanup() {
+                if (page != null) {
+                    if (page.status == PageStatus.Active)
+                        internal.setPageStatus(page, PageStatus.Inactive);
+                    if (owner != container) {
+                        // container is not the owner of the page - re-parent back to original owner
+                        page.visible = false;
+                        page.parent = owner;
+                    }
+                }
+                container.destroy();
+            }
+        }
+    }
+}
+
diff --git a/mardrone/imports/com/nokia/android.1.1/PageStackWindow.qml b/mardrone/imports/com/nokia/android.1.1/PageStackWindow.qml
new file mode 100644 (file)
index 0000000..a9f515f
--- /dev/null
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** 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
+
+Window {
+    id: root
+
+    property bool showStatusBar: true
+    property bool showToolBar: true
+    property variant initialPage
+    property alias pageStack: stack
+
+    property bool platformSoftwareInputPanelEnabled: false
+
+    Component.onCompleted: {
+        contentArea.initialized = true
+        if (initialPage && stack.depth == 0)
+            stack.push(initialPage, null, true)
+    }
+
+    onInitialPageChanged: {
+        if (initialPage && contentArea.initialized) {
+            if (stack.depth == 0)
+                stack.push(initialPage, null, true)
+            else if (stack.depth == 1)
+                stack.replace(initialPage, null, true)
+        }
+    }
+
+    onOrientationChangeStarted: {
+        statusBar.orientation = screen.currentOrientation
+    }
+
+    Item {
+        id: contentArea
+
+        property bool initialized: false
+
+        anchors {
+            top: sbar.bottom; bottom: sip.top;
+            left: parent.left; right: parent.right;
+        }
+
+        PageStack {
+            id: stack
+            anchors.fill: parent
+            toolBar: tbar
+        }
+    }
+
+    StatusBar {
+        id: sbar
+
+        width: parent.width
+        state: root.showStatusBar ? "Visible" : "Hidden"
+        platformInverted: root.platformInverted
+
+        states: [
+            State {
+                name: "Visible"
+                PropertyChanges { target: sbar; y: 0; opacity: 1 }
+            },
+            State {
+                name: "Hidden"
+                PropertyChanges { target: sbar; y: -height; opacity: 0 }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                from: "Hidden"; to: "Visible"
+                ParallelAnimation {
+                    NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad }
+                    NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            },
+            Transition {
+                from: "Visible"; to: "Hidden"
+                ParallelAnimation {
+                    NumberAnimation { target: sbar; properties: "y"; duration: 200; easing.type: Easing.Linear }
+                    NumberAnimation { target: sbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            }
+        ]
+    }
+
+    Item {
+        id: sip
+
+        anchors { bottom: parent.bottom; left: parent.left; right: parent.right }
+
+        Behavior on height { PropertyAnimation { duration: 200 } }
+
+        states: [
+            State {
+                name: "Visible"; when: inputContext.visible && root.platformSoftwareInputPanelEnabled
+                PropertyChanges { target: sip; height: inputContext.height }
+            },
+
+            State {
+                name: "Hidden"; when: root.showToolBar
+                PropertyChanges { target: sip; height: tbar.height }
+            },
+
+            State {
+                name: "HiddenInFullScreen"; when: !root.showToolBar
+                PropertyChanges { target: sip; height: 0 }
+            }
+        ]
+    }
+
+    ToolBar {
+        id: tbar
+
+        width: parent.width
+        state: root.showToolBar ? "Visible" : "Hidden"
+        platformInverted: root.platformInverted
+
+        states: [
+            State {
+                name: "Visible"
+                PropertyChanges { target: tbar; y: parent.height - height; opacity: 1 }
+            },
+            State {
+                name: "Hidden"
+                PropertyChanges { target: tbar; y: parent.height; opacity: 0 }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                from: "Hidden"; to: "Visible"
+                ParallelAnimation {
+                    NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.OutQuad }
+                    NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            },
+            Transition {
+                from: "Visible"; to: "Hidden"
+                ParallelAnimation {
+                    NumberAnimation { target: tbar; properties: "y"; duration: 200; easing.type: Easing.Linear }
+                    NumberAnimation { target: tbar; properties: "opacity"; duration: 200; easing.type: Easing.Linear }
+                }
+            }
+        ]
+    }
+
+    // event preventer when page transition is active
+    MouseArea {
+        anchors.fill: parent
+        enabled: pageStack.busy
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Popup.qml b/mardrone/imports/com/nokia/android.1.1/Popup.qml
new file mode 100644 (file)
index 0000000..3071785
--- /dev/null
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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 "AppManager.js" as Utils
+
+Item {
+    id: root
+
+    property Item visualParent
+    property int status: DialogStatus.Closed
+    property int animationDuration: 500
+    property Item fader
+    property bool platformInverted: false
+
+    signal faderClicked
+
+    function open() {
+        if (status == DialogStatus.Open || status == DialogStatus.Opening)
+            return
+
+        var notify = false
+        if (!fader) {
+            fader = faderComponent.createObject(visualParent ? visualParent : Utils.rootObject())
+            notify = true
+        }
+       
+        fader.animationDuration = root.animationDuration
+        root.parent = fader
+        status = DialogStatus.Opening
+        fader.state = "Visible"
+
+        if (notify)
+            platformPopupManager.privateNotifyPopupOpen()
+    }
+
+    function close() {
+        if (status != DialogStatus.Closed) {
+            status = DialogStatus.Closing
+            if (fader)
+                fader.state = "Hidden"
+        }
+    }
+
+    onStatusChanged: {
+        if (status == DialogStatus.Closed && fader) {
+            // Temporarily setting root window as parent
+            // otherwise transition animation jams
+            root.parent = null
+            fader.destroy()
+            fader = null // Prevent reuse in open()
+            root.parent = parentCache.oldParent
+            platformPopupManager.privateNotifyPopupClose()
+        }
+    }
+
+    Component.onCompleted: {
+        parentCache.oldParent = parent
+    }
+
+    //if this is not given, application may crash in some cases
+    Component.onDestruction: {
+        if (parentCache.oldParent != null) {
+            parent = parentCache.oldParent
+        }
+    }
+
+    QtObject {
+        id: parentCache
+        property QtObject oldParent: null
+    }
+
+    //This eats mouse events when popup area is clicked
+    MouseArea {
+        anchors.fill: parent
+    }
+
+    Component {
+        id: faderComponent
+
+        Fader {
+            platformInverted: root.platformInverted
+            onClicked: root.faderClicked()
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ProgressBar.qml b/mardrone/imports/com/nokia/android.1.1/ProgressBar.qml
new file mode 100644 (file)
index 0000000..e8aefcc
--- /dev/null
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** 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 Qt.labs.components 1.1
+import "." 1.1
+
+Item {
+    id: root
+
+    // Common Public API
+    property alias minimumValue: model.minimumValue
+    property alias maximumValue: model.maximumValue
+    property alias value: model.value
+    property bool indeterminate: false
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    implicitWidth: Math.max(50, screen.width / 2) // TODO: use screen.displayWidth
+    implicitHeight: privateStyle.sliderThickness
+
+    BorderImage {
+        id: background
+
+        source: privateStyle.imagePath("qtg_fr_progressbar_track", root.platformInverted)
+        border { left: platformStyle.borderSizeMedium; top: 0; right: platformStyle.borderSizeMedium; bottom: 0 }
+        anchors.fill: parent
+    }
+
+    Loader {
+        id: progressBarContent
+
+        LayoutMirroring.enabled: false
+        LayoutMirroring.childrenInherit: true
+
+        states: [
+            State {
+                name: "indeterminate"
+                when: root.indeterminate
+                PropertyChanges { target: progressBarContent; sourceComponent: indeterminateContent; anchors.fill: parent }
+            },
+            State {
+                name: "determinate"
+                when: !root.indeterminate
+                PropertyChanges { target: progressBarContent; sourceComponent: determinateContent }
+                AnchorChanges {
+                    target: progressBarContent
+                    anchors.top: parent.top
+                    anchors.bottom: parent.bottom
+                    anchors.left: parent.left
+                }
+            }
+        ]
+    }
+
+
+    Component {
+        id: indeterminateContent
+
+        Item {
+            anchors.fill: parent
+
+            Item {
+                id: indeterminateImageMask
+
+                // Mask margins prevent indeterminateImage to appear outside the rounded
+                // frame corners, hardcoded 3 has been instructed by UX
+                anchors { fill: parent; leftMargin: 3; rightMargin: 3 }
+                clip: true
+
+                Image {
+                    id: indeterminateImage
+
+                    x: parent.x
+                    height: parent.height
+                    width: parent.width + height // height is the amount of horizontal movement
+                    fillMode: Image.TileHorizontally
+                    source: privateStyle.imagePath(root.platformInverted ? "qtg_graf_progressbar_wait_inverse"
+                                                                         : "qtg_graf_progressbar_wait")
+
+                    NumberAnimation on x {
+                        loops: Animation.Infinite
+                        running: true
+                        from: 0
+                        to: -indeterminateImage.height // see indeterminateImage.width
+                        easing.type: Easing.Linear
+                        duration: privateStyle.sliderThickness * 30
+                    }
+                }
+            }
+
+            BorderImage {
+                id: indeterminateOverlay
+
+                anchors.fill: parent
+                source: privateStyle.imagePath("qtg_fr_progressbar_overlay", root.platformInverted)
+                border {
+                    left: platformStyle.borderSizeMedium
+                    right: platformStyle.borderSizeMedium
+                    top: 0
+                    bottom: 0
+                }
+            }
+        }
+    }
+
+    Component {
+        id: determinateContent
+
+        Item {
+            id: progressMask
+
+            width: model.position
+            clip: true
+
+            BorderImage {
+                id: progress
+
+                source: privateStyle.imagePath("qtg_fr_progressbar_fill", root.platformInverted)
+                border {
+                    left: platformStyle.borderSizeMedium
+                    right: platformStyle.borderSizeMedium
+                    top: 0
+                    bottom: 0
+                }
+                height: parent.height
+                width: root.width
+            }
+        }
+    }
+
+    RangeModel {
+        id: model
+        minimumValue: 0.0
+        maximumValue: 1.0
+        positionAtMinimum: 0.0
+        positionAtMaximum: background.width
+    }
+}
+
diff --git a/mardrone/imports/com/nokia/android.1.1/QueryDialog.qml b/mardrone/imports/com/nokia/android.1.1/QueryDialog.qml
new file mode 100644 (file)
index 0000000..a46fa35
--- /dev/null
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** 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
+
+CommonDialog {
+    id: root
+    objectName: "root"
+
+    property string message
+    property string acceptButtonText
+    property string rejectButtonText
+    property alias icon: root.titleIcon // for backwards compatibility
+
+    onAcceptButtonTextChanged: internal.updateButtonTexts()
+    onRejectButtonTextChanged: internal.updateButtonTexts()
+
+    onStatusChanged: {
+        if (status == DialogStatus.Open)
+            scrollBar.flash()
+    }
+
+    onButtonClicked: {
+        if (acceptButtonText && index == 0)
+            accepted()
+        else
+            rejected()
+    }
+
+    content: Item {
+        height: {
+            if (root.height > 0)
+                return Math.min(Math.max(privateStyle.dialogMinSize, root.height) - privateTitleHeight - privateButtonsHeight, root.platformContentMaximumHeight)
+            else
+                return Math.min(label.paintedHeight, root.platformContentMaximumHeight)
+        }
+        width: parent.width
+
+        Item {
+            anchors {
+                top: parent.top; topMargin: platformStyle.paddingLarge
+                bottom: parent.bottom; bottomMargin: platformStyle.paddingLarge
+                left: parent.left; leftMargin: platformStyle.paddingLarge
+                right: parent.right
+            }
+
+            Flickable {
+                id: flickable
+                width: parent.width
+                height: parent.height
+                anchors { left: parent.left; top: parent.top }
+                contentHeight: label.paintedHeight
+                flickableDirection: Flickable.VerticalFlick
+                clip: true
+                interactive: contentHeight > height
+
+                Text {
+                    id: label
+                    anchors { right: parent.right; rightMargin: privateStyle.scrollBarThickness }
+                    width: flickable.width - privateStyle.scrollBarThickness
+                    font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
+                    color: root.platformInverted ? platformStyle.colorNormalLightInverted
+                                                 : platformStyle.colorNormalLight
+                    wrapMode: Text.WordWrap
+                    text: root.message
+                    horizontalAlignment: Text.AlignLeft
+                }
+            }
+
+            ScrollBar {
+                id: scrollBar
+                height: parent.height
+                anchors { top: flickable.top; right: flickable.right }
+                flickableItem: flickable
+                interactive: false
+                orientation: Qt.Vertical
+                platformInverted: root.platformInverted
+            }
+        }
+    }
+
+    QtObject {
+        id: internal
+
+        function updateButtonTexts() {
+            var newButtonTexts = []
+            if (acceptButtonText)
+                newButtonTexts.push(acceptButtonText)
+            if (rejectButtonText)
+                newButtonTexts.push(rejectButtonText)
+            root.buttonTexts = newButtonTexts
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/RadioButton.qml b/mardrone/imports/com/nokia/android.1.1/RadioButton.qml
new file mode 100644 (file)
index 0000000..729c9ad
--- /dev/null
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** 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 Qt.labs.components 1.1
+import "." 1.1
+
+Item {
+    id: root
+
+    // Common Public API
+    property alias checked: checkable.checked
+    property bool pressed: stateGroup.state == "Pressed" || stateGroup.state == "KeyPressed"
+    signal clicked
+    property alias text: label.text
+
+    // Symbian specific API
+    property alias platformExclusiveGroup: checkable.exclusiveGroup
+    property bool platformInverted: false
+
+    QtObject {
+        id: internal
+        objectName: "internal"
+        property color disabledColor: root.platformInverted ? platformStyle.colorDisabledLightInverted
+                                                            : platformStyle.colorDisabledLight
+        property color pressedColor: root.platformInverted ? platformStyle.colorPressedInverted
+                                                           : platformStyle.colorPressed
+        property color normalColor: root.platformInverted ? platformStyle.colorNormalLightInverted
+                                                          : platformStyle.colorNormalLight
+
+        function toggle() {
+            clickedEffect.restart()
+            checkable.toggle()
+            root.clicked()
+        }
+
+        function icon_postfix() {
+            if (pressed)
+                return "pressed"
+            else if (root.checked) {
+                if (!root.enabled)
+                    return "disabled_selected"
+                else
+                    return "normal_selected"
+            } else {
+                if (!root.enabled)
+                    return "disabled_unselected"
+                else
+                    return "normal_unselected"
+            }
+        }
+    }
+
+    StateGroup {
+        id: stateGroup
+
+        states: [
+            State { name: "Pressed" },
+            State { name: "KeyPressed" },
+            State { name: "Canceled" }
+        ]
+
+        transitions: [
+            Transition {
+                to: "Pressed"
+                ScriptAction { script:  privateStyle.play(Android.BasicItem) }
+            },
+            Transition {
+                from: "Pressed"
+                to: ""
+                ScriptAction { script: privateStyle.play(Android.CheckBox) }
+                ScriptAction { script: internal.toggle() }
+            },
+            Transition {
+                from: "KeyPressed"
+                to: ""
+                ScriptAction { script: internal.toggle() }
+            }
+        ]
+    }
+
+    implicitWidth: privateStyle.textWidth(label.text, label.font) + platformStyle.paddingMedium + privateStyle.buttonSize
+    implicitHeight: privateStyle.buttonSize
+
+    Image {
+        id: image
+        source: privateStyle.imagePath("qtg_graf_radiobutton_" + internal.icon_postfix(),
+                                       root.platformInverted)
+        anchors.left: parent.left
+        anchors.verticalCenter: parent.verticalCenter
+        sourceSize.width: privateStyle.buttonSize
+        sourceSize.height: privateStyle.buttonSize
+
+        MouseArea {
+            id: mouseArea
+            anchors.fill: parent
+
+            onPressed: stateGroup.state = "Pressed"
+            onReleased: stateGroup.state = ""
+            onClicked: stateGroup.state = ""
+            onExited: stateGroup.state = "Canceled"
+            onCanceled: {
+                // Mark as canceled
+                stateGroup.state = "Canceled"
+                // Reset state. Can't expect a release since mouse was ungrabbed
+                stateGroup.state = ""
+            }
+        }
+    }
+
+    Text {
+        id: label
+        elide: Text.ElideRight
+        anchors.left: image.right
+        anchors.leftMargin: platformStyle.paddingMedium
+        anchors.verticalCenter: parent.verticalCenter
+        anchors.right: parent.right
+        horizontalAlignment: Text.AlignLeft
+
+        font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
+        color: root.enabled ? (root.pressed ? internal.pressedColor : internal.normalColor)
+                            : internal.disabledColor
+    }
+
+    ParallelAnimation {
+        id: clickedEffect
+        SequentialAnimation {
+            PropertyAnimation {
+                target: image
+                property: "scale"
+                from: 1.0
+                to: 0.8
+                easing.type: Easing.Linear
+                duration: 50
+            }
+            PropertyAnimation {
+                target: image
+                property: "scale"
+                from: 0.8
+                to: 1.0
+                easing.type: Easing.OutQuad
+                duration: 170
+            }
+        }
+    }
+
+    Keys.onPressed: {
+        if (!event.isAutoRepeat && (event.key == Qt.Key_Select
+                                    || event.key == Qt.Key_Return
+                                    || event.key == Qt.Key_Enter)) {
+            stateGroup.state = "KeyPressed"
+            event.accepted = true
+        }
+    }
+
+    Keys.onReleased: {
+        if (!event.isAutoRepeat && (event.key == Qt.Key_Select
+                                    || event.key == Qt.Key_Return
+                                    || event.key == Qt.Key_Enter)) {
+            stateGroup.state = ""
+            event.accepted = true
+        }
+    }
+
+    Checkable {
+        id: checkable
+        value: root.text
+        enabled: true
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/RectUtils.js b/mardrone/imports/com/nokia/android.1.1/RectUtils.js
new file mode 100644 (file)
index 0000000..00e0e5e
--- /dev/null
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+.pragma library
+
+function rectContainsPoint(rect, x, y) {
+    return rect.x < x
+           && x < rect.x + rect.width
+           && rect.y < y
+           && y < rect.y + rect.height;
+}
+
+function rectIntersectsRect(rect1, rect2) {
+    return !(rect2.x > rect1.x + rect1.width  || rect2.x + rect2.width < rect1.x ||
+             rect2.y > rect1.y + rect1.height || rect2.y + rect2.height < rect1.y);
+}
+
+// returns true if rect1 contains rect2
+function rectContainsRect(rect1, rect2) {
+    return (rect1.x < rect2.x && rect2.x + rect2.width < rect1.x + rect1.width &&
+            rect1.y < rect2.y && rect2.y + rect2.height < rect1.y + rect1.height);
+}
+
+function manhattan(point1, point2) {
+    return Math.abs(point1.x - point2.x) + Math.abs(point1.y - point2.y)
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ScrollBar.qml b/mardrone/imports/com/nokia/android.1.1/ScrollBar.qml
new file mode 100644 (file)
index 0000000..e356c55
--- /dev/null
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** 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 "SectionScroller.js" as Sections
+import "." 1.1
+
+Item {
+    id: root
+
+    property Flickable flickableItem: null
+    property int orientation: Qt.Vertical
+    property bool interactive: true
+    property int policy: Android.ScrollBarWhenScrolling
+    property bool privateSectionScroller: false
+    property bool platformInverted: false
+
+    //implicit values for qml designer when no Flickable is present
+    implicitHeight: privateStyle.scrollBarThickness * (orientation == Qt.Vertical ? 3 : 1)
+    implicitWidth: privateStyle.scrollBarThickness * (orientation == Qt.Horizontal ? 3 : 1)
+    height: {
+        if (flickableItem && orientation == Qt.Vertical)
+            return Math.floor(Math.round(flickableItem.height) - anchors.topMargin - anchors.bottomMargin)
+        return undefined
+    }
+    width: {
+        if (flickableItem && orientation == Qt.Horizontal)
+            return Math.floor(Math.round(flickableItem.width) - anchors.leftMargin - anchors.rightMargin)
+        return undefined
+    }
+    opacity: internal.rootOpacity
+
+    onFlickableItemChanged: internal.initSectionScroller();
+    onPrivateSectionScrollerChanged: internal.initSectionScroller();
+
+    //For showing explicitly a ScrollBar if policy is Android.ScrollBarWhenScrolling
+    function flash(type) {
+        if (policy == Android.ScrollBarWhenScrolling && internal.scrollBarNeeded) {
+            flashEffect.type = (type == undefined) ? Android.FadeOut : type
+            flashEffect.restart()
+        }
+    }
+
+    Connections {
+        target: screen
+        onCurrentOrientationChanged: flash()
+    }
+
+    Connections {
+        target: root.privateSectionScroller ? flickableItem : null
+        onModelChanged: internal.initSectionScroller()
+    }
+
+    Loader {
+        id: listItemInteraction
+        sourceComponent: flickableItem && flickableItem.hasOwnProperty("currentIndex") && flickableItem.activeFocus ? listItemInteractionComponent : undefined
+    }
+
+    Component {
+        id: listItemInteractionComponent
+        Item {
+            Connections {
+                target: symbian
+                onListInteractionModeChanged: flash()
+                onPrivateListItemKeyNavigation: flash()
+            }
+        }
+    }
+
+    QtObject {
+        id: internal
+        property int hideTimeout: 500
+        property int pageStepY: flickableItem ? Math.floor(flickableItem.visibleArea.heightRatio * flickableItem.contentHeight) : NaN
+        property int pageStepX: flickableItem ? Math.floor(flickableItem.visibleArea.widthRatio * flickableItem.contentWidth) : NaN
+        property int handleY: flickableItem ? Math.floor(handle.y / flickableItem.height * flickableItem.contentHeight) : NaN
+        property int handleX: flickableItem ? Math.floor(handle.x / flickableItem.width * flickableItem.contentWidth) : NaN
+        property int maximumY: flickableItem ? Math.floor(Math.min(flickableItem.contentHeight - root.height, flickableItem.contentY)) : NaN
+        property int maximumX: flickableItem ? Math.floor(Math.min(flickableItem.contentWidth - root.width, flickableItem.contentX)) : NaN
+        property bool scrollBarNeeded: root.visible && hasScrollableContent()
+        //Sets currentSection to empty string when flickableItem.currentSection is null
+        property string currentSection: flickableItem ? flickableItem.currentSection || "" : ""
+        //To be able to pressed on trackMouseArea, opacity needs to be greater than 0
+        property real rootOpacity: root.privateSectionScroller ? 0.01 : 0
+
+        // The following properties are used for calculating handle position and size:
+        // Minimum allowed handle length
+        property real minHandleLength: 3 * Math.floor(privateStyle.scrollBarThickness)
+        // Scroll bar "container" length
+        property real totalLength: !flickableItem ? NaN : (orientation == Qt.Vertical ? root.height : root.width)
+        property real relativeHandlePosition: !flickableItem ? NaN : (orientation == Qt.Vertical
+                                                                      ? flickableItem.visibleArea.yPosition
+                                                                      : flickableItem.visibleArea.xPosition)
+        property real lengthRatio: !flickableItem ? NaN : (orientation == Qt.Vertical
+                                                           ? flickableItem.visibleArea.heightRatio
+                                                           : flickableItem.visibleArea.widthRatio)
+        // Scroll bar handle length under normal circumstances
+        property real staticHandleLength: Math.max(minHandleLength, lengthRatio * totalLength)
+        // Adjustment factor needed to calculate correct position, which is needed because
+        // relativeHandlePosition and lengthRatio are assuming default handle length. Dividend is
+        // the maximum handle position taking into account restricted minimum handle length, whereas
+        // divisor is the maximum handle position if default handle size would be used.
+        property real handlePositionAdjustment: (totalLength - staticHandleLength) / (totalLength * (1 - lengthRatio))
+        property real handlePosition
+        handlePosition: {
+            // handle's position, which is adjusted to take into account non-default handle
+            // length and restricted never to exceed flickable boundaries
+            var pos = totalLength * relativeHandlePosition * handlePositionAdjustment
+            return Math.min(Math.max(0, pos), totalLength - minHandleLength)
+        }
+        property real dynamicHandleLength
+        dynamicHandleLength: {
+            // dynamic handle length, which may differ from static due to bounds behavior, i.e.
+            // handle gets shorter when content exceeds flickable boundaries
+            var length = 0
+            if (relativeHandlePosition >= 1 - lengthRatio) // overflow
+                length = 1 - relativeHandlePosition
+            else if (relativeHandlePosition < 0) // underflow
+                length = lengthRatio + relativeHandlePosition
+            else
+                length = lengthRatio
+            return Math.max(minHandleLength, length * totalLength)
+        }
+
+        // Show flash effect in case if no flicking (flickableItem.moving) has occured
+        // but there is a need to indicate scrollable content.
+        onScrollBarNeededChanged: {
+            if (stateGroup.state == "") {
+                if (scrollBarNeeded)
+                    flash()
+                else
+                    idleEffect.restart()
+            }
+        }
+
+        /**
+         * Checks whether ScrollBar is needed or not
+         * based on Flickable visibleArea height and width ratios
+         */
+        function hasScrollableContent() {
+            if (!flickableItem)
+                return false
+            var ratio = orientation == Qt.Vertical ? flickableItem.visibleArea.heightRatio : flickableItem.visibleArea.widthRatio
+            return ratio < 1.0 && ratio > 0
+        }
+        /**
+         * Does Page by Page movement of flickableItem
+         * when ScrollBar Track is being clicked/pressed
+         *
+         * @see #moveToLongTapPosition
+         */
+        function doPageStep() {
+            if (orientation == Qt.Vertical) {
+                if (trackMouseArea.mouseY > (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY += pageStepY
+                    flickableItem.contentY = maximumY
+                }
+                else if (trackMouseArea.mouseY < (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY -= pageStepY
+                    flickableItem.contentY = Math.max(0, flickableItem.contentY)
+                }
+            } else {
+                if (trackMouseArea.mouseX > (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX += pageStepX
+                    flickableItem.contentX = maximumX
+                }
+                else if (trackMouseArea.mouseX < (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX -= pageStepX
+                    flickableItem.contentX = Math.max(0, flickableItem.contentX)
+                }
+            }
+        }
+        /**
+         * Does movement of flickableItem
+         * when ScrollBar Handle is being dragged
+         */
+        function moveToHandlePosition() {
+            if (orientation == Qt.Vertical)
+                flickableItem.contentY = handleY
+            else
+                flickableItem.contentX = handleX
+        }
+        /**
+         * Moves flickableItem's content according to given mouseArea movement
+         * when mouseArea is pressed long
+         * Tries to position the handle and content in center of mouse position enough
+         *
+         * @see #doPageStep
+         */
+        function moveToLongTapPosition(mouseArea) {
+            if (orientation == Qt.Vertical) {
+                if (Math.abs(mouseArea.mouseY - (handle.height / 2 + handle.y)) < privateStyle.scrollBarThickness)
+                    return //if change is not remarkable enough, do nothing otherwise it would cause annoying flickering effect
+                if (mouseArea.mouseY > (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY += Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentY = maximumY
+                }
+                else if (mouseArea.mouseY < (handle.height / 2 + handle.y)) {
+                    flickableItem.contentY -= Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentY = Math.floor(Math.max(0, flickableItem.contentY))
+                }
+            } else {
+                if (Math.abs(mouseArea.mouseX - (handle.width / 2 + handle.x)) < privateStyle.scrollBarThickness)
+                    return //if change is not remarkable enough, do nothing otherwise it would cause annoying flickering effect
+                if (mouseArea.mouseX > (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX += Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentX = maximumX
+                }
+                else if (mouseArea.mouseX < (handle.width / 2 + handle.x)) {
+                    flickableItem.contentX -= Math.floor(privateStyle.scrollBarThickness)
+                    flickableItem.contentX = Math.floor(Math.max(0, flickableItem.contentX))
+                }
+            }
+        }
+
+        function adjustContentPosition(y) {
+            if (y < 0 || y > trackMouseArea.height)
+                return;
+
+            var sect = Sections.closestSection(y / trackMouseArea.height);
+            var idx = Sections.indexOf(sect);
+            currentSection = sect;
+            flickableItem.positionViewAtIndex(idx, ListView.Beginning);
+        }
+
+        function initSectionScroller() {
+            if (root.privateSectionScroller && flickableItem && flickableItem.model)
+                Sections.initSectionData(flickableItem);
+        }
+    }
+
+    BorderImage {
+        id: track
+        objectName: "track"
+        source: privateStyle.imagePath((orientation == Qt.Vertical
+                                        ? "qtg_fr_scrollbar_v_track_normal"
+                                        : "qtg_fr_scrollbar_h_track_normal"),
+                                       root.platformInverted)
+        visible: interactive
+        anchors.fill: parent
+        border.right: orientation == Qt.Horizontal ? 7 : 0
+        border.left: orientation == Qt.Horizontal ? 7 : 0
+        border.top: orientation == Qt.Vertical ? 7 : 0
+        border.bottom: orientation == Qt.Vertical ? 7 : 0
+        onVisibleChanged: { idleEffect.complete(); flashEffect.complete() }
+    }
+
+    Loader {
+        id: sectionScrollBackground
+        anchors.right: trackMouseArea.right
+        width: flickableItem ? flickableItem.width : 0
+        height: platformStyle.fontSizeMedium * 5
+        sourceComponent: root.privateSectionScroller ? sectionScrollComponent : null
+    }
+
+    Component {
+        id: sectionScrollComponent
+        BorderImage {
+            id: indexFeedbackBackground
+            objectName: "indexFeedbackBackground"
+            source: privateStyle.imagePath("qtg_fr_popup_transparent", root.platformInverted)
+            border { left: platformStyle.borderSizeMedium; top: platformStyle.borderSizeMedium; right: platformStyle.borderSizeMedium; bottom: platformStyle.borderSizeMedium }
+            visible: trackMouseArea.pressed
+            anchors.fill: parent
+            Text {
+                id: indexFeedbackText
+                objectName: "indexFeedbackText"
+                color: root.platformInverted ? platformStyle.colorNormalDarkInverted // intentionally dark inverted
+                                             : platformStyle.colorNormalLight
+                anchors {
+                    left: parent.left;
+                    leftMargin: platformStyle.paddingLarge;
+                    right: parent.right;
+                    rightMargin: platformStyle.paddingLarge;
+                    verticalCenter: parent.verticalCenter
+                }
+                font {
+                    family: platformStyle.fontFamilyRegular;
+                    pixelSize: indexFeedbackText.text.length == 1 ? platformStyle.fontSizeMedium * 4 : platformStyle.fontSizeMedium * 2;
+                    capitalization: indexFeedbackText.text.length == 1 ? Font.AllUppercase : Font.MixedCase
+                }
+                text: internal.currentSection
+                horizontalAlignment: Text.AlignLeft
+                elide: Text.ElideRight
+            }
+            states: [
+                State {
+                    when: (handle.y + (handle.height / 2)) - (sectionScrollBackground.height / 2) < 0
+                    AnchorChanges {
+                        target: sectionScrollBackground
+                        anchors { verticalCenter: undefined; top: track.top; bottom: undefined }
+                    }
+                },
+                State {
+                    when: (handle.y + (handle.height / 2)) + (sectionScrollBackground.height / 2) >= track.height
+                    AnchorChanges {
+                        target: sectionScrollBackground
+                        anchors { verticalCenter: undefined; top: undefined; bottom: track.bottom }
+                    }
+                },
+                State {
+                    when: (handle.y + (handle.height / 2)) - (sectionScrollBackground.height / 2) >= 0
+                    AnchorChanges {
+                        target: sectionScrollBackground
+                        anchors { verticalCenter: handle.verticalCenter; top: undefined; bottom: undefined }
+                    }
+                }
+            ]
+        }
+    }
+
+    // MouseArea for the move content "page by page" by tapping and scroll to press-and-hold position
+    MouseArea {
+        id: trackMouseArea
+        objectName: "trackMouseArea"
+        property bool longPressed: false
+        enabled: root.privateSectionScroller || interactive
+        anchors {
+            top: root.privateSectionScroller ? parent.top : undefined;
+            bottom: root.privateSectionScroller ? parent.bottom : undefined;
+            right: root.privateSectionScroller ? parent.right : undefined;
+            fill: root.privateSectionScroller ? undefined : (flickableItem ? track : undefined)
+        }
+        width: root.privateSectionScroller ? privateStyle.scrollBarThickness * 3 : undefined
+        drag {
+            target: root.privateSectionScroller ? sectionScrollBackground : undefined
+            // axis is set XandY to prevent flickable from stealing the mouse event
+            // SectionScroller is anchored to the right side of the mouse area so the user
+            // won't be able to drag it along the X axis
+            axis: root.privateSectionScroller ? Drag.XandYAxis : 0
+            minimumY: root.privateSectionScroller ? (flickableItem ? flickableItem.y : 0) : 0
+            maximumY: root.privateSectionScroller ? (flickableItem ? (trackMouseArea.height - sectionScrollBackground.height) : 0) : 0
+        }
+
+        onPressAndHold: {
+            if (!root.privateSectionScroller)
+                longPressed = true
+        }
+
+        onReleased: longPressed = false
+
+        onPositionChanged: {
+            if (root.privateSectionScroller)
+                internal.adjustContentPosition(trackMouseArea.mouseY);
+        }
+
+        onPressedChanged: {
+            if (root.privateSectionScroller && trackMouseArea.pressed)
+                internal.adjustContentPosition(trackMouseArea.mouseY);
+        }
+    }
+    Timer {
+        id: pressAndHoldTimer
+        running: trackMouseArea.longPressed
+        interval: 50
+        repeat: true
+        onTriggered: { internal.moveToLongTapPosition(trackMouseArea); privateStyle.play(Android.SensitiveSlider) }
+    }
+
+    BorderImage {
+        id: handle
+        objectName: "handle"
+        source: privateStyle.imagePath(handleFileName(), root.platformInverted)
+        x: orientation == Qt.Horizontal ? internal.handlePosition : NaN
+        y: orientation == Qt.Vertical ? internal.handlePosition : NaN
+        height: orientation == Qt.Vertical ? internal.dynamicHandleLength : root.height
+        width: orientation == Qt.Horizontal ? internal.dynamicHandleLength : root.width
+        border.right: orientation == Qt.Horizontal ? 7 : 0
+        border.left: orientation == Qt.Horizontal ? 7 : 0
+        border.top: orientation == Qt.Vertical ? 7 : 0
+        border.bottom: orientation == Qt.Vertical ? 7 : 0
+
+        function handleFileName() {
+            var fileName = (orientation == Qt.Vertical ? "qtg_fr_scrollbar_v_handle_" :
+                            "qtg_fr_scrollbar_h_handle_")
+            if (!interactive)
+                fileName += "indicative"
+            else if (handleMouseArea.pressed)
+                fileName += "pressed"
+            else
+                fileName += "normal"
+            return fileName
+        }
+    }
+
+    MouseArea {
+        id: handleMouseArea
+        objectName: "handleMouseArea"
+        property real maxDragY: flickableItem ? flickableItem.height - handle.height - root.anchors.topMargin - root.anchors.bottomMargin : NaN
+        property real maxDragX: flickableItem ? flickableItem.width - handle.width - root.anchors.leftMargin - root.anchors.rightMargin : NaN
+        enabled: interactive && !root.privateSectionScroller
+        width: orientation == Qt.Vertical ? 3 * privateStyle.scrollBarThickness : handle.width
+        height: orientation == Qt.Horizontal ? 3 * privateStyle.scrollBarThickness : handle.height
+        anchors {
+            verticalCenter: flickableItem ? handle.verticalCenter : undefined
+            horizontalCenter: flickableItem ? handle.horizontalCenter : undefined
+        }
+        drag {
+            target: handle
+            axis: orientation == Qt.Vertical ? Drag.YAxis : Drag.XAxis
+            minimumY: 0
+            maximumY: maxDragY
+            minimumX: 0
+            maximumX: maxDragX
+        }
+        onPositionChanged: internal.moveToHandlePosition()
+    }
+
+    PropertyAnimation {
+        id: indicateEffect
+
+        function play() {
+            flashEffect.stop()
+            idleEffect.stop()
+            restart()
+        }
+
+        target: root
+        property: "opacity"
+        to: 1
+        duration: 0
+    }
+    SequentialAnimation {
+        id: idleEffect
+
+        function play() {
+            indicateEffect.stop()
+            if (internal.scrollBarNeeded && root.policy == Android.ScrollBarWhenScrolling)
+                restart()
+        }
+
+        PauseAnimation { duration: root.interactive ? 1500 : 0 }
+        PropertyAnimation {
+            target: root
+            property: "opacity"
+            to: internal.rootOpacity
+            duration: internal.hideTimeout
+        }
+    }
+    SequentialAnimation {
+        id: flashEffect
+        property int type: Android.FadeOut
+
+        PropertyAnimation {
+            target: root
+            property: "opacity"
+            to: 1
+            duration: (flashEffect.type == Android.FadeInFadeOut) ? internal.hideTimeout : 0
+        }
+        PropertyAnimation {
+            target: root
+            property: "opacity"
+            to: internal.rootOpacity
+            duration: internal.hideTimeout
+        }
+    }
+    StateGroup {
+        id: stateGroup
+        states: [
+            State {
+                name: "Move"
+                when: handleMouseArea.pressed
+            },
+            State {
+                name: "Stepping"
+                when: trackMouseArea.longPressed
+            },
+            State {
+                name: "Step"
+                when: trackMouseArea.pressed && !trackMouseArea.longPressed && !root.privateSectionScroller
+            },
+            State {
+                name: "Indicate"
+                when: (internal.scrollBarNeeded && flickableItem.moving) ||
+                      (trackMouseArea.pressed && root.privateSectionScroller)
+            },
+            State {
+                name: ""
+            }
+        ]
+        transitions: [
+            Transition {
+                to: "Move"
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                to: "Step"
+                ScriptAction { script: internal.doPageStep() }
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                from: "Step"
+                to: "Stepping"
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                from: "Move"
+                to: "Indicate"
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                to: "Indicate"
+                ScriptAction { script: indicateEffect.play() }
+            },
+            Transition {
+                from: "Move"
+                to: ""
+                ScriptAction { script: privateStyle.play(Android.BasicSlider) }
+                ScriptAction { script: idleEffect.play() }
+            },
+            Transition {
+                to: ""
+                ScriptAction { script: idleEffect.play() }
+            }
+        ]
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ScrollDecorator.qml b/mardrone/imports/com/nokia/android.1.1/ScrollDecorator.qml
new file mode 100644 (file)
index 0000000..4ba220f
--- /dev/null
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    //Common Public API
+    property Flickable flickableItem: null
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    Item {
+        id: decorators
+        property bool completed: false
+
+        Loader {
+            sourceComponent: decorators.completed ? vertical : undefined
+        }
+        Loader {
+            sourceComponent: decorators.completed ? horizontal : undefined
+        }
+        Component {
+            id: horizontal
+            ScrollBar {
+                parent: flickableItem
+                flickableItem: root.flickableItem
+                orientation: Qt.Horizontal
+                interactive: false
+                platformInverted: root.platformInverted
+                anchors {
+                    left: flickableItem.left
+                    bottom: flickableItem.bottom
+                    rightMargin: height
+                }
+            }
+        }
+        Component {
+            id: vertical
+            ScrollBar {
+                parent: flickableItem
+                flickableItem: root.flickableItem
+                orientation: Qt.Vertical
+                interactive: false
+                platformInverted: root.platformInverted
+                anchors { top: flickableItem.top; right: flickableItem.right }
+            }
+        }
+        Component.onDestruction: {
+            decorators.completed = false
+        }
+    }
+    onFlickableItemChanged: {
+        decorators.completed = flickableItem ? true : false
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/SectionScroller.js b/mardrone/imports/com/nokia/android.1.1/SectionScroller.js
new file mode 100644 (file)
index 0000000..7b987fe
--- /dev/null
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+var _sectionData = [];
+var _sections = [];
+
+function initSectionData(list) {
+    if (!list || !list.model) return;
+    _sectionData = [];
+    _sections = [];
+    var current = "";
+    var prop = list.section.property;
+
+    for (var i = 0, count = list.model.count; i < count; i++) {
+        var item = list.model.get(i);
+        if (item[prop] !== current) {
+            current = item[prop];
+            _sections.push(current);
+            _sectionData.push({ index: i, header: current });
+        }
+    }
+}
+
+function closestSection(pos) {
+    var tmp = (_sections.length) * pos;
+    var val = Math.ceil(tmp) // TODO: better algorithm
+    val = val < 2 ? 1 : val;
+    return _sections[val-1];
+}
+
+function indexOf(sectionName) {
+    var val = _sectionData[_sections.indexOf(sectionName)].index;
+    return val === 0 || val > 0 ? val : -1;
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/SectionScroller.qml b/mardrone/imports/com/nokia/android.1.1/SectionScroller.qml
new file mode 100644 (file)
index 0000000..0b165d7
--- /dev/null
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** 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 "SectionScroller.js" as Sections
+import "." 1.1
+
+Item {
+    id: root
+
+    property ListView listView
+    property bool platformInverted: false
+
+    onListViewChanged: {
+        if (listView && listView.model)
+            internal.initDirtyObserver();
+    }
+
+    Connections {
+        target: listView
+        onModelChanged: {
+            if (listView && listView.model)
+                internal.initDirtyObserver();
+        }
+    }
+
+    ScrollBar {
+        id: scrollBar
+        parent: listView
+        flickableItem: listView
+        privateSectionScroller: true
+        platformInverted: root.platformInverted
+        anchors {
+            top: parent ? parent.top : undefined
+            right: parent ? parent.right : undefined
+        }
+    }
+
+    QtObject {
+        id: internal
+
+        function initDirtyObserver() {
+            Sections.initSectionData(listView);
+            function dirtyObserver() {
+                if (!internal.modelDirty) {
+                    internal.modelDirty = true;
+                    dirtyTimer.running = true;
+                }
+            }
+
+            if (listView.model.countChanged)
+                listView.model.countChanged.connect(dirtyObserver);
+
+            if (listView.model.itemsChanged)
+                listView.model.itemsChanged.connect(dirtyObserver);
+
+            if (listView.model.itemsInserted)
+                listView.model.itemsInserted.connect(dirtyObserver);
+
+            if (listView.model.itemsMoved)
+                listView.model.itemsMoved.connect(dirtyObserver);
+
+            if (listView.model.itemsRemoved)
+                listView.model.itemsRemoved.connect(dirtyObserver);
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/SelectionDialog.qml b/mardrone/imports/com/nokia/android.1.1/SelectionDialog.qml
new file mode 100644 (file)
index 0000000..6761534
--- /dev/null
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** 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
+
+CommonDialog {
+    id: root
+
+    // Common API
+    property alias model: listView.model
+    property int selectedIndex: -1
+    property Component delegate: defaultDelegate
+
+    privateCloseIcon: true
+
+    Component {
+        id: defaultDelegate
+
+        MenuItem {
+            platformInverted: root.platformInverted
+            text: modelData
+            onClicked: {
+                selectedIndex = index
+                root.accept()
+            }
+
+            Keys.onPressed: {
+                if (event.key == Qt.Key_Up || event.key == Qt.Key_Down)
+                    scrollBar.flash()
+            }
+        }
+    }
+
+    content: Item {
+        id: contentItem
+
+        function preferredHeight() {
+            // Need to create artifical binding to listView.delegate because of QTBUG-19037
+            var dummy = listView.delegate
+            var multiplier = screen.height > screen.width ? 0.45 : 0.6
+            var maxHeight = Math.floor(multiplier * screen.height / privateStyle.menuItemHeight)
+                * privateStyle.menuItemHeight
+            return Math.min(maxHeight, listView.count * privateStyle.menuItemHeight)
+        }
+
+        height: preferredHeight()
+        width: root.platformContentMaximumWidth
+
+        Item {
+            // Clipping item with bottom margin added to align content with rounded background graphics
+            id: clipItem
+            anchors.fill: parent
+            anchors.bottomMargin: platformStyle.paddingSmall
+            clip: true
+            ListView {
+                id: listView
+
+                currentIndex : -1
+                width: contentItem.width
+                height: contentItem.height
+                delegate: root.delegate
+                clip: true
+
+                Keys.onPressed: {
+                    if (event.key == Qt.Key_Up || event.key == Qt.Key_Down
+                        || event.key == Qt.Key_Left || event.key == Qt.Key_Right
+                        || event.key == Qt.Key_Select || event.key == Qt.Key_Enter
+                        || event.key == Qt.Key_Return) {
+                        android.listInteractionMode = Android.KeyNavigation
+                        listView.currentIndex = 0
+                        event.accepted = true
+                    }
+                }
+            }
+        }
+        ScrollBar {
+            id: scrollBar
+            flickableItem: listView
+            interactive: false
+            visible: listView.contentHeight > contentItem.height
+            platformInverted: root.platformInverted
+            anchors { top: clipItem.top; right: clipItem.right }
+        }
+    }
+
+    onClickedOutside: {
+        privateStyle.play(Android.PopupClose)
+        reject()
+    }
+
+    onStatusChanged: {
+        if (status == DialogStatus.Opening) {
+            if (listView.currentItem != null) {
+                listView.currentItem.focus = false
+            }
+            listView.currentIndex = -1
+            listView.positionViewAtIndex(0, ListView.Beginning)
+        }
+        else if (status == DialogStatus.Open) {
+            listView.focus = true
+            if (listView.contentHeight > contentItem.height)
+                scrollBar.flash(Android.FadeInFadeOut)
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/SelectionListItem.qml b/mardrone/imports/com/nokia/android.1.1/SelectionListItem.qml
new file mode 100644 (file)
index 0000000..d249d1d
--- /dev/null
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** 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
+
+ListItem {
+    id: root
+    property string title: ""
+    property string subTitle: ""
+
+    implicitHeight: background.height + 2 * platformStyle.paddingLarge
+
+    onModeChanged: {
+        if (root.mode == "pressed") {
+            pressed.source = privateStyle.imagePath("qtg_fr_choice_list_pressed", root.platformInverted)
+            pressed.opacity = 1
+        } else {
+            releasedEffect.restart()
+        }
+    }
+
+    BorderImage {
+        id: background
+        height: privateStyle.menuItemHeight - platformStyle.paddingSmall // from layout spec.
+        anchors {
+            left: parent.left
+            leftMargin: platformStyle.paddingLarge
+            right: parent.right
+            rightMargin: privateStyle.scrollBarThickness
+            verticalCenter: parent.verticalCenter
+        }
+        border {
+            left: platformStyle.borderSizeMedium
+            top: platformStyle.borderSizeMedium
+            right: platformStyle.borderSizeMedium
+            bottom: platformStyle.borderSizeMedium
+        }
+        source: privateStyle.imagePath("qtg_fr_choice_list_" + internal.getBackground(),
+                                       root.platformInverted)
+
+        BorderImage {
+            id: pressed
+            border {
+                left: platformStyle.borderSizeMedium
+                top: platformStyle.borderSizeMedium
+                right: platformStyle.borderSizeMedium
+                bottom: platformStyle.borderSizeMedium
+            }
+            opacity: 0
+            anchors.fill: parent
+        }
+
+        Column {
+            anchors {
+                verticalCenter: background.verticalCenter
+                right: indicator.left
+                rightMargin: platformStyle.paddingMedium
+                left: background.left
+                leftMargin: platformStyle.paddingLarge
+            }
+
+            Loader {
+                anchors.left: parent.left
+                sourceComponent: title != "" ? titleText : undefined
+                width: parent.width // elide requires explicit width
+            }
+
+            Loader {
+                anchors.left: parent.left
+                sourceComponent: subTitle != "" ? subTitleText : undefined
+                width: parent.width // elide requires explicit width
+            }
+        }
+        Image {
+            id: indicator
+            source: root.mode == "disabled" ? privateStyle.imagePath("qtg_graf_choice_list_indicator_disabled",
+                                                                     root.platformInverted)
+                                            : privateStyle.imagePath("qtg_graf_choice_list_indicator",
+                                                                     root.platformInverted)
+            sourceSize.width: platformStyle.graphicSizeSmall
+            sourceSize.height: platformStyle.graphicSizeSmall
+            anchors {
+                right: background.right
+                rightMargin: platformStyle.paddingSmall
+                verticalCenter: parent.verticalCenter
+            }
+        }
+    }
+
+    Component {
+        id: titleText
+        ListItemText {
+            mode: root.mode
+            role: "SelectionTitle"
+            text: root.title
+            platformInverted: root.platformInverted
+        }
+    }
+   Component {
+        id: subTitleText
+        ListItemText {
+            mode: root.mode
+            role: "SelectionSubTitle"
+            text: root.subTitle
+            platformInverted: root.platformInverted
+        }
+    }
+
+    QtObject {
+        id: internal
+        function getBackground() {
+            if (root.mode == "highlighted")
+                return "highlighted"
+            else if (root.mode == "disabled")
+                return "disabled"
+            else
+                return "normal"
+        }
+    }
+
+    SequentialAnimation {
+        id: releasedEffect
+        PropertyAnimation {
+            target: pressed
+            property: "opacity"
+            to: 0
+            easing.type: Easing.Linear
+            duration: 150
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Slider.qml b/mardrone/imports/com/nokia/android.1.1/Slider.qml
new file mode 100644 (file)
index 0000000..2910553
--- /dev/null
@@ -0,0 +1,344 @@
+/****************************************************************************
+**
+** 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 Qt.labs.components 1.1 as QtComponents
+
+Item {
+    id: root
+
+    // Common Public API
+    property alias stepSize: model.stepSize
+    property alias minimumValue: model.minimumValue
+    property alias maximumValue: model.maximumValue
+    property alias value: model.value
+    property int orientation: Qt.Horizontal
+    property bool pressed: track.inputActive
+    property bool valueIndicatorVisible: false
+    property string valueIndicatorText: ""
+    property alias inverted: model.inverted
+
+    LayoutMirroring.enabled: false
+    LayoutMirroring.childrenInherit: true
+
+    // Symbian specific
+    property bool platformInverted: false
+    signal valueChanged(real value)
+    implicitWidth: orientation == Qt.Horizontal ? 150 : privateStyle.menuItemHeight
+    implicitHeight: orientation == Qt.Horizontal ? privateStyle.menuItemHeight : 150
+
+    onActiveFocusChanged: {
+        if (!root.activeFocus)
+            track.activeKey = undefined
+    }
+
+    QtComponents.RangeModel {
+        id: model
+        value: 0.0
+        stepSize: 0.0
+        minimumValue: 0.0
+        maximumValue: 1.0
+        positionAtMinimum: 0
+        positionAtMaximum: orientation == Qt.Horizontal ? track.width - handle.width : track.height - handle.height
+        onValueChanged: root.valueChanged(value)
+    }
+
+    BorderImage {
+        id: track
+        objectName: "track"
+        property bool tapOnTrack: false
+        property variant activeKey
+        property bool inputActive: handleMouseArea.pressed || !!track.activeKey
+        onInputActiveChanged: {
+            if (!valueIndicatorVisible)
+                return
+            if (inputActive)
+                valueIndicator.show = true
+            else
+                indicatorTimer.restart()
+        }
+
+        states: [
+            State {
+                name: "Horizontal"
+                when: orientation == Qt.Horizontal
+
+                PropertyChanges {
+                    target: track
+                    height: privateStyle.sliderThickness
+                    width: undefined
+                    source: privateStyle.imagePath("qtg_fr_slider_h_track_normal", root.platformInverted)
+                    border {
+                        left: 20
+                        right: 20
+                        top: 0
+                        bottom: 0
+                    }
+                    smooth: true
+                }
+
+                AnchorChanges {
+                    target: track
+                    anchors {
+                        left: root.left
+                        right: root.right
+                        top: undefined
+                        bottom: undefined
+                        horizontalCenter: undefined
+                        verticalCenter: root.verticalCenter
+                    }
+                }
+            },
+            State {
+                name: "Vertical"
+                when: orientation == Qt.Vertical
+
+                PropertyChanges {
+                    target: track
+                    height: undefined
+                    width: privateStyle.sliderThickness
+                    source: privateStyle.imagePath("qtg_fr_slider_v_track_normal", root.platformInverted)
+                    border {
+                        left: 0
+                        right: 0
+                        top: 20
+                        bottom: 20
+                    }
+                    smooth: true
+                }
+
+                AnchorChanges {
+                    target: track
+                    anchors {
+                        left: undefined
+                        right: undefined
+                        top: root.top
+                        bottom: root.bottom
+                        horizontalCenter: root.horizontalCenter
+                        verticalCenter: undefined
+                    }
+                }
+            }
+        ]
+
+        anchors.leftMargin: platformStyle.paddingMedium
+        anchors.rightMargin: platformStyle.paddingMedium
+        anchors.topMargin: platformStyle.paddingMedium
+        anchors.bottomMargin: platformStyle.paddingMedium
+
+        MouseArea {
+            id: trackMouseArea
+            objectName: "trackMouseArea"
+
+            anchors.fill: parent
+
+            onClicked: {
+                if (track.tapOnTrack) {
+                    if (orientation == Qt.Horizontal) {
+                        if (handle.x > (mouseX - handle.width / 2)) {
+                            model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize
+                            handle.x = model.position
+                        } else {
+                            model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize
+                            handle.x = model.position
+                        }
+                    } else {
+                        if (handle.y > (mouseY - handle.height / 2)) {
+                            model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize
+                            handle.y = model.position
+                        } else {
+                            model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize
+                            handle.y = model.position
+                        }
+                    }
+                }
+            }
+
+            Image {
+                id: handle
+                objectName: "handle"
+                x: orientation == Qt.Horizontal ? (handleMouseArea.pressed ? x : model.position) : 0
+                y: orientation == Qt.Horizontal ? 0 : (handleMouseArea.pressed ? y : model.position)
+
+                sourceSize.height: platformStyle.graphicSizeSmall
+                sourceSize.width: platformStyle.graphicSizeSmall
+
+                anchors.verticalCenter: orientation == Qt.Horizontal ? parent.verticalCenter : undefined
+                anchors.horizontalCenter: orientation == Qt.Horizontal ? undefined : parent.horizontalCenter
+
+                source: {
+                    var handleIcon = "qtg_graf_slider_"
+                        + (orientation == Qt.Horizontal ? "h" : "v")
+                        + "_handle_"
+                        + (handleMouseArea.pressed ? "pressed" : "normal")
+                    privateStyle.imagePath(handleIcon, root.platformInverted)
+                }
+
+                onXChanged: valueIndicator.position()
+                onYChanged: valueIndicator.position()
+
+                MouseArea {
+                    id: handleMouseArea
+                    objectName: "handleMouseArea"
+
+                    height: platformStyle.graphicSizeMedium
+                    width: platformStyle.graphicSizeMedium
+                    anchors.verticalCenter: orientation == Qt.Horizontal ? parent.verticalCenter : undefined
+                    anchors.horizontalCenter: orientation == Qt.Horizontal ? undefined : parent.horizontalCenter
+
+                    drag.target: handle
+                    drag.axis: Drag.XandYAxis
+                    drag.minimumX: orientation == Qt.Horizontal ? model.positionAtMinimum : 0
+                    drag.maximumX: orientation == Qt.Horizontal ? model.positionAtMaximum : 0
+                    drag.minimumY: orientation == Qt.Horizontal ? 0 : model.positionAtMinimum
+                    drag.maximumY: orientation == Qt.Horizontal ? 0 : model.positionAtMaximum
+
+                    onPositionChanged: model.position = orientation == Qt.Horizontal ? handle.x : handle.y
+                    onPressed: privateStyle.play(Android.BasicSlider)
+                    onReleased: privateStyle.play(Android.BasicSlider)
+                }
+            }
+        }
+    }
+
+    Keys.onPressed: {
+        internal.handleKeyPressEvent(event)
+    }
+
+    Keys.onReleased: {
+        internal.handleKeyReleaseEvent(event)
+    }
+
+    Component {
+        id: valueIndicatorComponent
+        ToolTip {
+            text: root.valueIndicatorText == "" ? model.value : root.valueIndicatorText
+            platformInverted: root.platformInverted
+        }
+    }
+
+    Loader {
+        id: valueIndicator
+
+        property int spacing: 2 * platformStyle.paddingLarge
+        // Must match with the "maxWidth" padding defined in ToolTip
+        property int toolTipPadding: platformStyle.paddingLarge
+        property bool show: false
+        sourceComponent: valueIndicator.show ? valueIndicatorComponent : undefined
+        onLoaded: position()
+
+        function position() {
+            if (!valueIndicatorVisible || status != Loader.Ready)
+                return
+
+            var point = null
+            if (orientation == Qt.Horizontal) {
+                point = root.mapFromItem(track, handle.x + handle.width / 2 - valueIndicator.item.width / 2, 0)
+
+                // Check if valueIndicator will be positioned beyond the right or
+                // left boundaries and adjust if needed to keep it fully
+                // visible on screen. In case the valueIndicator is so wide that it
+                // does not fit the screen, it's positioned to left of the screen.
+                var rightStop = screen.width - toolTipPadding
+                var valueIndicatorLeftEdge = root.mapToItem(null, point.x, 0)
+                var valueIndicatorRightEdge = root.mapToItem(null, point.x + valueIndicator.item.width, 0)
+
+                if (valueIndicatorLeftEdge.x < toolTipPadding)
+                    point.x = root.mapFromItem(null, toolTipPadding, 0).x
+                else if (valueIndicatorRightEdge.x > rightStop)
+                    point.x = root.mapFromItem(null, rightStop - valueIndicator.item.width, 0).x
+
+                valueIndicator.item.x = point.x
+                valueIndicator.item.y = point.y - valueIndicator.spacing - valueIndicator.item.height
+            } else {
+                point = root.mapFromItem(track, 0, handle.y + handle.height / 2 - valueIndicator.item.height / 2)
+                valueIndicator.item.x = point.x - valueIndicator.spacing - valueIndicator.item.width
+                valueIndicator.item.y = point.y
+            }
+        }
+
+        Timer {
+            id: indicatorTimer
+            interval: 750
+            onTriggered: {
+                if (!track.inputActive)
+                    valueIndicator.show = false
+            }
+        }
+    }
+
+    QtObject {
+        id: internal
+
+        function handleKeyPressEvent(keyEvent) {
+            var oldValue = model.value
+            if (orientation == Qt.Horizontal) {
+                if (keyEvent.key == Qt.Key_Left) {
+                    model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize
+                } else if (keyEvent.key == Qt.Key_Right) {
+                    model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize
+                }
+            } else { //Vertical
+                if (keyEvent.key == Qt.Key_Up) {
+                    model.value = inverted ? model.value + model.stepSize : model.value - model.stepSize
+                } else if (keyEvent.key == Qt.Key_Down) {
+                    model.value = inverted ? model.value - model.stepSize : model.value + model.stepSize
+                }
+            }
+            if (oldValue != model.value)
+                keyEvent.accepted = true
+
+            if (keyEvent.accepted ||
+                keyEvent.key == Qt.Key_Select ||
+                keyEvent.key == Qt.Key_Return ||
+                keyEvent.key == Qt.Key_Enter)
+                track.activeKey = keyEvent.key
+        }
+
+        function handleKeyReleaseEvent(keyEvent) {
+            if (track.activeKey == keyEvent.key) {
+                if (!keyEvent.isAutoRepeat)
+                    track.activeKey = undefined
+                keyEvent.accepted = true
+            }
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/StatusBar.qml b/mardrone/imports/com/nokia/android.1.1/StatusBar.qml
new file mode 100644 (file)
index 0000000..073dc6e
--- /dev/null
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    implicitWidth: screen.width
+    implicitHeight: privateStyle.statusBarHeight
+    property bool platformInverted: false
+
+//    QtObject {
+//        id: priv
+//        objectName: "priv"
+
+//        property bool clickedOpensStatusPanel: android.s60Version == Android.SV_S60_5_2 ? true : false
+//        property int contentHeight: Math.round(privateStyle.statusBarHeight * 18 / 26)
+//        property int paddingSmallOneQuarter: Math.round(platformStyle.paddingSmall / 4)
+//        property int paddingSmallThreeQuarters: Math.round(platformStyle.paddingSmall * 3 / 4)
+
+//        function signalWidthPercentage(signalStrength) {
+//            if (signalStrength < 10)
+//                return 0;
+//            else if (signalStrength < 20)
+//                return 1/5;
+//            else if (signalStrength < 30)
+//                return 2/5;
+//            else if (signalStrength < 60)
+//                return 3/5;
+//            else if (signalStrength < 100)
+//                return 4/5;
+//            else
+//                return 1;
+//        }
+//    }
+
+//    MouseArea {
+//        id: mouseArea
+//        anchors.fill: parent
+
+//        onClicked: {
+//            if (priv.clickedOpensStatusPanel) {
+//                privateStyle.play(Android.PopUp)
+//                platformPopupManager.privateShowIndicatorPopup()
+//            }
+//        }
+//        onPressed: {
+//            if (!priv.clickedOpensStatusPanel) {
+//                privateStyle.play(Android.PopUp)
+//                platformPopupManager.privateShowIndicatorPopup()
+//                // reset MouseArea state since status panel window eats the release event
+//                android.privateSendMouseRelease(mouseArea)
+//            }
+//        }
+//    }
+
+//    BorderImage {
+//        source: privateStyle.imagePath("qtg_fr_statusbar", root.platformInverted)
+//        anchors.fill: parent
+//        width: parent.width
+
+//        UniversalIndicators {
+//            height: indicatorHeight
+//            anchors {
+//                right: networkMode.left
+//                rightMargin: priv.paddingSmallOneQuarter
+//                verticalCenter: parent.verticalCenter
+//                }
+
+//            width: priv.paddingSmallOneQuarter * 2 + 3 * indicatorWidth
+
+//            indicatorColor: root.platformInverted ? platformStyle.colorNormalDarkInverted
+//                                                  : platformStyle.colorNormalLight
+//            indicatorWidth: priv.contentHeight // same as height
+//            indicatorHeight: priv.contentHeight
+//            indicatorPadding: priv.paddingSmallOneQuarter
+//            maxIndicatorCount: 3
+//        }
+
+//        // icon for network signal type e.g. 3G, GPRS etc
+//        NetworkIndicator {
+//            id: networkMode
+//            height: priv.contentHeight
+//            width: priv.contentHeight // same as height
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: offline ? batteryBackground.left : signalBackground.left
+//            anchors.rightMargin: priv.paddingSmallThreeQuarters
+//            color: root.platformInverted ? platformStyle.colorNormalDarkInverted
+//                                         : platformStyle.colorNormalLight
+//        }
+//        // signal strength
+//        Image {
+//            id: signalBackground
+//            visible: !networkMode.offline
+//            sourceSize.height: priv.contentHeight
+//            sourceSize.width: Math.round(privateStyle.statusBarHeight * 19 / 26)
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: batteryBackground.left
+//            anchors.rightMargin: priv.paddingSmallThreeQuarters
+//            fillMode: Image.PreserveAspectFit
+//            source: privateStyle.imagePath("qtg_graf_signal_level_bg", root.platformInverted)
+//            Item {
+//                id: signalLevelItem
+//                anchors.left: parent.left
+//                anchors.top: parent.top
+//                height: parent.height
+//                width: priv.signalWidthPercentage(privateNetworkInfo.networkSignalStrength) * parent.width
+//                clip: true
+//                LayoutMirroring.enabled: false
+
+//                Image {
+//                    sourceSize.width: signalBackground.sourceSize.width
+//                    sourceSize.height: signalBackground.sourceSize.height
+//                    fillMode: Image.PreserveAspectFit
+//                    source: privateStyle.imagePath("qtg_graf_signal_level_full", root.platformInverted)
+//                }
+//            }
+//        }
+//        // battery indicator
+//        Image {
+//            id: batteryBackground
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: timeItem.left
+//            anchors.rightMargin: priv.paddingSmallThreeQuarters
+//            sourceSize.height: priv.contentHeight
+//            sourceSize.width: Math.round(privateStyle.statusBarHeight * 24 / 26)
+//            fillMode: Image.PreserveAspectFit
+//            source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ?
+//                "qtg_graf_battery_level_psm_bg" :
+//                "qtg_graf_battery_level_bg"), root.platformInverted)
+
+//            Item {
+//                id: batteryLevel
+
+//                property int animatedLevel
+
+//                anchors.left: parent.left
+//                anchors.top: parent.top
+//                width: Math.round(privateStyle.statusBarHeight
+//                    * ((privateBatteryInfo.charging ? Math.floor(animatedLevel / 100) :
+//                        privateBatteryInfo.batteryLevel) + 2) / 13)
+//                height: parent.height
+//                clip: true
+//                LayoutMirroring.enabled: false
+
+//                Image {
+//                    sourceSize.width: batteryBackground.sourceSize.width
+//                    sourceSize.height: batteryBackground.sourceSize.height
+
+//                    fillMode: Image.PreserveAspectFit
+
+//                    // Battery state mappings: Levels 0 and 1 are low, 2-4 are medium, 5-7 are full.
+//                    // Currently all levels use same graphics with white color.
+
+//                    source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ?
+//                        "qtg_graf_battery_level_psm_full" :
+//                        "qtg_graf_battery_level_full"), root.platformInverted)
+//                }
+//            }
+
+//            Image {
+//                // power save mode indicator
+//                anchors.fill: parent
+//                sourceSize.width: parent.sourceSize.width
+//                sourceSize.height: parent.sourceSize.height
+//                source: privateStyle.imagePath("qtg_graf_battery_psm")
+//                visible: privateBatteryInfo.powerSaveModeEnabled
+//            }
+
+//            NumberAnimation {
+//                id: batteryChargingAnimation
+//                loops: Animation.Infinite
+//                running: privateBatteryInfo.charging
+//                target: batteryLevel
+//                property: "animatedLevel"
+//                // Use bigger range (compared to 0-7) in order to make the animation smoother.
+//                from: 0
+//                to: 799
+//                duration: 3500
+//            }
+//        }
+//        // clock
+//        Text {
+//            id: timeItem
+//            width: Math.round(privateStyle.statusBarHeight * 44 / 26)
+//            color: root.platformInverted ? platformStyle.colorNormalDarkInverted
+//                                         : platformStyle.colorNormalLight
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: parent.right
+//            anchors.rightMargin: platformStyle.paddingSmall
+//            horizontalAlignment: Text.AlignRight
+//            text: android.currentTime
+//            font {
+//                family: platformStyle.fontFamilyRegular;
+//                pixelSize: priv.contentHeight
+//                weight: Font.Light
+//            }
+//        }
+//    }
+}
+
diff --git a/mardrone/imports/com/nokia/android.1.1/StatusBar.qml~ b/mardrone/imports/com/nokia/android.1.1/StatusBar.qml~
new file mode 100644 (file)
index 0000000..d9853db
--- /dev/null
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+//    id: root
+
+//    implicitWidth: screen.width
+//    implicitHeight: privateStyle.statusBarHeight
+    property bool platformInverted: false
+
+//    QtObject {
+//        id: priv
+//        objectName: "priv"
+
+//        property bool clickedOpensStatusPanel: android.s60Version == Android.SV_S60_5_2 ? true : false
+//        property int contentHeight: Math.round(privateStyle.statusBarHeight * 18 / 26)
+//        property int paddingSmallOneQuarter: Math.round(platformStyle.paddingSmall / 4)
+//        property int paddingSmallThreeQuarters: Math.round(platformStyle.paddingSmall * 3 / 4)
+
+//        function signalWidthPercentage(signalStrength) {
+//            if (signalStrength < 10)
+//                return 0;
+//            else if (signalStrength < 20)
+//                return 1/5;
+//            else if (signalStrength < 30)
+//                return 2/5;
+//            else if (signalStrength < 60)
+//                return 3/5;
+//            else if (signalStrength < 100)
+//                return 4/5;
+//            else
+//                return 1;
+//        }
+//    }
+
+//    MouseArea {
+//        id: mouseArea
+//        anchors.fill: parent
+
+//        onClicked: {
+//            if (priv.clickedOpensStatusPanel) {
+//                privateStyle.play(Android.PopUp)
+//                platformPopupManager.privateShowIndicatorPopup()
+//            }
+//        }
+//        onPressed: {
+//            if (!priv.clickedOpensStatusPanel) {
+//                privateStyle.play(Android.PopUp)
+//                platformPopupManager.privateShowIndicatorPopup()
+//                // reset MouseArea state since status panel window eats the release event
+//                android.privateSendMouseRelease(mouseArea)
+//            }
+//        }
+//    }
+
+//    BorderImage {
+//        source: privateStyle.imagePath("qtg_fr_statusbar", root.platformInverted)
+//        anchors.fill: parent
+//        width: parent.width
+
+//        UniversalIndicators {
+//            height: indicatorHeight
+//            anchors {
+//                right: networkMode.left
+//                rightMargin: priv.paddingSmallOneQuarter
+//                verticalCenter: parent.verticalCenter
+//                }
+
+//            width: priv.paddingSmallOneQuarter * 2 + 3 * indicatorWidth
+
+//            indicatorColor: root.platformInverted ? platformStyle.colorNormalDarkInverted
+//                                                  : platformStyle.colorNormalLight
+//            indicatorWidth: priv.contentHeight // same as height
+//            indicatorHeight: priv.contentHeight
+//            indicatorPadding: priv.paddingSmallOneQuarter
+//            maxIndicatorCount: 3
+//        }
+
+//        // icon for network signal type e.g. 3G, GPRS etc
+//        NetworkIndicator {
+//            id: networkMode
+//            height: priv.contentHeight
+//            width: priv.contentHeight // same as height
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: offline ? batteryBackground.left : signalBackground.left
+//            anchors.rightMargin: priv.paddingSmallThreeQuarters
+//            color: root.platformInverted ? platformStyle.colorNormalDarkInverted
+//                                         : platformStyle.colorNormalLight
+//        }
+//        // signal strength
+//        Image {
+//            id: signalBackground
+//            visible: !networkMode.offline
+//            sourceSize.height: priv.contentHeight
+//            sourceSize.width: Math.round(privateStyle.statusBarHeight * 19 / 26)
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: batteryBackground.left
+//            anchors.rightMargin: priv.paddingSmallThreeQuarters
+//            fillMode: Image.PreserveAspectFit
+//            source: privateStyle.imagePath("qtg_graf_signal_level_bg", root.platformInverted)
+//            Item {
+//                id: signalLevelItem
+//                anchors.left: parent.left
+//                anchors.top: parent.top
+//                height: parent.height
+//                width: priv.signalWidthPercentage(privateNetworkInfo.networkSignalStrength) * parent.width
+//                clip: true
+//                LayoutMirroring.enabled: false
+
+//                Image {
+//                    sourceSize.width: signalBackground.sourceSize.width
+//                    sourceSize.height: signalBackground.sourceSize.height
+//                    fillMode: Image.PreserveAspectFit
+//                    source: privateStyle.imagePath("qtg_graf_signal_level_full", root.platformInverted)
+//                }
+//            }
+//        }
+//        // battery indicator
+//        Image {
+//            id: batteryBackground
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: timeItem.left
+//            anchors.rightMargin: priv.paddingSmallThreeQuarters
+//            sourceSize.height: priv.contentHeight
+//            sourceSize.width: Math.round(privateStyle.statusBarHeight * 24 / 26)
+//            fillMode: Image.PreserveAspectFit
+//            source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ?
+//                "qtg_graf_battery_level_psm_bg" :
+//                "qtg_graf_battery_level_bg"), root.platformInverted)
+
+//            Item {
+//                id: batteryLevel
+
+//                property int animatedLevel
+
+//                anchors.left: parent.left
+//                anchors.top: parent.top
+//                width: Math.round(privateStyle.statusBarHeight
+//                    * ((privateBatteryInfo.charging ? Math.floor(animatedLevel / 100) :
+//                        privateBatteryInfo.batteryLevel) + 2) / 13)
+//                height: parent.height
+//                clip: true
+//                LayoutMirroring.enabled: false
+
+//                Image {
+//                    sourceSize.width: batteryBackground.sourceSize.width
+//                    sourceSize.height: batteryBackground.sourceSize.height
+
+//                    fillMode: Image.PreserveAspectFit
+
+//                    // Battery state mappings: Levels 0 and 1 are low, 2-4 are medium, 5-7 are full.
+//                    // Currently all levels use same graphics with white color.
+
+//                    source: privateStyle.imagePath((privateBatteryInfo.powerSaveModeEnabled ?
+//                        "qtg_graf_battery_level_psm_full" :
+//                        "qtg_graf_battery_level_full"), root.platformInverted)
+//                }
+//            }
+
+//            Image {
+//                // power save mode indicator
+//                anchors.fill: parent
+//                sourceSize.width: parent.sourceSize.width
+//                sourceSize.height: parent.sourceSize.height
+//                source: privateStyle.imagePath("qtg_graf_battery_psm")
+//                visible: privateBatteryInfo.powerSaveModeEnabled
+//            }
+
+//            NumberAnimation {
+//                id: batteryChargingAnimation
+//                loops: Animation.Infinite
+//                running: privateBatteryInfo.charging
+//                target: batteryLevel
+//                property: "animatedLevel"
+//                // Use bigger range (compared to 0-7) in order to make the animation smoother.
+//                from: 0
+//                to: 799
+//                duration: 3500
+//            }
+//        }
+//        // clock
+//        Text {
+//            id: timeItem
+//            width: Math.round(privateStyle.statusBarHeight * 44 / 26)
+//            color: root.platformInverted ? platformStyle.colorNormalDarkInverted
+//                                         : platformStyle.colorNormalLight
+//            anchors.verticalCenter: parent.verticalCenter
+//            anchors.right: parent.right
+//            anchors.rightMargin: platformStyle.paddingSmall
+//            horizontalAlignment: Text.AlignRight
+//            text: android.currentTime
+//            font {
+//                family: platformStyle.fontFamilyRegular;
+//                pixelSize: priv.contentHeight
+//                weight: Font.Light
+//            }
+//        }
+//    }
+}
+
diff --git a/mardrone/imports/com/nokia/android.1.1/Switch.qml b/mardrone/imports/com/nokia/android.1.1/Switch.qml
new file mode 100644 (file)
index 0000000..207d5c0
--- /dev/null
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    // Common Public API
+    property bool checked: false
+    property bool pressed: stateGroup.state == "Pressed" || stateGroup.state == "KeyPressed"
+                           || stateGroup.state == "Dragging"
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    signal clicked
+
+    QtObject {
+        id: internal
+        objectName: "internal"
+
+        function toggle() {
+            root.checked = !root.checked
+            root.clicked()
+        }
+    }
+
+    StateGroup {
+        id: stateGroup
+
+        states: [
+            State { name: "Pressed" },
+            State { name: "Released" },
+            State { name: "KeyPressed" },
+            State { name: "Dragging" },
+            State { name: "Canceled" }
+        ]
+
+        transitions: [
+            Transition {
+                to: "Pressed"
+                ScriptAction { script: privateStyle.play(Android.BasicItem) }
+            },
+            Transition {
+                from: "Released, Dragging"
+                to: ""
+                ScriptAction { script: privateStyle.play(Android.CheckBox) }
+                ScriptAction { script: internal.toggle() }
+            },
+            Transition {
+                from: "KeyPressed"
+                to: ""
+                ScriptAction { script: internal.toggle() }
+            }
+        ]
+    }
+
+    implicitWidth: track.width
+    implicitHeight: privateStyle.switchButtonHeight
+
+    Image {
+        id: track
+
+        function trackPostfix() {
+            if (!root.enabled && root.checked)
+                return "disabled_on"
+            else if (!root.enabled && !root.checked)
+                return "disabled_off"
+            else
+                return "track"
+        }
+
+        source: privateStyle.imagePath("qtg_graf_switchbutton_" + track.trackPostfix(),
+                                       root.platformInverted)
+        anchors.left: parent.left
+        anchors.verticalCenter: parent.verticalCenter
+        sourceSize.width: Android.UndefinedSourceDimension
+        sourceSize.height: privateStyle.switchButtonHeight
+        scale: root.LayoutMirroring.enabled ? -1 : 1
+    }
+
+    MouseArea {
+        id: mouseArea
+
+        property real lastX
+
+        function isChecked() {
+            if (root.LayoutMirroring.enabled)
+                return (handle.x + handle.width / 2 < track.x + (track.width / 2))
+            else
+                return (handle.x + handle.width / 2 > track.x + (track.width / 2))
+        }
+        function updateHandlePos() {
+            // The middle of the handle follows mouse, the handle is bound to the track
+            handle.x = Math.max(track.x, Math.min(mouseArea.lastX - handle.width / 2,
+                                                  track.x + track.width - handle.width))
+        }
+
+        anchors.fill: track
+        onPressed: stateGroup.state = "Pressed"
+        onReleased: stateGroup.state = "Released" // releasing doesn't toggle yet, it is intermediate state
+        onClicked: stateGroup.state = ""
+        onCanceled: stateGroup.state = "Canceled"
+        onPositionChanged: {
+            mouseArea.lastX = mouse.x
+            if (mouseArea.drag.active)
+                updateHandlePos()
+        }
+        drag {
+            // The handle is moved manually but MouseArea can be used to decide when dragging
+            // should start (QApplication::startDragDistance). A dummy target needs to be bound or
+            // dragging won't get activated.
+            target: Item { visible: false }
+
+            axis: Drag.XandYAxis
+            minimumY: 0; maximumY: 0 // keep dragging active eventhough only x axis switches
+            minimumX: track.x; maximumX: mouseArea.drag.minimumX + track.width - handle.width
+            onActiveChanged: {
+                if (mouseArea.drag.active) {
+                    updateHandlePos()
+                    stateGroup.state = "Dragging"
+                }
+                else {
+                    stateGroup.state = (root.checked != isChecked()) ? "" : "Canceled"
+                }
+            }
+        }
+    }
+
+    Item {
+        id: fill
+
+        clip: true
+        anchors.left: track.left
+        anchors.right: handle.horizontalCenter
+        anchors.verticalCenter: parent.verticalCenter
+        height: privateStyle.switchButtonHeight
+        visible: root.enabled
+
+        Image {
+            source: privateStyle.imagePath("qtg_graf_switchbutton_fill",
+                                           root.platformInverted)
+            anchors.left: parent.left
+            anchors.top: parent.top
+            height: parent.height
+        }
+    }
+
+    Image {
+        id: handle
+
+        source: privateStyle.imagePath("qtg_graf_switchbutton_"
+                                       + (root.pressed ? "handle_pressed" : "handle_normal"),
+                                       root.platformInverted)
+        anchors.verticalCenter: root.verticalCenter
+        sourceSize.width: privateStyle.switchButtonHeight
+        sourceSize.height: privateStyle.switchButtonHeight
+        visible: root.enabled
+
+        states: [
+            State {
+                name: "Off"
+                when: !mouseArea.drag.active && !checked
+                PropertyChanges {
+                    target: handle
+                    restoreEntryValues: false
+                    x: root.LayoutMirroring.enabled ? mouseArea.drag.maximumX : mouseArea.drag.minimumX
+                }
+            },
+            State {
+                name: "On"
+                when: !mouseArea.drag.active && checked
+                PropertyChanges {
+                    target: handle
+                    restoreEntryValues: false
+                    x: root.LayoutMirroring.enabled ? mouseArea.drag.minimumX : mouseArea.drag.maximumX
+                }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                to: "Off"
+                SmoothedAnimation {properties: "x"; easing.type: Easing.InOutQuad; duration: 200 }
+            },
+            Transition {
+                to: "On"
+                SmoothedAnimation {properties: "x"; easing.type: Easing.InOutQuad; duration: 200 }
+            }
+        ]
+    }
+
+    Keys.onPressed: {
+        if (!event.isAutoRepeat && (event.key == Qt.Key_Select
+                                    || event.key == Qt.Key_Return
+                                    || event.key == Qt.Key_Enter)) {
+            stateGroup.state = "KeyPressed"
+            event.accepted = true
+        }
+    }
+
+
+    Keys.onReleased: {
+        if (!event.isAutoRepeat && (event.key == Qt.Key_Select
+                                    || event.key == Qt.Key_Return
+                                    || event.key == Qt.Key_Enter)) {
+            stateGroup.state = ""
+            event.accepted = true
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TabBar.qml b/mardrone/imports/com/nokia/android.1.1/TabBar.qml
new file mode 100644 (file)
index 0000000..86f050a
--- /dev/null
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+    default property alias content: tabBarLayout.data
+    property alias layout: tabBarLayout
+    property bool platformInverted: false
+
+    implicitWidth: Math.max(50, screen.width)
+    implicitHeight: screen.width < screen.height ? privateStyle.tabBarHeightPortrait : privateStyle.tabBarHeightLandscape
+
+    BorderImage {
+        anchors.fill: parent
+        source: privateStyle.imagePath("qtg_fr_tab_bar", root.platformInverted)
+        border { left: 20; top: 20; right: 20; bottom: 20 }
+    }
+
+    TabBarLayout {
+        id: tabBarLayout
+        anchors.fill: parent
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TabBarLayout.qml b/mardrone/imports/com/nokia/android.1.1/TabBarLayout.qml
new file mode 100644 (file)
index 0000000..b258357
--- /dev/null
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    Component.onCompleted: priv.layoutChildren()
+    onChildrenChanged: priv.layoutChildren()
+    onWidthChanged: priv.layoutChildren()
+    onHeightChanged: priv.layoutChildren()
+
+    Keys.onPressed: {
+        if (event.key == Qt.Key_Right || event.key == Qt.Key_Left) {
+            if (event.key == Qt.Key_Right || priv.mirrored) {
+                var oldIndex = priv.currentButtonIndex()
+                if (oldIndex != root.children.length - 1) {
+                    priv.tabGroup.currentTab = root.children[oldIndex + 1].tab
+                    event.accepted = true
+                }
+            } else if (event.key == Qt.Key_Left || priv.mirrored) {
+                var oldIndex = priv.currentButtonIndex()
+                if (oldIndex != 0) {
+                    priv.tabGroup.currentTab = root.children[oldIndex - 1].tab
+                    event.accepted = true
+                }
+            }
+        }
+    }
+
+    focus: true
+
+    QtObject {
+        id: priv
+        property Item firstButton: root.children.length > 0 ? root.children[0] : null
+        property Item firstTab: firstButton ? (firstButton.tab != null ? firstButton.tab : null) : null
+        property Item tabGroup: firstTab ? (firstTab.parent ? firstTab.parent.parent : null) : null
+        property bool mirrored: root.LayoutMirroring.enabled
+
+        onMirroredChanged: layoutChildren()
+
+        function currentButtonIndex() {
+            for (var i = 0; i < root.children.length; ++i) {
+                if (root.children[i].tab == tabGroup.currentTab)
+                    return i
+            }
+            return -1
+        }
+
+        function layoutChildren() {
+            var childCount = root.children.length
+            var contentWidth = 0
+            var contentHeight = 0
+            if (childCount != 0) {
+                var itemWidth = root.width / childCount
+                var itemIndex = mirrored ? childCount - 1 : 0
+                var increment = mirrored ? - 1 : 1
+
+                for (var i = 0; i < childCount; ++i, itemIndex += increment) {
+                    var child = root.children[itemIndex]
+                    child.x = i * itemWidth
+                    child.y = 0
+                    child.width = itemWidth
+                    child.height = root.height
+
+                    if (child.implicitWidth != undefined) {
+                        contentWidth = Math.max(contentWidth, child.implicitWidth * childCount)
+                        contentHeight = Math.max(contentHeight, child.implicitHeight)
+                    }
+                }
+            }
+            root.implicitWidth = contentWidth
+            root.implicitHeight = contentHeight
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TabButton.qml b/mardrone/imports/com/nokia/android.1.1/TabButton.qml
new file mode 100644 (file)
index 0000000..fce0fb0
--- /dev/null
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** 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 "AppManager.js" as Utils
+
+Item {
+    id: root
+
+    // Common Public API
+    property Item tab
+    property bool checked: internal.tabGroup != null && internal.tabGroup.currentTab == tab
+    property bool pressed: stateGroup.state == "Pressed" && mouseArea.containsMouse
+    property alias text: label.text
+    property alias iconSource: imageLoader.source
+
+    signal clicked
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    implicitWidth: Math.max(2 * imageLoader.width, 2 * platformStyle.paddingMedium + privateStyle.textWidth(label.text, label.font))
+    implicitHeight: internal.portrait ? privateStyle.tabBarHeightPortrait : privateStyle.tabBarHeightLandscape
+
+    QtObject {
+        id: internal
+
+        property Item tabGroup: Utils.findParent(tab, "currentTab")
+        property bool portrait: screen.currentOrientation == Screen.Portrait || screen.currentOrientation == Screen.PortraitInverted
+
+        function press() {
+            privateStyle.play(Android.BasicButton)
+        }
+        function click() {
+            root.clicked()
+            privateStyle.play(Android.BasicButton)
+            if (internal.tabGroup)
+                internal.tabGroup.currentTab = tab
+        }
+
+        function isButtonRow(item) {
+            return (item &&
+                    item.hasOwnProperty("checkedButton") &&
+                    item.hasOwnProperty("privateDirection") &&
+                    item.privateDirection == Qt.Horizontal)
+        }
+
+        function imageName() {
+            // If the parent of a TabButton is ButtonRow, segmented-style graphics
+            // are used to create a seamless row of buttons. Otherwise normal
+            // TabButton graphics are utilized.
+            var mirror = root.LayoutMirroring.enabled // To create binding
+            if (isButtonRow(parent))
+                return parent.privateGraphicsName(root, 1)
+            else
+                return "qtg_fr_tab_"
+        }
+
+        function modeName() {
+            if (isButtonRow(parent)) {
+                return parent.privateModeName(root, 1)
+            } else {
+                return root.checked ? "active" : "passive_normal"
+            }
+        }
+
+        function modeNamePressed() {
+            if (isButtonRow(parent)) {
+                return "pressed"
+            } else {
+                return "passive_pressed"
+            }
+        }
+    }
+
+    StateGroup {
+        id: stateGroup
+
+        states: [
+            State {
+                name: "Pressed"
+                PropertyChanges { target: pressedGraphics; opacity: 1 }
+            },
+            State { name: "Canceled" }
+        ]
+        transitions: [
+            Transition {
+                to: "Pressed"
+                ScriptAction { script: internal.press() }
+            },
+            Transition {
+                from: "Pressed"
+                to: ""
+                NumberAnimation {
+                    target: pressedGraphics
+                    property: "opacity"
+                    to: 0
+                    duration: 150
+                    easing.type: Easing.Linear
+                }
+                ScriptAction { script: internal.click() }
+            }
+        ]
+    }
+
+    BorderImage {
+        id: background
+
+        source: privateStyle.imagePath(internal.imageName() + internal.modeName(), root.platformInverted)
+        anchors.fill: parent
+        border {
+            left: platformStyle.borderSizeMedium
+            top: platformStyle.borderSizeMedium
+            right: platformStyle.borderSizeMedium
+            bottom: platformStyle.borderSizeMedium
+        }
+    }
+
+    BorderImage {
+        id: pressedGraphics
+
+        source: privateStyle.imagePath(internal.imageName() + internal.modeNamePressed(), root.platformInverted)
+        anchors.fill: parent
+        opacity: 0
+
+        border {
+            left: platformStyle.borderSizeMedium
+            top: platformStyle.borderSizeMedium
+            right: platformStyle.borderSizeMedium
+            bottom: platformStyle.borderSizeMedium
+        }
+    }
+
+    Text {
+        id: label
+
+        objectName: "label"
+        // hide in landscape if icon is present
+        visible: !(iconSource.toString() && !internal.portrait)
+        anchors {
+            left: parent.left
+            right: parent.right
+            bottom: parent.bottom
+            leftMargin: platformStyle.paddingMedium
+            rightMargin: platformStyle.paddingMedium
+            bottomMargin: iconSource.toString()
+                ? platformStyle.paddingSmall
+                : Math.ceil((parent.height - label.height) / 2.0)
+        }
+        elide: Text.ElideRight
+        horizontalAlignment: Text.AlignHCenter
+        font {
+            family: platformStyle.fontFamilyRegular;
+            pixelSize: platformStyle.fontSizeSmall
+            weight: Font.Light
+        }
+        color: {
+            if (root.pressed)
+                root.platformInverted ? platformStyle.colorPressedInverted
+                                      : platformStyle.colorPressed
+            else if (root.checked)
+                root.platformInverted ? platformStyle.colorNormalLightInverted
+                                      : platformStyle.colorNormalLight
+            else
+                root.platformInverted ? platformStyle.colorNormalMidInverted
+                                      : platformStyle.colorNormalMid
+        }
+    }
+
+    Loader {
+        // imageLoader acts as wrapper for Image and Icon items. The Image item is
+        // shown when the source points to a image (jpg, png). Icon item is used for
+        // locigal theme icons which are colorised.
+        id: imageLoader
+
+        property url source
+        property string iconId: privateStyle.isTabBarIcon(source) ? source.toString() : ""
+
+        width : platformStyle.graphicSizeSmall
+        height : platformStyle.graphicSizeSmall
+        sourceComponent: {
+            if (iconId)
+                return iconComponent
+            if (source.toString())
+                return imageComponent
+            return undefined
+        }
+        anchors {
+            top: parent.top
+            topMargin : !parent.text || !internal.portrait
+                ? Math.floor((parent.height - imageLoader.height) / 2.0)
+                : platformStyle.paddingSmall
+            horizontalCenter: parent.horizontalCenter
+        }
+
+        Component {
+            id: imageComponent
+
+            Image {
+                id: image
+
+                objectName: "image"
+                source: imageLoader.iconId ? "" : imageLoader.source
+                sourceSize.width: width
+                sourceSize.height: height
+                fillMode: Image.PreserveAspectFit
+                smooth: true
+                anchors.fill: parent
+            }
+        }
+
+        Component {
+            id: iconComponent
+
+            Icon {
+                id: icon
+
+                objectName: "icon"
+                anchors.fill: parent
+                iconColor: label.color
+                iconName: imageLoader.iconId
+            }
+        }
+    }
+
+    MouseArea {
+        id: mouseArea
+
+        onPressed: stateGroup.state = "Pressed"
+        onReleased: stateGroup.state = ""
+        onCanceled: {
+            // Mark as canceled
+            stateGroup.state = "Canceled"
+            // Reset state
+            stateGroup.state = ""
+        }
+        onExited: {
+            if (pressed)
+                stateGroup.state = "Canceled"
+        }
+
+        anchors.fill: parent
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TabGroup.js b/mardrone/imports/com/nokia/android.1.1/TabGroup.js
new file mode 100644 (file)
index 0000000..da1c654
--- /dev/null
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+var allContainers = []
+var allContents = []
+
+function hasContainer(content) {
+    for (var i = 0; i < allContainers.length; ++i) {
+        if (allContainers[i].children[0] == content)
+            return true
+    }
+    return false
+}
+
+function addContent(content) {
+    for (var i = 0; i < allContents.length; ++i) {
+        if (allContents[i] == content)
+            return false
+    }
+    allContents.push(content)
+    return true
+}
+
+function ensureContainers() {
+    var somethingChanged = false
+
+    // check if we need to create a container
+    for (var i = 0; i < root.privateContents.length; ++i) {
+        var content = root.privateContents[i]
+        addContent(content)
+        if (!hasContainer(content)) {
+            var newContainer = tabContainerComponent.createObject(containerHost)
+            content.parent = newContainer
+            allContainers.push(newContainer)
+            somethingChanged = true
+        }
+    }
+    return somethingChanged
+}
+
+function addTab(content) {
+    if (addContent(content)) {
+        var newContainer = tabContainerComponent.createObject(containerHost)
+        content.parent = newContainer
+        allContainers.push(newContainer)
+    }
+}
+
+function removeTab(content) {
+    var foundIndex = -1
+    for (var i = 0; i < allContents.length && foundIndex == -1; ++i) {
+        if (allContents[i] == content)
+            foundIndex = i
+    }
+
+    if (foundIndex != -1)
+        allContents.splice(foundIndex, 1)
+
+    if (hasContainer(content))
+        content.parent = null // this causes deletion of container
+}
+
+function removeContainer(container) {
+    var foundIndex = -1
+    for (var i = 0; i < allContainers.length && foundIndex == -1; ++i) {
+        if (allContainers[i] == container)
+            foundIndex = i
+    }
+
+    if (foundIndex != -1) {
+        var deletedContainer = allContainers[foundIndex]
+        if (deletedContainer.children.length > 0)
+            removeTab(deletedContainer.children[0])
+        allContainers.splice(foundIndex, 1)
+        deletedContainer.destroy()
+    }
+}
+
diff --git a/mardrone/imports/com/nokia/android.1.1/TabGroup.qml b/mardrone/imports/com/nokia/android.1.1/TabGroup.qml
new file mode 100644 (file)
index 0000000..e9ae445
--- /dev/null
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** 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 "TabGroup.js" as Engine
+
+Item {
+    id: root
+    property Item currentTab
+
+    property list<Item> privateContents
+    // Qt defect: cannot have list as default property
+    default property alias privateContentsDefault: root.privateContents
+    property bool platformAnimated: true
+
+    onChildrenChanged: {
+        //  [0] is containerHost
+        if (children.length > 1)
+            Engine.addTab(children[1])
+    }
+
+    onPrivateContentsChanged: {
+        Engine.ensureContainers()
+    }
+
+    Component.onCompleted: {
+        // Set first tabs as current if currentTab is not set by application
+        if (currentTab == null && containerHost.children[0] && containerHost.children[0].children[0])
+            currentTab = containerHost.children[0].children[0]
+        priv.complete = true;
+    }
+
+    Item {
+        id: containerHost
+        objectName: "containerHost"
+        anchors.fill: parent
+    }
+
+    Component {
+        id: tabContainerComponent
+        Item {
+            id: tabContainerItem
+
+            onChildrenChanged: {
+                if (children.length == 0)
+                    Engine.removeContainer(tabContainerItem)
+
+                else if (children.length == 1) {
+                    children[0].width = width
+                    children[0].height = height
+                    // tab content created. set the first tab as current (if not set before, and if
+                    // child is added after TabGroup has completed)
+                    if (priv.complete && root.currentTab == null)
+                        root.currentTab = children[0]
+                }
+            }
+
+            onWidthChanged: {
+                if (children.length > 0)
+                    children[0].width = width
+            }
+
+            onHeightChanged: {
+                if (children.length > 0)
+                    children[0].height = height
+            }
+
+            Component.onDestruction: {
+                if (typeof(root) != "undefined" && !root.currentTab) {
+                    // selected one deleted. try to activate the neighbour
+                    var removedIndex = -1
+                    for (var i = 0; i < containerHost.children.length; i++) {
+                        if (containerHost.children[i] == tabContainerItem) {
+                            removedIndex = i
+                            break
+                        }
+                    }
+                    var newIndex = -1
+                    if (removedIndex != -1) {
+                        if (removedIndex != containerHost.children.length - 1)
+                            newIndex = removedIndex + 1
+                        else if (removedIndex != 0)
+                            newIndex = removedIndex - 1
+                    }
+
+                    if (newIndex != -1)
+                        root.currentTab = containerHost.children[newIndex].children[0]
+                    else
+                        root.currentTab = null
+                }
+            }
+
+            function incomingDone() {
+                state = ""
+                if (priv.incomingPage) {
+                    priv.incomingPage.status = PageStatus.Active
+                    priv.incomingPage = null
+                }
+            }
+
+            function outgoingDone() {
+                if (priv.outgoingPage) {
+                    priv.outgoingPage.status = PageStatus.Inactive
+                    priv.outgoingPage.visible = false
+                    priv.outgoingPage = null
+                }
+                state = "Hidden"
+            }
+
+            width: parent ? parent.width : 0
+            height: parent ? parent.height : 0
+            state: "Hidden"
+
+            states: [
+                State { name: ""; PropertyChanges { target: tabContainerItem; opacity: 1.0 } },
+                State { name: "Incoming"; PropertyChanges { target: tabContainerItem; opacity: 1.0 } },
+                State { name: "Outgoing"; PropertyChanges { target: tabContainerItem; opacity: 0.0 } },
+                State { name: "Hidden"; PropertyChanges { target: tabContainerItem; opacity: 0.0 } }
+            ]
+
+            transitions:  [
+                Transition {
+                    to: "Incoming"
+                    SequentialAnimation {
+                        PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: 250 }
+                        ScriptAction { script: incomingDone() }
+                    }
+                },
+                Transition {
+                    to: "Outgoing"
+                    SequentialAnimation {
+                        PropertyAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: 250 }
+                        ScriptAction { script: outgoingDone() }
+                    }
+                }
+            ]
+        }
+    }
+
+    QtObject {
+        id: priv
+        property bool reparenting: false
+        property bool complete: false
+        property Item currentTabContainer: root.currentTab ? root.currentTab.parent : null
+        property Item incomingPage
+        property Item outgoingPage
+        property bool animate
+
+        function disableAnimations() {
+            animate = false
+
+            // outgoingPage might have been found before the incomingPage changes the orientation
+            if (outgoingPage)
+                outgoingPage.parent.outgoingDone()
+        }
+
+        onCurrentTabContainerChanged: {
+            animate = platformAnimated // updated on orientation change
+            screen.currentOrientationChanged.connect(priv.disableAnimations)
+            for (var i = 0; i < containerHost.children.length; i++) {
+                var tabContainer = containerHost.children[i]
+                var isNewTab = (tabContainer == currentTabContainer)
+                if (isNewTab) {
+                    if (tabContainer.state != "") {
+                        if (tabContainer.children[0].status != undefined) {
+                            incomingPage = tabContainer.children[0]
+                            incomingPage.status = PageStatus.Activating // triggers the orientation change
+                            incomingPage.visible = true
+                            if (incomingPage == outgoingPage)
+                                outgoingPage = null
+                        }
+                        if (animate)
+                            tabContainer.state = "Incoming"
+                        else
+                            tabContainer.incomingDone()
+                    }
+                } else {
+                    if (tabContainer.state != "Hidden") {
+                        if (tabContainer.children.length > 0 && tabContainer.children[0].status != undefined) {
+                            outgoingPage = tabContainer.children[0]
+                            outgoingPage.status = PageStatus.Deactivating
+                            if (incomingPage == outgoingPage)
+                                incomingPage = null
+                        }
+                        if (animate)
+                            tabContainer.state = "Outgoing"
+                        else
+                            tabContainer.outgoingDone()
+                    }
+                }
+            }
+            screen.currentOrientationChanged.disconnect(priv.disableAnimations)
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TextArea.qml b/mardrone/imports/com/nokia/android.1.1/TextArea.qml
new file mode 100644 (file)
index 0000000..cb7ba88
--- /dev/null
@@ -0,0 +1,417 @@
+/****************************************************************************
+**
+** 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
+
+FocusScopeItem {
+    id: root
+
+    // Common public API
+    property alias font: textEdit.font
+    property alias cursorPosition: textEdit.cursorPosition
+    property alias horizontalAlignment: textEdit.horizontalAlignment
+    property alias inputMethodHints: textEdit.inputMethodHints
+    property alias verticalAlignment: textEdit.verticalAlignment
+    property alias readOnly: textEdit.readOnly
+    property alias selectedText: textEdit.selectedText
+    property alias selectionEnd: textEdit.selectionEnd
+    property alias selectionStart: textEdit.selectionStart
+    property alias text: textEdit.text
+    property alias textFormat: textEdit.textFormat
+    property alias wrapMode: textEdit.wrapMode
+    property bool errorHighlight: false
+
+    function copy() {
+        textEdit.copy()
+    }
+
+    function paste() {
+        textEdit.paste()
+    }
+
+    function cut() {
+        textEdit.cut()
+    }
+
+    function select(start, end) {
+        textEdit.select(start, end)
+    }
+
+    function selectAll() {
+        textEdit.selectAll()
+    }
+
+    function selectWord() {
+        textEdit.selectWord()
+    }
+
+    function positionAt(x, y) {
+        var p = mapToItem(textEdit, x, y);
+        return textEdit.positionAt(p.x, p.y)
+    }
+
+    function positionToRectangle(pos) {
+        var rect = textEdit.positionToRectangle(pos)
+        var point = mapFromItem(textEdit, rect.x, rect.y)
+        rect.x = point.x; rect.y = point.y
+        return rect;
+    }
+
+    function openSoftwareInputPanel() {
+        textEdit.openSoftwareInputPanel()
+    }
+
+    function closeSoftwareInputPanel() {
+        textEdit.closeSoftwareInputPanel()
+    }
+
+    // API extensions
+    property alias placeholderText: placeholder.text
+    // TODO: Refactor implicit size when following bugs are resolved
+    // http://bugreports.qt.nokia.com/browse/QTBUG-14957
+    // http://bugreports.qt.nokia.com/browse/QTBUG-16665
+    // http://bugreports.qt.nokia.com/browse/QTBUG-16710 (fixed in Qt 4.7.2)
+    // http://bugreports.qt.nokia.com/browse/QTBUG-12305 (fixed in QtQuick1.1)
+    property real platformMaxImplicitWidth: -1
+    property real platformMaxImplicitHeight: -1
+    property bool platformInverted: false
+    property bool enabled: true // overriding due to QTBUG-15797 and related bugs
+
+    implicitWidth: {
+        var preferredWidth = placeholder.visible ? placeholder.model.paintedWidth
+                                                 : flick.contentWidth
+        preferredWidth = Math.max(preferredWidth, privy.minImplicitWidth)
+        preferredWidth += container.horizontalMargins
+        if (root.platformMaxImplicitWidth >= 0)
+            return Math.min(preferredWidth, root.platformMaxImplicitWidth)
+        return preferredWidth
+    }
+
+    implicitHeight: {
+        var preferredHeight = placeholder.visible ? placeholder.model.paintedHeight
+                                                  : flick.contentHeight
+        preferredHeight += container.verticalMargins
+        // layout spec gives minimum height (textFieldHeight) which includes required padding
+        preferredHeight = Math.max(privateStyle.textFieldHeight, preferredHeight)
+        if (root.platformMaxImplicitHeight >= 0)
+            return Math.min(preferredHeight, root.platformMaxImplicitHeight)
+        return preferredHeight
+    }
+
+    onWidthChanged: {
+        // Detect when a width has been explicitly set. Needed to determine if the TextEdit should
+        // grow horizontally or wrap. I.e. in determining the model's width. There's no way to get
+        // notified of having an explicit width set. Therefore it's polled in widthChanged.
+        privy.widthExplicit = root.widthExplicit()
+    }
+
+    Connections {
+         target: screen
+         onCurrentOrientationChanged: {
+             delayedEnsureVisible.start()
+             fade.start()
+             scroll.start()
+         }
+    }
+
+    QtObject {
+        id: privy
+        // TODO: More consistent minimum width for empty TextArea than 20 * " " on current font?
+        property real minImplicitWidth: privateStyle.textWidth("                    ", textEdit.font)
+        property bool widthExplicit: false
+        property bool wrap: privy.widthExplicit || root.platformMaxImplicitWidth >= 0
+        property real wrapWidth: privy.widthExplicit ? root.width - container.horizontalMargins
+                                                     : root.platformMaxImplicitWidth - container.horizontalMargins
+        function bg_postfix() {
+            if (root.errorHighlight)
+                return "error"
+            else if (root.readOnly || !root.enabled)
+                return "uneditable"
+            else if (textEdit.activeFocus)
+                return "highlighted"
+            else
+                return "editable"
+        }
+    }
+
+    BorderImage {
+        id: background
+        anchors.fill: parent
+        source: privateStyle.imagePath("qtg_fr_textfield_" + privy.bg_postfix(), root.platformInverted)
+        border {
+            left: platformStyle.borderSizeMedium
+            top: platformStyle.borderSizeMedium
+            right: platformStyle.borderSizeMedium
+            bottom: platformStyle.borderSizeMedium
+        }
+        smooth: true
+    }
+
+    Item {
+        id: container
+
+        property real horizontalMargins:   container.anchors.leftMargin
+                                         + container.anchors.rightMargin
+                                         + flick.anchors.leftMargin
+                                         + flick.anchors.rightMargin
+
+        property real verticalMargins:  container.anchors.topMargin
+                                      + container.anchors.bottomMargin
+                                      + flick.anchors.topMargin
+                                      + flick.anchors.bottomMargin
+
+        anchors {
+            fill: parent
+            leftMargin: platformStyle.paddingSmall; rightMargin: platformStyle.paddingSmall
+            topMargin: platformStyle.paddingMedium; bottomMargin: platformStyle.paddingMedium
+        }
+
+        clip: true
+
+        // TODO: Should placeholder also be scrollable?
+        Text {
+            id: placeholder
+            objectName: "placeholder"
+
+            // TODO: See TODO: Refactor implicit size...
+            property variant model: Text {
+                font: textEdit.font
+                text: placeholder.text
+                visible: false
+                wrapMode: textEdit.wrapMode
+                horizontalAlignment: textEdit.horizontalAlignment
+                verticalAlignment: textEdit.verticalAlignment
+                opacity: 0
+
+                Binding {
+                    when: privy.wrap
+                    target: placeholder.model
+                    property: "width"
+                    value: privy.wrapWidth
+                }
+            }
+
+            color: root.platformInverted ? platformStyle.colorNormalMidInverted
+                                         : platformStyle.colorNormalMid
+            font: textEdit.font
+            horizontalAlignment: textEdit.horizontalAlignment
+            verticalAlignment: textEdit.verticalAlignment
+            visible: {
+                if (text && (textEdit.model.paintedWidth == 0 && textEdit.model.paintedHeight <= textEdit.cursorRectangle.height))
+                    return (readOnly || !textEdit.activeFocus)
+                else
+                    return false
+            }
+            wrapMode: textEdit.wrapMode
+            x: flick.x; y: flick.y
+            height: flick.height; width: flick.width
+        }
+
+        Flickable {
+            id: flick
+
+            property real tiny: Math.round(platformStyle.borderSizeMedium / 2)
+
+            function ensureVisible(rect) {
+                if (Math.round(contentX) > Math.round(rect.x))
+                    contentX = rect.x
+                else if (Math.round(contentX + width) < Math.round(rect.x + rect.width))
+                    contentX = rect.x + rect.width - width
+
+                if (Math.round(contentY) > Math.round(rect.y))
+                    contentY = rect.y
+                else if (Math.round(contentY + height) < Math.round(rect.y + rect.height))
+                     contentY = rect.y + rect.height - height
+            }
+
+            anchors {
+                fill: parent
+                leftMargin: tiny
+                rightMargin: tiny
+                topMargin: tiny / 2
+                bottomMargin: tiny / 2
+            }
+
+            boundsBehavior: Flickable.StopAtBounds
+            contentHeight: textEdit.model.paintedHeight
+            contentWidth: textEdit.model.paintedWidth +
+                         (textEdit.wrapMode == TextEdit.NoWrap ? textEdit.cursorRectangle.width : 0)
+            interactive: root.enabled
+
+            onHeightChanged: {
+                if(textEdit.cursorVisible || textEdit.cursorPosition == textEdit.selectionEnd)
+                    ensureVisible(textEdit.cursorRectangle)
+            }
+
+            onWidthChanged: {
+                if(textEdit.cursorVisible || textEdit.cursorPosition == textEdit.selectionEnd)
+                    ensureVisible(textEdit.cursorRectangle)
+            }
+
+            TextEdit {
+                id: textEdit
+                objectName: "textEdit"
+
+                // TODO: See TODO: Refactor implicit size...
+                property variant model: TextEdit {
+                    font: textEdit.font
+                    text: textEdit.text
+                    horizontalAlignment: textEdit.horizontalAlignment
+                    verticalAlignment: textEdit.verticalAlignment
+                    wrapMode: textEdit.wrapMode
+                    visible: false
+                    opacity: 0
+
+                    // In Wrap mode, if the width is bound the text will always get wrapped at the
+                    // set width. If the width is not bound the text won't get wrapped but
+                    // paintedWidth will increase.
+                    Binding {
+                        when: privy.wrap
+                        target: textEdit.model
+                        property: "width"
+                        value: privy.wrapWidth
+                    }
+                }
+                activeFocusOnPress: false
+                enabled: root.enabled
+                focus: true
+                font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
+                color: root.platformInverted ? platformStyle.colorNormalLightInverted
+                                             : platformStyle.colorNormalDark
+                cursorVisible: activeFocus && !selectedText
+                selectedTextColor: root.platformInverted ? platformStyle.colorNormalDarkInverted
+                                                         : platformStyle.colorNormalLight
+                selectionColor: root.platformInverted ? platformStyle.colorTextSelectionInverted
+                                                      : platformStyle.colorTextSelection
+                textFormat: TextEdit.AutoText
+                wrapMode: TextEdit.Wrap
+                height: flick.height
+                width: flick.width
+                // TODO: Make bug report?
+                // Called too early (before TextEdit size is adjusted) delay ensureVisible call a bit
+                onCursorRectangleChanged: delayedEnsureVisible.start()
+                onActiveFocusChanged: {
+                    if (activeFocus) {
+                        horizontal.flash()
+                        vertical.flash()
+                    }
+                }
+                onEnabledChanged: {
+                    if (!enabled) {
+                        deselect()
+                        // De-focusing requires setting focus elsewhere, in this case editor's parent
+                        if (root.parent)
+                            root.parent.forceActiveFocus()
+                    }
+                }
+
+                Keys.forwardTo: touchController
+
+                TextTouchController {
+                    id: touchController
+
+                    //  selection handles require touch area geometry to differ from TextEdit's geometry
+                    anchors {
+                        top: editor.top; topMargin: -container.verticalMargins / 2
+                        left: editor.left; leftMargin: -container.horizontalMargins / 2
+                    }
+                    height: Math.max(root.height, flick.contentHeight + container.verticalMargins * 2)
+                    width: Math.max(root.width, flick.contentWidth + container.horizontalMargins * 2)
+                    editorScrolledX: flick.contentX - (container.horizontalMargins / 2)
+                    editorScrolledY: flick.contentY - (container.verticalMargins / 2)
+                    copyEnabled: textEdit.selectedText
+                    cutEnabled: !textEdit.readOnly && textEdit.selectedText
+                    platformInverted: root.platformInverted
+                    Component.onCompleted: flick.movementEnded.connect(touchController.flickEnded)
+                    Connections { target: screen; onCurrentOrientationChanged: touchController.updateGeometry() }
+                    Connections {
+                        target: textEdit
+                        onHeightChanged: touchController.updateGeometry()
+                        onWidthChanged: touchController.updateGeometry()
+                        onHorizontalAlignmentChanged: touchController.updateGeometry()
+                        onVerticalAlignmentChanged: touchController.updateGeometry()
+                        onWrapModeChanged: touchController.updateGeometry()
+                        onFontChanged: touchController.updateGeometry()
+                    }
+                }
+            }
+
+            PropertyAnimation { id: fade; target: textEdit; property: "opacity"; from: 0; to: 1; duration: 250 }
+            PropertyAnimation { id: scroll; target: flick; property: "contentY"; duration: 250 }
+        }
+
+        ScrollBar {
+            id: vertical
+            anchors {
+                top: flick.top;
+                topMargin: -flick.tiny / 2;
+                left: flick.right;
+            }
+            flickableItem: flick
+            interactive: false
+            orientation: Qt.Vertical
+            height: container.height
+            platformInverted: root.platformInverted
+        }
+
+        ScrollBar {
+            id: horizontal
+            anchors {
+                left: flick.left;
+                leftMargin: -flick.tiny;
+                bottom: container.bottom;
+                bottomMargin: -flick.tiny / 2
+                rightMargin: vertical.opacity ? vertical.width : 0
+            }
+            flickableItem: flick
+            interactive: false
+            orientation: Qt.Horizontal
+            width: container.width
+            platformInverted: root.platformInverted
+        }
+
+        Timer {
+            id: delayedEnsureVisible
+            interval: 1
+            onTriggered: flick.ensureVisible(textEdit.cursorRectangle)
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TextContextMenu.qml b/mardrone/imports/com/nokia/android.1.1/TextContextMenu.qml
new file mode 100644 (file)
index 0000000..9a4e500
--- /dev/null
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** 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 "AppManager.js" as AppManager
+
+Item {
+    id: root
+
+    property Item editor: null
+    property bool copyEnabled: false
+    property bool cutEnabled: false
+    property bool platformInverted: false
+
+    function show() {
+        // Show menu only if some of the options is available
+        if(root.copyEnabled || root.cutEnabled || editor.canPaste) {
+            parent = AppManager.rootObject();
+            root.visible = true;
+            calculatePosition();
+        }
+    }
+
+    function hide() {
+        root.visible = false;
+    }
+
+    function calculatePosition() {
+        if (editor && root.visible) {
+            var rectStart = editor.positionToRectangle(editor.selectionStart);
+            var rectEnd = editor.positionToRectangle(editor.selectionEnd);
+
+            var posStart = editor.mapToItem(parent, rectStart.x, rectStart.y);
+            var posEnd = editor.mapToItem(parent, rectEnd.x, rectEnd.y);
+
+            var selectionCenterX = (posEnd.x + posStart.x) / 2;
+            if (posStart.y != posEnd.y)
+                // we have multiline selection so center to the screen
+                selectionCenterX = parent.width / 2;
+
+            var contextMenuMargin = 10; // the space between the context menu and the line above/below
+            var contextMenuAdjustedRowHeight = row.height + contextMenuMargin;
+            var tempY = posStart.y - contextMenuAdjustedRowHeight;
+            var minVerticalPos = internal.textArea ? internal.textArea.mapToItem(parent, 0, 0).y - row.height : 0
+                       
+            if (tempY < minVerticalPos)
+                tempY = minVerticalPos
+                               
+            if (tempY < 0)
+                tempY = 0
+
+            root.x = Math.max(0, Math.min(selectionCenterX - row.width / 2, parent.width - row.width));
+            root.y = tempY;
+        }
+    }
+
+    onVisibleChanged: {
+        if (visible) {
+            internal.editorSceneXChanged.connect(internal.editorMoved)
+            internal.editorSceneYChanged.connect(internal.editorMoved)
+        } else {
+            internal.editorSceneXChanged.disconnect(internal.editorMoved)
+            internal.editorSceneYChanged.disconnect(internal.editorMoved)
+        }
+    }
+
+    x: 0; y: 0
+    visible: false
+
+    Binding { target: internal; property: "editorSceneX"; value: AppManager.sceneX(root.editor); when: root.visible && (root.editor != null) }
+    Binding { target: internal; property: "editorSceneY"; value: AppManager.sceneY(root.editor); when: root.visible && (root.editor != null) }
+
+    QtObject {
+        id: internal
+
+        property real editorSceneX
+        property real editorSceneY
+        property Item textArea: null
+
+        function editorMoved() {
+            root.calculatePosition()
+        }
+               
+        Component.onCompleted: textArea = AppManager.findParent(editor, "errorHighlight")
+    }
+
+    ButtonRow {
+        id: row
+
+        function visibleButtonCount() {
+            var count = 0
+            if (copyButton.visible) ++count
+            if (cutButton.visible) ++count
+            if (pasteButton.visible) ++count
+            return count
+        }
+
+        exclusive: false
+        width: Math.round(privateStyle.buttonSize * 1.5) * visibleButtonCount()
+
+        onWidthChanged: calculatePosition()
+        onHeightChanged: calculatePosition()
+
+        Button {
+            id: copyButton
+            iconSource: privateStyle.imagePath("qtg_toolbar_copy", root.platformInverted)
+            visible: root.copyEnabled
+            platformInverted: root.platformInverted
+            onClicked: editor.copy()
+        }
+        Button {
+            id: cutButton
+            iconSource: privateStyle.imagePath("qtg_toolbar_cut", root.platformInverted)
+            visible: root.cutEnabled
+            platformInverted: root.platformInverted
+            onClicked: {
+                editor.cut()
+                root.visible = false
+            }
+        }
+        Button {
+            id: pasteButton
+            iconSource: privateStyle.imagePath("qtg_toolbar_paste", root.platformInverted)
+            visible: editor.canPaste
+            platformInverted: root.platformInverted
+            onClicked: {
+                editor.paste()
+                root.visible = false
+            }
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TextField.qml b/mardrone/imports/com/nokia/android.1.1/TextField.qml
new file mode 100644 (file)
index 0000000..4b96760
--- /dev/null
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** 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
+
+FocusScope {
+    id: root
+
+    // Common Public API
+    property alias placeholderText: placeholder.text
+    property alias inputMethodHints: textInput.inputMethodHints
+    property alias font: textInput.font
+    property alias cursorPosition: textInput.cursorPosition
+    property alias readOnly: textInput.readOnly
+    property alias echoMode: textInput.echoMode
+    property alias acceptableInput: textInput.acceptableInput
+    property alias inputMask: textInput.inputMask
+    property alias validator: textInput.validator
+    property alias selectedText: textInput.selectedText
+    property alias selectionEnd: textInput.selectionEnd
+    property alias selectionStart: textInput.selectionStart
+    property alias text: textInput.text
+    property bool errorHighlight: !acceptableInput
+    property alias maximumLength: textInput.maximumLength
+
+    function copy() {
+        textInput.copy()
+    }
+
+    function paste() {
+        textInput.paste()
+    }
+
+    function cut() {
+        textInput.cut()
+    }
+
+    function select(start, end) {
+        textInput.select(start, end)
+    }
+
+    function selectAll() {
+        textInput.selectAll()
+    }
+
+    function selectWord() {
+        textInput.selectWord()
+    }
+
+    function positionAt(x) {
+        return textInput.positionAt(mapToItem(textInput, x, 0).x)
+    }
+
+    function positionToRectangle(pos) {
+        var rect = textInput.positionToRectangle(pos)
+        rect.x = mapFromItem(textInput, rect.x, 0).x
+        return rect;
+    }
+
+    function openSoftwareInputPanel() {
+        textInput.openSoftwareInputPanel()
+    }
+
+    function closeSoftwareInputPanel() {
+        textInput.closeSoftwareInputPanel()
+    }
+
+    // API extensions
+    implicitWidth: platformLeftMargin + platformRightMargin + flick.tiny * 2 +
+                   Math.max(privateStyle.textWidth(text, textInput.font), priv.minWidth)
+    implicitHeight: Math.max(privateStyle.textFieldHeight,
+                             2 * platformStyle.paddingMedium + privateStyle.fontHeight(textInput.font))
+
+    property bool enabled: true // overriding due to QTBUG-15797 and related bugs
+
+    property real platformLeftMargin: platformStyle.paddingSmall
+    property real platformRightMargin: platformStyle.paddingSmall
+    property bool platformInverted: false
+
+    // Private data
+    QtObject {
+        id: priv
+        // Minimum width is either placeholder text lenght or 5 spaces on current font.
+        // Using placeholder text lenght as minimum width prevents implicit sized item from
+        // shrinking on focus gain.
+        property real minWidth: placeholder.text ? privateStyle.textWidth(placeholder.text, textInput.font)
+                                                 : privateStyle.textWidth("     ", textInput.font)
+
+        function bg_postfix() {
+            if (root.errorHighlight)
+                return "error"
+            else if (root.readOnly || !root.enabled)
+                return "uneditable"
+            else if (textInput.activeFocus)
+                return "highlighted"
+            else
+                return "editable"
+        }
+    }
+
+    BorderImage {
+        id: frame
+        anchors.fill: parent
+        source: privateStyle.imagePath("qtg_fr_textfield_" + priv.bg_postfix(), root.platformInverted)
+        border {
+            left: platformStyle.borderSizeMedium
+            top: platformStyle.borderSizeMedium
+            right: platformStyle.borderSizeMedium
+            bottom: platformStyle.borderSizeMedium
+        }
+        smooth: true
+    }
+
+    Item {
+        id: container
+
+        anchors {
+            left: root.left; leftMargin: root.platformLeftMargin
+            right: root.right; rightMargin: root.platformRightMargin
+            verticalCenter : root.verticalCenter
+        }
+        clip: true
+        height: root.height
+
+        Flickable {
+            id: flick
+
+            property real tiny: Math.round(platformStyle.borderSizeMedium / 2)
+
+            function ensureVisible(rect) {
+                if (rect.x >= 0 && Math.round(contentX) > Math.round(rect.x))
+                    contentX = rect.x
+                else if (Math.round(contentX + width) < Math.round(rect.x + rect.width))
+                  contentX = rect.x + rect.width - width
+            }
+
+            anchors {
+                left: parent.left; leftMargin: tiny
+                right: parent.right; rightMargin: tiny
+                verticalCenter : parent.verticalCenter
+            }
+
+            boundsBehavior: Flickable.StopAtBounds
+            contentWidth: textInput.paintedWidth
+            height: textInput.paintedHeight
+
+            onWidthChanged: ensureVisible(textInput.cursorRectangle)
+
+            TextInput {
+                id: textInput; objectName: "textInput"
+
+                property real paintedWidth: textInput.model.paintedWidth
+                property real paintedHeight: textInput.model.paintedHeight
+
+                // TODO: See TODO: Refactor implicit size...
+                property variant model: Text {
+                    font: textInput.font
+                    text: textInput.text
+                    horizontalAlignment: textInput.horizontalAlignment
+                    visible: false
+                    opacity: 0
+                }
+
+                activeFocusOnPress: false
+                cursorVisible: activeFocus && !selectedText
+                enabled: root.enabled
+                color: root.platformInverted ? platformStyle.colorNormalLightInverted
+                                             : platformStyle.colorNormalDark
+                focus: true
+                font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
+                selectedTextColor: root.platformInverted ? platformStyle.colorNormalDarkInverted
+                                                         : platformStyle.colorNormalLight
+                selectionColor: root.platformInverted ? platformStyle.colorTextSelectionInverted
+                                                      : platformStyle.colorTextSelection
+                width: Math.max(flick.width, paintedWidth)
+
+                onEnabledChanged: {
+                    if (!enabled) {
+                        deselect()
+                        // De-focusing requires setting focus elsewhere, in this case editor's parent
+                        if (root.parent)
+                            root.parent.forceActiveFocus()
+                    }
+                }
+
+                onCursorPositionChanged: flick.ensureVisible(textInput.cursorRectangle)
+
+                Keys.forwardTo: touchController
+
+                TextTouchController {
+                    id: touchController
+
+                    //  selection handles require touch area geometry to differ from TextInput's geometry
+                    anchors {
+                        left: parent.left;
+                        leftMargin: -flick.tiny
+                        verticalCenter : parent.verticalCenter
+                    }
+                    height: root.height
+                    width: Math.max(root.width, flick.contentWidth + flick.tiny * 2)
+                    editorScrolledX: flick.contentX - container.anchors.leftMargin - flick.tiny
+                    editorScrolledY: 0
+                    copyEnabled: textInput.selectedText
+                    cutEnabled: !textInput.readOnly && textInput.selectedText
+                    platformInverted: root.platformInverted
+                    Component.onCompleted: flick.movementEnded.connect(touchController.flickEnded)
+                    Connections { target: screen; onCurrentOrientationChanged: touchController.updateGeometry() }
+                    Connections {
+                        target: textInput
+                        onHeightChanged: touchController.updateGeometry()
+                        onWidthChanged: touchController.updateGeometry()
+                        onHorizontalAlignmentChanged: touchController.updateGeometry()
+                        onFontChanged: touchController.updateGeometry()
+                    }
+                }
+            }
+        }
+
+        Text {
+            id: placeholder; objectName: "placeholder"
+            anchors {
+                left: parent.left; leftMargin: flick.tiny
+                right: parent.right; rightMargin: flick.tiny
+                verticalCenter : parent.verticalCenter
+            }
+            color: root.platformInverted ? platformStyle.colorNormalMidInverted
+                                         : platformStyle.colorNormalMid
+            font: textInput.font
+            visible: (!textInput.activeFocus || readOnly) && !textInput.text && text
+        }
+
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TextMagnifier.qml b/mardrone/imports/com/nokia/android.1.1/TextMagnifier.qml
new file mode 100644 (file)
index 0000000..5781b6e
--- /dev/null
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 "AppManager.js" as AppManager
+
+Magnifier {
+    id: root
+
+    property Item editor: parent.editor
+    // Defines the center of the content to be magnified in X axis
+    // Note: the center of the content to be magnified in Y axis is determined by the cursor position.
+    property variant contentCenter: Qt.point(0, 0) //editor's coordinate system
+    property bool platformInverted: false
+
+    function show() {
+        parent = AppManager.rootObject();
+        sourceRect = internal.calculateSourceGeometry();
+        internal.calculatePosition();
+        root.visible = true;
+    }
+
+    function hide() {
+        root.visible = false;
+    }
+
+    sourceRect: Qt.rect(0, 0, 0, 0)
+    visible: false
+    scaleFactor: 1.5
+    maskFileName: root.platformInverted ? ":/graphics_1_1/qtg_graf_magnifier_mask_inverse.svg"
+                                        : ":/graphics_1_1/qtg_graf_magnifier_mask.svg"
+    overlayFileName: root.platformInverted ? ":/graphics_1_1/qtg_graf_magnifier_inverse.svg"
+                                           : ":/graphics_1_1/qtg_graf_magnifier.svg"
+
+    onContentCenterChanged: internal.updateSourceRect()
+
+    onSourceRectChanged: if (visible) internal.calculatePosition()
+
+    Connections {
+        target: editor
+        onCursorPositionChanged: internal.updateSourceRect
+    }
+
+    // Private
+    QtObject {
+        id: internal
+
+        function updateSourceRect () {
+            if (visible)
+                sourceRect = internal.calculateSourceGeometry();
+        }
+
+        function calculatePosition() {
+            width = sourceRect.width * scaleFactor;
+            height = sourceRect.height * scaleFactor;
+
+            var magnifierMargin = -platformStyle.paddingLarge; // the offset between the magnifier and top of the line
+            var pos = parent.mapFromItem(editor,
+                                         contentCenter.x - width/2,
+                                         contentCenter.y - sourceRect.height/2 - height - magnifierMargin);
+
+            root.x = pos.x;
+            root.y = pos.y;
+        }
+
+        // Calculates and returns the source geometry of the content to be magnified in scene coordinates
+        function calculateSourceGeometry() {
+            var magniferSize = Qt.size(platformStyle.graphicSizeMedium * 2, platformStyle.graphicSizeMedium * 2);
+            var sourceSize = Qt.size(magniferSize.width / scaleFactor, magniferSize.height / scaleFactor);
+            var contentCenterScene = editor.mapToItem(null, contentCenter.x, contentCenter.y);
+
+            var rect = Qt.rect(contentCenterScene.x - sourceSize.width / 2,
+                               contentCenterScene.y - sourceSize.height / 2,
+                               sourceSize.width, sourceSize.height);
+            return rect;
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/TextSelectionHandle.qml b/mardrone/imports/com/nokia/android.1.1/TextSelectionHandle.qml
new file mode 100644 (file)
index 0000000..dd53941
--- /dev/null
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** 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 "RectUtils.js" as Utils
+
+Item {
+    id: root
+
+    property alias imageSource: image.source
+    property int editorPos: 0 //character position of the handle in the editor document
+    property Item editor
+    // the default size of the touch area should come from some style constant
+    property variant touchAreaSize: Qt.size(platformStyle.graphicSizeMedium, platformStyle.graphicSizeMedium)
+    property alias showImage: image.visible
+    property variant viewPortRect: Qt.rect(0, 0, 0, 0) // item's coordinates
+    //parent's coordinate system
+    property variant center: Qt.point(root.x + root.width / 2, root.y + root.height / 2)
+
+    // Point is in Item's coordinate system
+    function containsPoint(point) {
+        var touchAreaRect = Qt.rect(touchArea.x, touchArea.y, touchArea.width, touchArea.height);
+
+        if (!Utils.rectContainsRect(viewPortRect, touchAreaRect)
+            && Utils.rectIntersectsRect(viewPortRect, touchAreaRect)) {
+                touchAreaRect.x = Math.max(touchAreaRect.x, viewPortRect.x);
+                touchAreaRect.x = Math.min(touchAreaRect.x, viewPortRect.x + viewPortRect.width - touchAreaRect.width);
+                touchAreaRect.y = Math.max(touchAreaRect.y, viewPortRect.y);
+                touchAreaRect.y = Math.min(touchAreaRect.y, viewPortRect.y + viewPortRect.height - touchAreaRect.height);
+        }
+
+        effectiveTouchArea.x = touchAreaRect.x;
+        effectiveTouchArea.y = touchAreaRect.y;
+
+        if (Utils.rectContainsPoint(touchAreaRect, point.x, point.y))
+            return true;
+
+        return false;
+    }
+
+    // Point is in parent's coordinate system
+    function pointDistanceFromCenter(point) {
+        return Utils.manhattan(point, root.center);
+    }
+
+    function updateGeometry() {
+        var rect = root.editor.positionToRectangle(editorPos);
+        var pos = root.editor.mapToItem(root.parent, rect.x, rect.y);
+        root.x = pos.x;
+        root.y = pos.y;
+        root.width = rect.width;
+        root.height = rect.height;
+    }
+
+    onEditorPosChanged: root.updateGeometry()
+
+    Item {
+        id: touchArea
+
+        visible: false
+        anchors.centerIn: root
+        width: root.touchAreaSize.width
+        height: Math.max(root.touchAreaSize.height, root.height)
+    }
+
+    Item {
+        id: effectiveTouchArea
+
+        visible: false
+        width: root.touchAreaSize.width
+        height: Math.max(root.touchAreaSize.height, root.height)
+    }
+
+    BorderImage {
+        id: image
+
+        property real tiny: Math.round(platformStyle.borderSizeMedium / 2)
+
+        anchors {
+            left: root.objectName == "SelectionEnd" ? root.right : undefined
+            right: root.objectName == "SelectionBegin" ? root.left : undefined
+            leftMargin: root.objectName == "SelectionEnd" ? -editor.cursorRectangle.width : 0
+            top: root.top
+            topMargin: -tiny / 2 + 1
+            bottom: root.bottom
+            bottomMargin: -tiny / 2
+        }
+
+        border {
+            left: 0
+            top: tiny
+            right: 0
+            bottom: tiny
+        }
+    }
+}
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()
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ToolBar.qml b/mardrone/imports/com/nokia/android.1.1/ToolBar.qml
new file mode 100644 (file)
index 0000000..5de815c
--- /dev/null
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    property Item tools: null
+
+    // The transition type. One of the following:
+    //      set         an instantaneous change (default)
+    //      push        follows page stack push animation
+    //      pop         follows page stack pop animation
+    //      replace     follows page stack replace animation
+    property string transition: "set"
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    // Sets the tools with a transition.
+    function setTools(tools, transition) {
+        stateGroup.state = tools ? "" : "Hidden"
+        if (tools)
+            tools.height = root.height
+        priv.transition = transition
+        root.tools = tools
+    }
+
+    LayoutMirroring.enabled: false
+    LayoutMirroring.childrenInherit: true
+
+    implicitWidth: Math.max(50, screen.width)
+    implicitHeight: (screen.width < screen.height)
+        ? privateStyle.toolBarHeightPortrait
+        : privateStyle.toolBarHeightLandscape
+
+    BorderImage {
+        id: background
+        anchors.fill: parent
+        source: privateStyle.imagePath("qtg_fr_toolbar", root.platformInverted)
+        border { left: 20; top: 20; right: 20; bottom: 20 }
+    }
+
+    //Prevents mouse events from propagating to elements below the ToolBar
+    MouseArea {
+        anchors.fill: parent
+    }
+
+    Item {
+        id: container
+        anchors.fill: parent
+    }
+
+    onToolsChanged: {
+        priv.performTransition(priv.transition || transition)
+        priv.transition = undefined
+    }
+
+    Component {
+        id: containerComponent
+
+        Item {
+            id: item
+
+            anchors.fill: parent
+
+            // The states correspond to the different possible positions of the item.
+            state: "Hidden"
+
+            // The tools held by this item.
+            property Item tools: null
+            // The owner of the tools.
+            property Item owner: null
+
+            states: [
+
+                // Active state
+                State {
+                    name: ""
+                    PropertyChanges { target: item; visible: true; opacity: 1 }
+                },
+
+                // Start state for pop entry, end state for push exit.
+                State {
+                    name: "Prev"
+                    PropertyChanges { target: item; opacity: 0.0 }
+                },
+                // Start state for push entry, end state for pop exit.
+                State {
+                    name: "Next"
+                    PropertyChanges { target: item; opacity: 0.0 }
+                },
+                // Start state for replace entry.
+                State {
+                    name: "Front"
+                    PropertyChanges { target: item; opacity: 0.0 }
+                },
+                // End state for replace exit.
+                State {
+                    name: "Back"
+                    PropertyChanges { target: item; opacity: 0.0 }
+                },
+                // Inactive state.
+                State {
+                    name: "Hidden"
+                    PropertyChanges { target: item; visible: false }
+                    StateChangeScript {
+                        script: {
+                            if (item.tools) {
+                                // re-parent back to original owner
+                                tools.visible = false
+                                tools.parent = owner
+
+                                // reset item
+                                item.tools = item.owner = null
+                            }
+                        }
+                    }
+                }
+            ]
+
+            transitions: [
+                // Pop entry and push exit transition.
+                Transition {
+                    from: "";  to: "Prev";  reversible: true
+                    SequentialAnimation {
+                        PropertyAnimation { properties: "opacity";  easing.type: Easing.InCubic;  duration: priv.transitionDuration / 2 }
+                        PauseAnimation { duration: priv.transitionDuration / 2 }
+                        ScriptAction { script: if (state == "Prev") state = "Hidden" }
+                    }
+                },
+                // Push entry and pop exit transition.
+                Transition {
+                    from: "";  to: "Next";  reversible: true
+                    SequentialAnimation {
+                        PropertyAnimation { properties: "opacity";  easing.type: Easing.InCubic;  duration: priv.transitionDuration / 2 }
+                        PauseAnimation { duration: priv.transitionDuration / 2 }
+                        ScriptAction { script: if (state == "Next") state = "Hidden" }
+                    }
+                },
+                Transition {
+                    // Replace entry transition.
+                    from: "Front";  to: ""
+                    SequentialAnimation {
+                        PropertyAnimation { properties: "opacity";  easing.type: Easing.InOutExpo;  duration: priv.transitionDuration }
+                    }
+                },
+                Transition {
+                    // Replace exit transition.
+                    from: "";  to: "Back"
+                    SequentialAnimation {
+                        PropertyAnimation { properties: "opacity";  easing.type: Easing.InOutExpo;  duration: priv.transitionDuration }
+                        ScriptAction { script: if (state == "Back") state = "Hidden"  }
+                    }
+                }
+            ]
+        }
+    }
+
+    QtObject {
+        id: priv
+
+        property Item currentComponent: null
+
+        // Alternating components used for transitions.
+        property Item compA: null
+        property Item compB: null
+
+        // The transition to perform next.
+        property variant transition
+
+        // Duration of transition animation (in ms)
+        property int transitionDuration: 500
+
+        // Performs a transition between tools in the toolbar.
+        function performTransition(transition) {
+            // lazily create components if they have not been created
+            if (!priv.currentComponent) {
+                priv.compA = containerComponent.createObject(container)
+                priv.compB = containerComponent.createObject(container)
+                priv.currentComponent = priv.compB
+            }
+
+            // no transition if the tools are unchanged
+            if (priv.currentComponent.tools == tools)
+                return
+
+            // select component states based on the transition animation
+            var transitions = {
+                "set":      { "new": "",        "old": "Hidden" },
+                "push":     { "new": "Next",    "old": "Prev" },
+                "pop":      { "new": "Prev",    "old": "Next" },
+                "replace":  { "new": "Front",   "old": "Back" }
+            }
+
+            var animation = transitions[transition]
+            // initialize the free component
+            var component = priv.currentComponent == priv.compA ? priv.compB : priv.compA
+            component.state = "Hidden"
+            if (tools) {
+                component.tools = tools
+                component.owner = tools.parent
+                tools.parent = component
+                tools.visible = true
+                if (tools.layoutChildren != undefined && typeof tools.layoutChildren == 'function' )
+                    tools.layoutChildren()
+            }
+
+            // perform transition
+            priv.currentComponent.state = animation["old"]
+            if (tools) {
+                component.state = animation["new"]
+                component.state = ""
+            }
+            priv.currentComponent = component
+        }
+    }
+
+    StateGroup {
+        id: stateGroup
+        states: [
+            // Inactive state.
+            State {
+                name: "Hidden"
+                PropertyChanges { target: root;  height: 0;  opacity: 0.0 }
+            }
+        ]
+
+        transitions: [
+            // Transition between active and inactive states.
+            Transition {
+                from: "";  to: "Hidden";  reversible: true
+                SequentialAnimation {
+                    PropertyAnimation { properties: "opacity";  easing.type: Easing.InOutExpo;  duration: priv.transitionDuration }
+                    PropertyAnimation { properties: "height";  duration: 10 }
+                }
+            }
+        ]
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ToolBarLayout.qml b/mardrone/imports/com/nokia/android.1.1/ToolBarLayout.qml
new file mode 100644 (file)
index 0000000..49dfa0d
--- /dev/null
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+// ToolBarLayout is a container for items on a toolbar that automatically
+// implements an appropriate layout for its children.
+
+import QtQuick 1.1
+import "." 1.1
+
+Item {
+    id: root
+
+    property bool backButton: true // deprecated
+    onBackButtonChanged: { console.log("warning: ToolBarLayout.backButton is deprecated.") }
+
+    implicitWidth: screen.width
+    implicitHeight: internal.defaultHeightToolBar
+
+    visible: false
+
+    Connections {
+        target: privateStyle
+        onLayoutParametersChanged: internal.layoutChildren()
+    }
+    Connections {
+        target: screen
+        onCurrentOrientationChanged: internal.layoutChildren()
+    }
+
+    QtObject {
+        id: internal
+        objectName: "internal"
+
+        property bool portrait: screen.width < screen.height
+
+        // These are the dynamic layout parameters used by the toolbar layout.
+        property real defaultHeightToolBar: portrait ?
+            privateStyle.toolBarHeightPortrait : privateStyle.toolBarHeightLandscape
+        property real defaultHeightToolButton: privateStyle.toolBarHeightLandscape
+        property real outerMarginHorizontal: portrait ?
+            0 : (2 * platformStyle.paddingLarge)
+        property real outerMarginButtonRowLong: portrait ?
+            platformStyle.paddingLarge : (3 * platformStyle.paddingLarge)
+        property real innerSpacingTextButtonSingle: portrait ?
+            platformStyle.paddingMedium + (3 * platformStyle.paddingLarge) : (3 * platformStyle.paddingLarge)
+        property real innerSpacingTextButtonDouble: portrait ?
+            platformStyle.paddingSmall : (3 * platformStyle.paddingLarge)
+        property real innerSpacingButtonRowTwoChildren: portrait ?
+            platformStyle.paddingMedium : (3 * platformStyle.paddingLarge)
+        property real innerSpacingButtonRowLong: portrait ?
+            platformStyle.paddingMedium : platformStyle.paddingLarge
+        property real centerSpacingTextButtonDouble: platformStyle.paddingLarge
+
+        function isIconButton(item) {
+            return item.hasOwnProperty("iconSource")
+                && item.hasOwnProperty("text")
+                && item.text == ""
+        }
+
+        function isTextToolButton(item) {
+            // ToolButton has both iconSource and flat property,
+            // Button only has iconSource
+            return (item.hasOwnProperty("iconSource")
+                    && item.iconSource == ""
+                    && item.hasOwnProperty("flat"))
+        }
+
+        function isButtonRow(item) {
+            return item.hasOwnProperty("checkedButton")
+        }
+
+        function buttonWidth(child) {
+            if ((isTextToolButton(child)) || !(child.hasOwnProperty("implicitWidth"))) {
+                // ImplicitWidth for the ToolButton returns wrong value right after
+                // orientation change, and also we want to override its own
+                // layout width calculation, so use the actual width
+                return child.width
+            }
+            return child.implicitWidth
+        }
+
+        function centerOffset(outerLength, innerLength) {
+            // calculate the offset of the leading edge of a centered child item
+            return Math.floor((outerLength - innerLength) / 2.0)
+        }
+
+        function widthTextButtonSingle(leftMargin, innerSpacing) {
+            // calculate the remaining width for a centered item
+            var outerContents = leftMargin + innerSpacing
+            return root.width - (outerContents * 2)
+        }
+
+        function widthTextButtonDouble(leftMargin, innerSpacing, centerSpacing) {
+            // calculate the space available when there are two items with a center
+            // margin, and share it between the two
+            var outerContents = leftMargin + innerSpacing
+            return Math.round((root.width - (outerContents * 2) - centerSpacing) / 2.0)
+        }
+
+        function widthButtonRowLong(leftButton, rightButton, itemMargin, innerSpacing, outerMargin) {
+            // calculate the width of a long button row, which is used in the case that there are more
+            // than three icons. If either left or right button is present, allocate the itemMargin to
+            // ensure that there is sufficient space; otherwise we can use the special
+            // outerMargin value
+            var leftContents = leftButton ? itemMargin + innerSpacing : outerMargin
+            var rightContents = rightButton ? itemMargin + innerSpacing : outerMargin
+            return root.width - leftContents - rightContents
+        }
+
+        function layoutChildren() {
+            var numChildren = children.length
+            if (parent == null || root.width == 0 || numChildren == 0)
+                return
+
+            for (var i = 0; i < numChildren; ++i) {
+                // make sure all the children have correct parent, height, and y
+                children[i].parent = root
+                if (isButtonRow(children[i])) {
+                    var buttonRow = children[i]
+                    // ButtonRow frame height is always tool bar's height in
+                    // landscape, regardless of the current orientation, so we need
+                    // to override the heights of the buttons within (because it's a
+                    // Row, so its height is based on its children)
+                    for (var j = 0; j < buttonRow.children.length; ++j) {
+                        buttonRow.children[j].implicitHeight = defaultHeightToolButton
+                    }
+                }
+                // child's vertical center always goes to middle of the toolbar
+                var childHeight = children[i].hasOwnProperty("implicitHeight") ?
+                        children[i].implicitHeight : children[i].height
+                children[i].y = root.y + centerOffset(root.implicitHeight, childHeight)
+            }
+
+            // detect whether we have left and or right items. we need to lay out
+            // the remaining children (that are not left or right items) whether they
+            // are tool buttons, text buttons or a button row
+            var leftItem = isIconButton(children[0]) ?
+                    children[0] : undefined
+            var rightItem = isIconButton(children[numChildren-1]) ?
+                    children[numChildren-1] : undefined
+            var childrenRemaining = numChildren - (leftItem != undefined ? 1 : 0) - (rightItem != undefined ? 1 : 0)
+            var firstRemainingIndex = leftItem != undefined ? 1 : 0
+            var lastRemainingIndex = rightItem != undefined ? (numChildren - 2) : (numChildren - 1)
+
+            // precalculate the margins for the left and right items, we will work
+            // out child sizes assuming they are present
+            var leftMargin = outerMarginHorizontal + defaultHeightToolBar
+            var rightMargin = root.width - leftMargin
+
+            // In the case of a lone remaining chlld, or in the case of 2 text
+            // buttons, we need to override the width
+            var overrideChildWidth = 0
+            for (var p = firstRemainingIndex; p <= lastRemainingIndex; p++) {
+                var child = children[p]
+                overrideChildWidth = buttonWidth(child)
+
+                // If necessary, we calculate and override the width first before we
+                // can calculate the x positions
+                if ((isTextToolButton(child) && childrenRemaining == 1)
+                        || (isButtonRow(child) && child.children.length == 1)) {
+                    // we treat a button row with a single item like a single tool button,
+                    // but in landscape, calculate size as if there were two buttons
+                    overrideChildWidth = portrait ?
+                        widthTextButtonSingle(leftMargin, innerSpacingTextButtonSingle) :
+                        widthTextButtonDouble(leftMargin, innerSpacingTextButtonDouble, innerSpacingTextButtonDouble)
+
+                } else if (isTextToolButton(child) && childrenRemaining == 2) {
+                    // special case of margins for two text buttons
+                    overrideChildWidth = widthTextButtonDouble(
+                                leftMargin, innerSpacingTextButtonDouble, centerSpacingTextButtonDouble)
+
+                } else if (isButtonRow(child) && ((child.children.length == 2)
+                              || (child.children.length > 2 && leftItem != undefined && rightItem != undefined))) {
+                    // there are special margins if the button row has two children,
+                    // or if it has more than two children and there is a left and
+                    // a right item
+                    overrideChildWidth = widthTextButtonSingle(
+                                leftMargin, innerSpacingButtonRowTwoChildren)
+
+                } else if (isButtonRow(child) && child.children.length > 2) {
+                    // the long button row has special margins, which are used on
+                    // either side if the side icon button is missing on that side. If the item is present,
+                    // the leftMargin can be used on either side to leave space for either icon button
+                    overrideChildWidth = widthButtonRowLong(
+                                leftItem != undefined,
+                                rightItem != undefined,
+                                leftMargin,
+                                innerSpacingButtonRowLong,
+                                outerMarginButtonRowLong)
+                }
+
+                child.width = overrideChildWidth
+            }
+
+            if (numChildren == 1) {
+                var loneChild = children[0]
+                var loneChildWidth = buttonWidth(loneChild)
+                if (isButtonRow(loneChild)) {
+                    loneChildWidth = overrideChildWidth
+                }
+                if (isIconButton(loneChild))
+                    loneChild.x = outerMarginHorizontal
+                else
+                    loneChild.x = centerOffset(root.width, loneChildWidth)
+                return
+            }
+
+            // we can easily calculate the positions of the left and right items,
+            // but if they are missing then correct the margins
+            if (leftItem != undefined){
+                leftItem.x = outerMarginHorizontal
+            } else {
+                leftMargin = 0
+            }
+            if (rightItem != undefined){
+                rightItem.x = root.width - defaultHeightToolBar - outerMarginHorizontal
+            } else {
+                rightMargin = root.width
+            }
+
+            if (!childrenRemaining)
+                return;
+
+            if (childrenRemaining == 1) {
+                var loneChild = children[firstRemainingIndex]
+                var loneChildWidth = buttonWidth(loneChild)
+                if (isButtonRow(loneChild)) {
+                    // ButtonRow should have the override width (but it won't have
+                    // been updated yet)
+                    loneChildWidth = overrideChildWidth
+                }
+                // lone child is always centered, unless it's a long button row on
+                // one side only
+                if (isButtonRow(loneChild) && loneChild.children.length >= 3
+                        && ((leftItem == undefined) != (rightItem == undefined))) {
+                    loneChild.x = (leftItem != undefined) ?
+                                (leftMargin + innerSpacingButtonRowLong) : outerMarginButtonRowLong
+                } else {
+                    loneChild.x = centerOffset(root.width, loneChildWidth)
+                }
+            } else if (childrenRemaining == 2 && isTextToolButton(children[firstRemainingIndex])) {
+                // text buttons are distributed around the center with a center spacing
+                var midPoint = Math.floor(root.width / 2.0)
+                var halfSpacing = Math.round(platformStyle.paddingLarge / 2.0)
+                children[firstRemainingIndex].x = midPoint - halfSpacing - buttonWidth(children[firstRemainingIndex])
+                children[firstRemainingIndex + 1].x = midPoint + halfSpacing
+            } else {
+                // icon buttons are deployed evenly in the remaining space,
+                // but we need to ensure that the spacings are integer values,
+                // and share the rounding error to ensure that they are centered
+                var remainingSpace = rightMargin - leftMargin
+                var spacingNotRounded = remainingSpace
+                for (var p = 0; p < childrenRemaining; p++) {
+                    var nextChild = children[leftItem != undefined ? p + 1 : p]
+                    spacingNotRounded -= buttonWidth(nextChild)
+                }
+                spacingNotRounded /= (childrenRemaining + 1)
+                var spacing = Math.floor(spacingNotRounded)
+                var totalRoundingError = (spacingNotRounded - spacing) * (childrenRemaining + 1)
+                var curPos = leftMargin + Math.floor(totalRoundingError / 2.0)
+
+                for (var p = 0; p < childrenRemaining; p++) {
+                    var nextChild = children[leftItem != undefined ? p + 1 : p]
+                    curPos += spacing
+                    nextChild.x = curPos
+                    curPos += buttonWidth(nextChild)
+                }
+            }
+        }
+    }
+
+    Component.onCompleted: internal.layoutChildren()
+    onParentChanged: internal.layoutChildren()
+    onChildrenChanged: internal.layoutChildren()
+    onImplicitWidthChanged: internal.layoutChildren()
+    onImplicitHeightChanged: internal.layoutChildren()
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ToolButton.qml b/mardrone/imports/com/nokia/android.1.1/ToolButton.qml
new file mode 100644 (file)
index 0000000..65857e6
--- /dev/null
@@ -0,0 +1,385 @@
+/****************************************************************************
+**
+** 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 Qt.labs.components 1.1
+import "." 1.1
+
+Item {
+    id: root
+
+    // Common API
+    property alias checkable: checkableItem.enabled
+    property alias checked: checkableItem.checked
+    property bool enabled: true // overridden from base class
+    property alias text: label.text
+    property url iconSource
+    property bool flat: (!text && iconSource != "" && parent && !internal.isButtonRow(parent))
+    property bool pressed: mouseArea.containsMouse && (stateGroup.state == "Pressed" || stateGroup.state == "PressAndHold")
+
+    // Platform API
+    property alias platformExclusiveGroup: checkableItem.exclusiveGroup
+    property bool platformInverted: false
+
+    // Common API
+    signal clicked
+
+    // Platform API
+    signal platformReleased
+    signal platformPressAndHold
+
+    onFlatChanged: {
+        background.visible = !flat || (checkableItem.enabled && checkableItem.checked && !internal.isButtonRow(parent))
+    }
+
+    implicitWidth: {
+        if (!text)
+            return internal.iconButtonWidth()
+        else if (iconSource == "")
+            return Math.max(internal.iconButtonWidth(), internal.textButtonWidth())
+        else
+            return internal.iconButtonWidth() + internal.textButtonWidth()
+    }
+    implicitHeight: {
+        if ((iconSource != "") && !text)
+            // if there is just an icon, then it's full button height
+            return internal.iconButtonWidth()
+        else
+            // Otherwise frame height is always tool bar's height in landscape, regardless of the current orientation
+            return privateStyle.toolBarHeightLandscape
+    }
+
+    BorderImage {
+        id: background
+
+        source: privateStyle.imagePath(internal.imageName() + internal.modeName(), root.platformInverted)
+        border {
+            left: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+            top: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+            right: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+            bottom: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+        }
+        smooth: true
+        anchors.fill: parent
+        visible: !flat
+
+        BorderImage {
+            id: highlight
+            border {
+                left: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+                top: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+                right: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+                bottom: internal.isFrameGraphic ? platformStyle.borderSizeMedium : 0;
+            }
+            smooth: true
+            anchors.fill: background
+            opacity: 0
+        }
+    }
+
+    Image {
+        id: contentIcon
+        source: privateStyle.toolBarIconPath(iconSource, root.platformInverted)
+        visible: iconSource != ""
+        sourceSize.width: platformStyle.graphicSizeSmall
+        sourceSize.height: platformStyle.graphicSizeSmall
+        anchors {
+            centerIn: !text ? parent : undefined
+            verticalCenter: !text ? undefined : parent.verticalCenter
+            left: !text ? undefined : parent.left
+            leftMargin: !text ? 0 : 2 * platformStyle.paddingMedium
+        }
+        smooth: true
+    }
+
+    Text {
+        id: label
+        clip: true
+        horizontalAlignment: Text.AlignHCenter
+        verticalAlignment: Text.AlignVCenter
+        elide: Text.ElideRight
+        font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeLarge }
+        color: {
+            if (!root.enabled)
+                return root.platformInverted ? platformStyle.colorDisabledLightInverted
+                                             : platformStyle.colorDisabledLight
+            else if (stateGroup.state == "Pressed" || stateGroup.state == "PressAndHold")
+                return root.platformInverted ? platformStyle.colorPressedInverted
+                                             : platformStyle.colorPressed
+            else
+                return root.platformInverted ? platformStyle.colorNormalLightInverted
+                                             : platformStyle.colorNormalLight
+        }
+        visible: text
+        anchors {
+            top: parent.top; bottom: parent.bottom
+            left: iconSource != "" ? contentIcon.right : parent.left; right: parent.right
+            leftMargin: iconSource != "" ? platformStyle.paddingSmall : platformStyle.paddingMedium
+            rightMargin: platformStyle.paddingMedium
+        }
+        smooth: true
+    }
+
+    MouseArea {
+        id: mouseArea
+        anchors.fill: parent
+
+        onPressed: if (root.enabled) stateGroup.state = "Pressed"
+        onReleased: if (root.enabled) stateGroup.state = ""
+        onCanceled: {
+            if (root.enabled) {
+                // Mark as canceled
+                stateGroup.state = "Canceled"
+                // Reset state. Can't expect a release since mouse was ungrabbed
+                stateGroup.state = ""
+            }
+        }
+        onPressAndHold: if (stateGroup.state != "Canceled" && root.enabled) stateGroup.state = "PressAndHold"
+        onExited: if (root.enabled) stateGroup.state = "Canceled"
+    }
+
+    QtObject {
+        id: internal
+
+        property bool isFrameGraphic : imageName().search("_fr") > 0
+
+        function belongsToExclusiveGroup() {
+            return checkableItem.exclusiveGroup
+                   || (root.parent
+                   && root.parent.hasOwnProperty("checkedButton")
+                   && root.parent.exclusive)
+        }
+
+        function modeName() {
+            if (isButtonRow(parent))
+                return parent.privateModeName(root, 1)
+            else if (!enabled)
+                return "disabled"
+            else if (flat && checkableItem.checked && !internal.isButtonRow(parent))
+                return "latched"
+            else
+                return "normal"
+        }
+
+        function iconButtonWidth() {
+            return (screen.width < screen.height) ? privateStyle.toolBarHeightPortrait : privateStyle.toolBarHeightLandscape
+        }
+
+        function textButtonWidth() {
+            return platformStyle.paddingMedium * ((screen.width < screen.height) ? 15 : 25)
+        }
+
+        function press() {
+            if (!belongsToExclusiveGroup()) {
+                if (checkableItem.enabled && checkableItem.checked)
+                    privateStyle.play(Android.SensitiveButton)
+                else
+                    privateStyle.play(Android.BasicButton)
+            } else if (checkableItem.enabled && !checkableItem.checked) {
+                privateStyle.play(Android.BasicButton)
+            }
+
+            if (flat)
+                background.visible = true
+            highlight.source = privateStyle.imagePath(internal.imageName() + "pressed", root.platformInverted)
+            label.scale = 0.95
+            contentIcon.scale = 0.95
+            highlight.opacity = 1
+        }
+
+        function release() {
+            label.scale = 1
+            contentIcon.scale = 1
+            highlight.opacity = 0
+
+            if (((checkableItem.enabled && checkableItem.checked && !belongsToExclusiveGroup())
+                || !checkableItem.enabled) && stateGroup.state != "Canceled")
+                privateStyle.play(Android.BasicButton)
+
+            if (flat && isButtonRow(parent))
+                visibleEffect.restart() //Background invisible
+            else if (flat && !checkableItem.enabled)
+                visibleEffect.restart() //Background invisible
+            else
+                clickedEffect.restart() //Background stays visible
+
+            root.platformReleased()
+        }
+
+        function click() {
+            checkableItem.toggle()
+            root.clicked()
+        }
+
+        function hold() {
+            root.platformPressAndHold()
+        }
+
+        function isButtonRow(item) {
+            return (item &&
+                    item.hasOwnProperty("checkedButton") &&
+                    item.hasOwnProperty("privateDirection") &&
+                    item.privateDirection == Qt.Horizontal)
+        }
+
+        // The function imageName() handles fetching correct graphics for the ToolButton.
+        // If the parent of a ToolButton is ButtonRow, segmented-style graphics are used to create a
+        // seamless row of buttons. Otherwise normal ToolButton graphics are utilized.
+        function imageName() {
+            var mirror = root.LayoutMirroring.enabled // To create binding
+            if (isButtonRow(parent))
+                return parent.privateGraphicsName(root, 1)
+            else
+                return (!flat || text || iconSource == "") ? "qtg_fr_toolbutton_text_" : "qtg_graf_toolbutton_"
+        }
+    }
+
+    StateGroup {
+        id: stateGroup
+
+        states: [
+            State { name: "Pressed" },
+            State { name: "PressAndHold" },
+            State { name: "Canceled" }
+        ]
+
+        transitions: [
+            Transition {
+                to: "Pressed"
+                ScriptAction { script: internal.press() }
+            },
+            Transition {
+                from: "Pressed"
+                to: "PressAndHold"
+                ScriptAction { script: internal.hold() }
+            },
+            Transition {
+                from: "Pressed"
+                to: ""
+                ScriptAction { script: internal.release() }
+                ScriptAction { script: internal.click() }
+            },
+            Transition {
+                from: "PressAndHold"
+                to: ""
+                ScriptAction { script: internal.release() }
+                ScriptAction { script: internal.click() }
+            },
+            Transition {
+                from: "Pressed"
+                to: "Canceled"
+                ScriptAction { script: internal.release() }
+            },
+            Transition {
+                from: "PressAndHold"
+                to: "Canceled"
+                ScriptAction { script: internal.release() }
+            }
+        ]
+    }
+
+    ParallelAnimation {
+        id: clickedEffect
+        PropertyAnimation {
+            target: label
+            property: "scale"
+            from: 0.95
+            to: 1.0
+            easing.type: Easing.Linear
+            duration: 100
+        }
+        PropertyAnimation {
+            target: contentIcon
+            property: "scale"
+            from: 0.95
+            to: 1.0
+            easing.type: Easing.Linear
+            duration: 100
+        }
+        PropertyAnimation {
+            target: highlight
+            property: "opacity"
+            from: 1
+            to: 0
+            easing.type: Easing.Linear
+            duration: 150
+        }
+    }
+
+    SequentialAnimation {
+        id: visibleEffect
+        ParallelAnimation {
+            PropertyAnimation {
+                target: label
+                property: "scale"
+                from: 0.95
+                to: 1.0
+                easing.type: Easing.Linear
+                duration: 100
+            }
+            PropertyAnimation {
+                target: contentIcon
+                property: "scale"
+                from: 0.95
+                to: 1.0
+                easing.type: Easing.Linear
+                duration: 100
+            }
+            PropertyAnimation {
+                target: highlight
+                property: "opacity"
+                from: 1
+                to: 0
+                easing.type: Easing.Linear
+                duration: 150
+            }
+        }
+        PropertyAction {
+            target: background
+            property: "visible"
+            value: false
+        }
+    }
+
+    Checkable {
+        id: checkableItem
+        value: root.text
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/ToolTip.qml b/mardrone/imports/com/nokia/android.1.1/ToolTip.qml
new file mode 100644 (file)
index 0000000..bae2155
--- /dev/null
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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 "AppManager.js" as AppView
+
+Item {
+    id: root
+
+    // Public API
+    property alias font: label.font
+    property alias text: label.text
+    property variant target: null
+    property bool platformInverted: false
+
+    implicitWidth: Math.min(privy.maxWidth, (privateStyle.textWidth(text, font) + privy.hMargin * 2))
+    implicitHeight: privateStyle.fontHeight(font) + privy.vMargin * 2
+    opacity: 0
+
+    function show() {
+        opacity = 1
+        visible = true
+    }
+
+    function hide() {
+        opacity = 0
+    }
+
+    Behavior on opacity {
+        PropertyAnimation { duration: 100 }
+    }
+
+    Component.onCompleted: {
+        if (visible)
+            show()
+    }
+
+    onOpacityChanged: {
+        if (opacity == 0)
+            visible = false
+        else
+            visible = true
+    }
+
+    onVisibleChanged: {
+        if (visible) {
+            root.parent = AppView.rootObject()
+            privy.calculatePosition()
+            opacity = 1
+            privy.targetSceneXChanged.connect(privy.targetMoved)
+            privy.targetSceneYChanged.connect(privy.targetMoved)
+        } else {
+            privy.targetSceneXChanged.disconnect(privy.targetMoved)
+            privy.targetSceneYChanged.disconnect(privy.targetMoved)
+        }
+    }
+
+    Binding { target: privy; property: "targetSceneX"; value: AppView.sceneX(root.target); when: root.visible && (root.target != null) }
+    Binding { target: privy; property: "targetSceneY"; value: AppView.sceneY(root.target); when: root.visible && (root.target != null) }
+
+    QtObject {
+        id: privy
+
+        property real hMargin: platformStyle.paddingMedium * 2
+        property real vMargin: platformStyle.paddingMedium
+        property real spacing: platformStyle.paddingLarge
+        property real maxWidth: screen.width - spacing * 2
+        property real targetSceneX
+        property real targetSceneY
+
+        function targetMoved() {
+            if (root.opacity == 1)
+                hide()
+        }
+
+        function calculatePosition() {
+            if (!target)
+                return
+
+            // Determine and set the main position for the ToolTip, order: top, right, left, bottom
+            var targetPos = root.parent.mapFromItem(target, 0, 0)
+
+            // Top
+            if (targetPos.y >= (root.height + privy.vMargin + privy.spacing)) {
+                root.x = targetPos.x + (target.width / 2) - (root.width / 2)
+                root.y = targetPos.y - root.height - privy.vMargin
+
+            // Right
+            } else if (targetPos.x <= (screen.width - target.width - privy.hMargin - root.width - privy.spacing)) {
+                root.x = targetPos.x + target.width + privy.hMargin;
+                root.y = targetPos.y + (target.height / 2) - (root.height / 2)
+
+            // Left
+            } else if (targetPos.x >= (root.width + privy.hMargin + privy.spacing)) {
+                root.x = targetPos.x - root.width - privy.hMargin
+                root.y = targetPos.y + (target.height / 2) - (root.height / 2)
+
+            // Bottom
+            } else {
+                root.x = targetPos.x + (target.width / 2) - (root.width / 2)
+                root.y = targetPos.y + target.height + privy.vMargin
+            }
+
+            // Fine-tune the ToolTip position based on the screen borders
+            if (root.x > (screen.width - privy.spacing - root.width))
+                root.x = screen.width - root.width - privy.spacing
+
+            if (root.x < privy.spacing)
+                root.x = privy.spacing;
+
+            if (root.y > (screen.height - root.height - privy.spacing))
+                root.y = screen.height - root.height - privy.spacing
+
+            if (root.y < privy.spacing)
+                root.y = privy.spacing
+        }
+    }
+
+    BorderImage {
+        id: frame
+        anchors.fill: parent
+        source: privateStyle.imagePath("qtg_fr_tooltip", root.platformInverted)
+        border { left: 20; top: 20; right: 20; bottom: 20 }
+    }
+
+    Text {
+       id: label
+       clip: true
+       color: root.platformInverted ? platformStyle.colorNormalLightInverted
+                                    : platformStyle.colorNormalLight
+       elide: Text.ElideRight
+       font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
+       verticalAlignment: Text.AlignVCenter
+       horizontalAlignment: Text.AlignHCenter
+       anchors {
+           fill: parent
+           leftMargin: privy.hMargin
+           rightMargin: privy.hMargin
+           topMargin: privy.vMargin
+           bottomMargin: privy.vMargin
+       }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/Window.qml b/mardrone/imports/com/nokia/android.1.1/Window.qml
new file mode 100644 (file)
index 0000000..efde5ca
--- /dev/null
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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
+
+Item {
+    id: root
+
+    property bool inPortrait: height > width
+
+    signal orientationChangeAboutToStart
+    signal orientationChangeStarted
+    signal orientationChangeFinished
+
+    // Symbian specific API
+    property bool platformInverted: false
+
+    width: screen.width > 0 ? screen.width : screen.displayWidth
+    height: screen.height > 0 ? screen.height : screen.displayHeight
+
+    Rectangle {
+        anchors.fill: parent
+        color: platformInverted ? platformStyle.colorBackgroundInverted
+                                : platformStyle.colorBackground
+    }
+
+    Binding { target: root; property: "height"; value: screen.height; when: screen.height > 0 }
+    Binding { target: root; property: "width"; value: screen.width; when: screen.width > 0 }
+
+    Connections {
+        target: screen
+        onCurrentOrientationChanged: root.orientationChangeFinished()
+        onPrivateAboutToChangeOrientation: {
+            root.orientationChangeAboutToStart()
+            root.orientationChangeStarted()
+        }
+    }
+}
diff --git a/mardrone/imports/com/nokia/android.1.1/qmldir b/mardrone/imports/com/nokia/android.1.1/qmldir
new file mode 100644 (file)
index 0000000..54b71bf
--- /dev/null
@@ -0,0 +1,42 @@
+plugin androidplugin_1_1
+ApplicationWindow 1.1 ApplicationWindow.qml
+BusyIndicator 1.1 BusyIndicator.qml
+Button 1.1 Button.qml
+ButtonColumn 1.1 ButtonColumn.qml
+ButtonRow 1.1 ButtonRow.qml
+CheckBox 1.1 CheckBox.qml
+CommonDialog 1.1 CommonDialog.qml
+ContextMenu 1.1 ContextMenu.qml
+Dialog 1.1 Dialog.qml
+Label 1.1 Label.qml
+ListHeading 1.1 ListHeading.qml
+ListItem 1.1 ListItem.qml
+ListItemText 1.1 ListItemText.qml
+Menu 1.1 Menu.qml
+MenuItem 1.1 MenuItem.qml
+MenuLayout 1.1 MenuLayout.qml
+Page 1.1 Page.qml
+PageStack 1.1 PageStack.qml
+PageStackWindow 1.1 PageStackWindow.qml
+ProgressBar 1.1 ProgressBar.qml
+QueryDialog 1.1 QueryDialog.qml
+RadioButton 1.1 RadioButton.qml
+ScrollBar 1.1 ScrollBar.qml
+ScrollDecorator 1.1 ScrollDecorator.qml
+SectionScroller 1.1 SectionScroller.qml
+SelectionDialog 1.1 SelectionDialog.qml
+SelectionListItem 1.1 SelectionListItem.qml
+Slider 1.1 Slider.qml
+StatusBar 1.1 StatusBar.qml
+Switch 1.1 Switch.qml
+TabBar 1.1 TabBar.qml
+TabBarLayout 1.1 TabBarLayout.qml
+TabButton 1.1 TabButton.qml
+TabGroup 1.1 TabGroup.qml
+TextArea 1.1 TextArea.qml
+TextField 1.1 TextField.qml
+ToolBar 1.1 ToolBar.qml
+ToolBarLayout 1.1 ToolBarLayout.qml
+ToolButton 1.1 ToolButton.qml
+ToolTip 1.1 ToolTip.qml
+Window 1.1 Window.qml