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
+                    }