diff --git a/mobile-datovka.pro b/mobile-datovka.pro index 97b0fe8784b838d7516c69de4835ff32d6bc7c26..9e2cc1682556d1ac5f3db9e5a667022204771597 100644 --- a/mobile-datovka.pro +++ b/mobile-datovka.pro @@ -125,6 +125,7 @@ SOURCES += \ src/qml_interaction/interaction_zfo_file.cpp \ src/qml_interaction/message_envelope.cpp \ src/qml_interaction/message_info.cpp \ + src/qml_interaction/string_manipulation.cpp \ src/settings.cpp \ src/setwrapper.cpp \ src/sqlite/account_db.cpp \ @@ -188,6 +189,7 @@ HEADERS += \ src/qml_interaction/interaction_zfo_file.h \ src/qml_interaction/message_envelope.h \ src/qml_interaction/message_info.h \ + src/qml_interaction/string_manipulation.h \ src/settings.h \ src/setwrapper.h \ src/sqlite/account_db.h \ diff --git a/qml/components/AccessibleButton.qml b/qml/components/AccessibleButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..dedabcdd3312476d02da62f5530895a1a435b58d --- /dev/null +++ b/qml/components/AccessibleButton.qml @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +/* + * Accessible button component. + */ +Button { + id: root + + /* These properties must be set by caller. */ + property string accessibleDescription: "" + property string accessibleName: "" + + Accessible.role: Accessible.Button + Accessible.checkable: root.checkable + Accessible.checked: root.checked + Accessible.description: root.accessibleDescription + Accessible.name: (root.accessibleName !== "") ? root.accessibleName : root.text + Accessible.pressed: root.pressed + Accessible.onPressAction: { + root.clicked() + } +} diff --git a/qml/components/AccessibleComboBox.qml b/qml/components/AccessibleComboBox.qml new file mode 100644 index 0000000000000000000000000000000000000000..e0320a75d89a1d1b93b9dce12d923e80f8f8cda9 --- /dev/null +++ b/qml/components/AccessibleComboBox.qml @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import cz.nic.mobileDatovka 1.0 + +/* + * Accessible combo box component. + */ +ComboBox { + id: root + + /* These properties must be set by caller. */ + property string accessibleDescription: "" + + /* + * The model entries must contain these properties. + * label - (string with item description) + * item description + * key - (string holding one of the keys) + * unique string identifying an item + * e.g.: + ListModel { + id: listModel + ListElement { + label: qsTr("Some selection") + key: "some" + } + } + */ + + currentIndex: 0 + textRole: "label" + + height: inputItemHeight + font.pointSize: defaultTextFont.font.pointSize + + /* + * Return current key value. + */ + function currentKey() { + return root.model.get(root.currentIndex).key + } + + /* + * Selects entry with given key. + */ + function selectCurrentKey(key) { + for (var i = 0; i < root.model.count; ++i) { + //root.model.get(i)["key"] + if (root.model.get(i).key === key) { + root.currentIndex = i; + return; + } + } + /* Not found. */ + console.log("No entry found for key '" + key + "'."); + } + + delegate: ItemDelegate { + width: root.width + height: inputItemHeight + contentItem: AccessibleTextButton { + text: label + color: datovkaPalette.text + font.pointSize: defaultTextFont.font.pointSize + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + onClicked: { + root.currentIndex = index + root.popup.close() + } + } + highlighted: root.highlightedIndex == index + } + + Accessible.role: Accessible.ComboBox + Accessible.description: accessibleDescription + Accessible.name: root.currentText + Accessible.onPressAction: { + root.popup.open() + } +} diff --git a/qml/components/AccessibleImageButton.qml b/qml/components/AccessibleImageButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..5029d0828555ac45a84cdcc365270bf925a0ef26 --- /dev/null +++ b/qml/components/AccessibleImageButton.qml @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 + +/* + * Accessible button image component. + */ +Image { + id: root + + /* These properties must be set by caller. */ + property string accessibleName: "" + + signal clicked() + + MouseArea { + function handleClick() { + root.clicked() + } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: root.accessibleName + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } +} diff --git a/qml/components/AccessibleMenu.qml b/qml/components/AccessibleMenu.qml new file mode 100644 index 0000000000000000000000000000000000000000..23352d2e7075b8a1ae60db4ed734e857a92efbd0 --- /dev/null +++ b/qml/components/AccessibleMenu.qml @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import cz.nic.mobileDatovka 1.0 + +/* + * Accessible menu component. + * + * An object containing functions to be called and a model in specified form + * must be provided. + */ +ScrollableListView { + id: root + + delegateHeight: headerHeight + + /* These properties must be set by caller. */ + property var funcArr: null /* https://stackoverflow.com/a/24376300 */ + + /* + * The property funcArr must hold an object (associative array). + * The keys must be strings, values must be functions. + * e.g.: + property var funcs: { + "funcName": function callFuncName { + console.log("Calling function funcName.") + } + } + + * The model entries must contain these properties. + * image - (string containing path of image resource) + * image to be shown in the menu before the text + * showEntry - (boolean value) + * true means that the entire menu entry is being shown + * showNext - (boolean value) + * true means that an further menu is being indicated + * name - (string with action description) + * action + * funcName - (string holding one of function array keys) + * string identifying function to be called + * e.g.: + ListModel { + id: listModel + ListElement { + image: "qrc:/ui/settings.svg" + showEntry: true + showNext: false + name: qsTr("Perform some action") + funcName: "funcName" + } + } + */ + + /* + * Return the first index of the element having a specified property of + * specified value. + * + * Returns -1 if nothing found. + */ + function get_model_index(model, property, value) { + for (var i = 0; i < model.count; ++i) { + if (model.get(i)[property] === value) { + return i; + } + } + /* Not found. */ + return -1; + } + + /* https://stackoverflow.com/a/7356528 */ + function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; + } + + Component { + id: menuDelegate + Rectangle { + visible: showEntry + color: datovkaPalette.base + height: showEntry ? root.delegateHeight : 0 /* Collapse when not visible. */ + width: parent.width + Image { + id: menuImage + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: defaultMargin + sourceSize.height: imgHeight + source: image + } + ColorOverlay { + anchors.fill: menuImage + source: menuImage + color: datovkaPalette.text + } + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: menuImage.right + anchors.leftMargin: defaultMargin + color: datovkaPalette.text + text: name + } + Rectangle { + id: next2 + visible: showNext + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + height: parent.height + width: parent.width * 0.07 + color: parent.color + Image { + id: nextImage2 + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: defaultMargin + sourceSize.height: navImgHeight + source: "qrc:/ui/next.svg" + } + ColorOverlay { + anchors.fill: nextImage2 + source: nextImage2 + color: datovkaPalette.text + } + } + MouseArea { + function handleClick() { + /* https://stackoverflow.com/a/1098955 */ + if (funcName in funcArr) { + if (isFunction(funcArr[funcName])) { + /* Call function. */ + funcArr[funcName]() + } else { + console.log("'" + funcName + "' is not a function.") + } + } else { + console.log("Unknown function '" + funcName + "'.") + } + } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: name + Accessible.onScrollDownAction: { + root.scrollDown() + } + Accessible.onScrollUpAction: { + root.scrollUp() + } + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } + } + } + + delegate: menuDelegate +} diff --git a/qml/components/AccessibleOverlaidImageButton.qml b/qml/components/AccessibleOverlaidImageButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..e17652bed0a7397efaaab653fe603e4f031fa0bb --- /dev/null +++ b/qml/components/AccessibleOverlaidImageButton.qml @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import cz.nic.mobileDatovka 1.0 + +/* + * Accessible button ovelaid image component. + */ +OverlaidImage { + id: root + + /* These properties must be set by caller. */ + property string accessibleName: "" + + signal clicked() + + MouseArea { + function handleClick() { + root.clicked() + } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: root.accessibleName + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } +} diff --git a/qml/components/AccessibleSpinBox.qml b/qml/components/AccessibleSpinBox.qml new file mode 100644 index 0000000000000000000000000000000000000000..c5bba9d7756e7f7d881fb05d2347a8e84fdd52bd --- /dev/null +++ b/qml/components/AccessibleSpinBox.qml @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +/* + * Accessible spin box component. + */ +SpinBox { + id: root + + /* These properties must be set by caller. */ + property string accessibleDescription: "" + + height: inputItemHeight + font.pointSize: defaultTextFont.font.pointSize + + /* + * Return spin box value. + */ + function val() { + return root.value; + } + + /* + * Set spin box value. + */ + function setVal(v) { + if (v < root.from) { + root.value = root.from + } else if (v > root.to) { + root.value = root.to + } else { + root.value = v + } + } + + MouseArea { + anchors.fill: root.down.indicator + + function handleClick() { + root.decrease() + root.valueModified() + } + + Accessible.role: Accessible.Button + Accessible.description: root.accessibleDescription + //Accessible.focusable: true + Accessible.name: qsTr("Decrease value '%1'.").arg(root.val()) + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } + MouseArea { + anchors.top: root.top + anchors.bottom: root.bottom + anchors.left: (root.down.indicator.x < root.up.indicator.x) ? root.down.indicator.right : root.up.indicator.right + anchors.right: (root.down.indicator.x < root.up.indicator.x) ? root.up.indicator.left : root.up.indicator.left + + Accessible.role: root.editable ? Accessible.EditableText : Accessible.StaticText + Accessible.description: root.accessibleDescription + Accessible.editable: root.editable + Accessible.focusable: true + Accessible.multiLine: false + Accessible.name: root.val() + Accessible.readOnly: !root.editable + } + MouseArea { + anchors.fill: root.up.indicator + + function handleClick() { + root.increase() + root.valueModified() + } + + Accessible.role: Accessible.Button + Accessible.description: root.accessibleDescription + //Accessible.focusable: true + Accessible.name: qsTr("Increase value '%1'.").arg(root.val()) + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } +} diff --git a/qml/components/SpinBoxZeroMax.qml b/qml/components/AccessibleSpinBoxZeroMax.qml similarity index 55% rename from qml/components/SpinBoxZeroMax.qml rename to qml/components/AccessibleSpinBoxZeroMax.qml index eaa0bd2da9ad6bd05f10b35734905ef4dbbb9d10..0be7bfeb9bbf7d4ce42c0d46e98694cdfbb1190a 100644 --- a/qml/components/SpinBoxZeroMax.qml +++ b/qml/components/AccessibleSpinBoxZeroMax.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,9 +21,13 @@ * the two. */ +import QtQuick 2.7 import QtQuick.Controls 2.2 +import cz.nic.mobileDatovka 1.0 + +AccessibleSpinBox { + id: root -SpinBox { /* Must be a non-decreasing list ending with infinity. */ property var items: [1, qsTr("max")] property int dfltIdx: 0 @@ -73,4 +77,57 @@ SpinBox { value = from } } + + MouseArea { + anchors.fill: root.down.indicator + + function handleClick() { + root.decrease() + root.valueModified() + } + + Accessible.role: Accessible.Button + Accessible.description: root.accessibleDescription + //Accessible.focusable: true + Accessible.name: qsTr("Decrease value '%1'.").arg(root.textFromValue(root.value)) + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } + MouseArea { + anchors.top: root.top + anchors.bottom: root.bottom + anchors.left: (root.down.indicator.x < root.up.indicator.x) ? root.down.indicator.right : root.up.indicator.right + anchors.right: (root.down.indicator.x < root.up.indicator.x) ? root.up.indicator.left : root.up.indicator.left + + Accessible.role: root.editable ? Accessible.EditableText : Accessible.StaticText + Accessible.description: root.accessibleDescription + Accessible.editable: root.editable + Accessible.focusable: true + Accessible.multiLine: false + Accessible.name: root.textFromValue(root.value) + Accessible.readOnly: !root.editable + } + MouseArea { + anchors.fill: root.up.indicator + + function handleClick() { + root.increase() + root.valueModified() + } + + Accessible.role: Accessible.Button + Accessible.description: root.accessibleDescription + //Accessible.focusable: true + Accessible.name: qsTr("Increase value '%1'.").arg(root.textFromValue(root.value)) + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } } diff --git a/qml/components/AccessibleSwitch.qml b/qml/components/AccessibleSwitch.qml new file mode 100644 index 0000000000000000000000000000000000000000..6479f198221af769c3f77f3f269ced65a5b6e313 --- /dev/null +++ b/qml/components/AccessibleSwitch.qml @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +/* + * Accessible switch component. + */ +Switch { + id: root + + /* These properties must be set by caller. */ + property string accessibleDescription: "" + property string accessibleName: "" + + Accessible.role: Accessible.CheckBox + Accessible.checkStateMixed: false + Accessible.checkable: true + Accessible.checked: root.checked + Accessible.description: root.accessibleDescription + //Accessible.focusable: true + Accessible.name: (root.accessibleName !== "") ? root.accessibleName : root.text + Accessible.onPressAction: { + root.toggle() + } + Accessible.onToggleAction: { + root.toggle() + } +} diff --git a/qml/components/AccessibleTabButton.qml b/qml/components/AccessibleTabButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..484edc97eb0f5d7c38c5d55039eb90a277126095 --- /dev/null +++ b/qml/components/AccessibleTabButton.qml @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +/* + * Accessible button component. + */ +TabButton { + id: root + + /* These properties must be set by caller. */ + property string accessibleDescription: "" + property string accessibleName: "" + + Accessible.role: Accessible.Button + Accessible.checkable: root.checkable + Accessible.checked: root.checked + Accessible.description: root.accessibleDescription + Accessible.name: (root.accessibleName !== "") ? root.accessibleName : root.text + Accessible.onPressAction: { + /* + * It looks like TabBar is actually a Container of checkable + * AbstractButton elements. For further details see sources of + * QQuickTabBar, QQuickContainer and QQuickAbstractButton. Some hints + * can also be found here: https://stackoverflow.com/a/32426364 + */ + root.checked = true + } +} diff --git a/qml/components/AccessibleText.qml b/qml/components/AccessibleText.qml new file mode 100644 index 0000000000000000000000000000000000000000..7b96f36ac274374ee536f85260d97a3ee0b6d138 --- /dev/null +++ b/qml/components/AccessibleText.qml @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 + +/* + * Accessible text component. + * + * This component exposes its text to the accessibility interface. Rich text + * is converted onto plain text. + * If accessibleName is specified then its content is used instead of the text + * property value. + */ +Text { + id: root + + /* These properties must be set by caller. */ + property string accessibleName: "" + + Accessible.role: Accessible.StaticText + Accessible.focusable: true /* This enables the text to selectable and readable by Android TalkBack. */ + Accessible.name: (root.accessibleName !== "") ? root.accessibleName : ((root.textFormat !== TextEdit.RichText) ? root.text : strManipulation.removeRich(root.text)) +} diff --git a/qml/components/AccessibleTextButton.qml b/qml/components/AccessibleTextButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..da62ad1d9e3a6dbe68cd18fbcdd0b167d5fa07e3 --- /dev/null +++ b/qml/components/AccessibleTextButton.qml @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 + +/* + * Accessible text component. + * + * This component exposes the its text to the accessibility interface. + * If accessibleName is specified then its content is used instead of the text + * property value. + */ +Text { + id: root + + /* These properties must be set by caller. */ + property string accessibleName: "" + + signal clicked() + + MouseArea { + function handleClick() { + root.clicked() + } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: (root.accessibleName !== "") ? root.accessibleName : ((root.textFormat !== TextEdit.RichText) ? root.text : strManipulation.removeRich(root.text)) + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } +} diff --git a/qml/components/AccessibleTextField.qml b/qml/components/AccessibleTextField.qml new file mode 100644 index 0000000000000000000000000000000000000000..fd72f612e71edb3d44024a45152ea1e52fd57d47 --- /dev/null +++ b/qml/components/AccessibleTextField.qml @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +/* + * Accessible text field component. + */ +TextField { + id: root + + /* These properties must be set by caller. */ + property string accessibleDescription: "" + + Accessible.role: Accessible.EditableText + Accessible.description: (root.accessibleDescription !== "") ? root.accessibleDescription : root.placeholderText + Accessible.editable: root.enabled + //Accessible.focusable: true + Accessible.multiLine: false + Accessible.name: root.text + Accessible.passwordEdit: (root.echoMode === TextInput.Password) || (root.echoMode === TextInput.PasswordEchoOnEdit) + Accessible.readOnly: root.readOnly + Accessible.searchEdit: false + Accessible.selectableText: true +} diff --git a/qml/components/DataboxList.qml b/qml/components/DataboxList.qml index 01605592271d51fb356a30dc224e1f47e7f45971..28c27749d773fa4133169e97edfddbcd551d189c 100644 --- a/qml/components/DataboxList.qml +++ b/qml/components/DataboxList.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,12 +25,14 @@ import QtGraphicalEffects 1.0 import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.1 - +import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.modelEntries 1.0 -ListView { +ScrollableListView { id: root + delegateHeight: listItemHeight + /* These properties must be set by caller. */ property bool canDetailBoxes: false // enables viewing of box details property bool canSelectBoxes: false // enables selecting of entries @@ -47,7 +49,7 @@ ListView { delegate: Rectangle { id: dbItem - height: listItemHeight + height: root.delegateHeight width: parent.width color: rDbSelected ? readBgColor : datovkaPalette.base Item { @@ -132,17 +134,79 @@ ListView { } // GridLayout } // Item MouseArea { - anchors.fill: parent - onClicked: { + /* + * Returns specified value: + * -1 - none ov below + * 0 - show box detail + * 1 - deselect box + * 2 - select box + */ + function actionOnClick() { + var ret = -1; if (canDetailBoxes && !(canSelectBoxes || canDeselectBoxes)) { - root.boxDetail(rDbID) + ret = 0; } else if (!canDetailBoxes && (canSelectBoxes || canDeselectBoxes)) { if (rDbSelected && canDeselectBoxes) { - root.boxDeselect(rDbID) + ret = 1; } else if (!rDbSelected && canSelectBoxes) { - root.boxSelect(rDbID) + ret = 2; } } + return ret; + } + + /* Construct string to be presented to accessibility interface. */ + function accessibleText() { + var aText = ""; + + switch (actionOnClick()) { + case 0: + aText += qsTr("View details about data box '%1' (identifier '%2').").arg(rDbName).arg(rDbID); + break; + case 1: + aText += qsTr("Deselect data box '%1' (identifier '%2').").arg(rDbName).arg(rDbID); + break; + case 2: + aText += qsTr("Select data box '%1' (identifier '%2').").arg(rDbName).arg(rDbID); + break; + default: + break; + } + + return aText; + } + + function handleClick() { + switch (actionOnClick()) { + case 0: + root.boxDetail(rDbID) + break; + case 1: + root.boxDeselect(rDbID) + break; + case 2: + root.boxSelect(rDbID) + break; + default: + break; + } + } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: accessibleText() + Accessible.onScrollDownAction: { + root.scrollDown() + } + Accessible.onScrollUpAction: { + root.scrollUp() + } + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() } } Rectangle { @@ -183,14 +247,43 @@ ListView { MouseArea { // visible only if canDetailBoxes visible: (!canDetailBoxes && !(canSelectBoxes || canDeselectBoxes) && canRemoveBoxes) - anchors.fill: parent - onClicked: { - // we must allow remove exist recipients from model. - if (!canDetailBoxes && !(canSelectBoxes || canDeselectBoxes) && canRemoveBoxes) { + + /* + * Returns true if can delete entries. + */ + function canRemoveEntries() { + return !canDetailBoxes && !(canSelectBoxes || canDeselectBoxes) && canRemoveBoxes; + } + + /* Construct string to be presented to accessibility interface. */ + function accessibleText() { + var aText = ""; + + if (canRemoveEntries()) { + aText += qsTr("Remove data box '%1' (identifier '%2').").arg(rDbName).arg(rDbID); + } + + return aText; + } + + function handleClick() { + /* We must allow removal of existing recipients from model. */ + if (canRemoveEntries()) { root.boxRemove(rDbID) } } - } // MouseArea + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: accessibleText() + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } } // Rectangle Rectangle { anchors.top: parent.bottom diff --git a/qml/components/FilterBar.qml b/qml/components/FilterBar.qml new file mode 100644 index 0000000000000000000000000000000000000000..e3ef95dae81f24d0491ecce239eff5519eba2ac2 --- /dev/null +++ b/qml/components/FilterBar.qml @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.1 + +/* + * Filter bar component. + */ +Rectangle { + id: root + + /* These properties must be set by caller. */ + property string placeholderText: "" + property real fontPointSize: 1.0 + property int buttonImageHeight: 1 + property string buttonAccessibleName: "" + + signal textChanged(string text) + signal clearClicked() + + property alias filterField: filterField + + AccessibleTextField { + id: filterField + + anchors.top: parent.top + width: parent.width - clearFilterButton.width + placeholderText: root.placeholderText + font.pointSize: root.fontPointSize + inputMethodHints: Qt.ImhNoPredictiveText + /* + * Using explicit top, left, ... padding because plain padding + * does not centre the text properly. + */ + topPadding: 8.0 + bottomPadding: 8.0 + leftPadding: 8.0 + rightPadding: 8.0 + /* TODO - remove background if used materials */ + background: Rectangle { + anchors.fill: parent + color: "transparent" + border.color: "transparent" + } + onTextChanged: { + root.textChanged(filterField.text) + } + + Accessible.searchEdit: true + } + AccessibleImageButton { + id: clearFilterButton + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: buttonImageHeight + source: "qrc:/ui/remove.svg" + accessibleName: buttonAccessibleName + onClicked: { + filterField.clear() + root.clearClicked() + } + } +} diff --git a/qml/components/MessageList.qml b/qml/components/MessageList.qml index 483db65165b669232ad2cce5d1040234cbfbc8c8..a3a67894c2bc81fedc8b63e7f6303ffccaf76083 100644 --- a/qml/components/MessageList.qml +++ b/qml/components/MessageList.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,19 +24,22 @@ import QtGraphicalEffects 1.0 import QtQuick 2.7 import QtQuick.Layouts 1.3 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.2 +import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.messages 1.0 -ListView { +ScrollableListView { id: root + delegateHeight: listItemHeight + /* These signals should be captured to implement message interaction. */ signal msgClicked(string userName, int msgType, string msgId) signal msgPressAndHold(string userName, int msgType, string msgId, bool canDelete) delegate: Rectangle { id: messageItem - height: listItemHeight + height: root.delegateHeight width: parent.width color: (rMsgType == MessageType.TYPE_RECEIVED && !rReadLocally) ? datovkaPalette.base : readBgColor Item { @@ -131,11 +134,41 @@ ListView { } } // Rectangle MouseArea { + /* Construct string to be presented to accessibility interface. */ + function accessibleText() { + var aText = ""; + if (rMsgType == MessageType.TYPE_RECEIVED) { + aText += !rReadLocally ? qsTr("Unread message from sender") : qsTr("Read message from sender") + } else { + aText += qsTr("Message to receiver") + } + aText += " '" + ((rMsgType == MessageType.TYPE_RECEIVED) ? rFrom : rTo) + "'. " + aText += qsTr("Subject") + ": '" + rAnnotation + "'." + return aText; + } + + function handleClick() { + root.msgClicked(rUserName, rMsgType, rMsgId) + } + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: accessibleText() + Accessible.onScrollDownAction: { + root.scrollDown() + } + Accessible.onScrollUpAction: { + root.scrollUp() + } + Accessible.onPressAction: { + handleClick() + } onClicked: { - root.msgClicked(rUserName, rMsgType, rMsgId) + handleClick() } onPressAndHold: { + /* Accessible doesn't support this type of action. */ root.msgPressAndHold(rUserName, rMsgType, rMsgId, compareMsgDate(rDelivTime)) } } @@ -146,5 +179,6 @@ ListView { color: datovkaPalette.dark } } // Rectangle + ScrollIndicator.vertical: ScrollIndicator {} } // ListView diff --git a/qml/components/PageHeader.qml b/qml/components/PageHeader.qml index 7257c1bedf284a3adee2c02d229e570b7527fe87..7e6ff5c80552b354197172dbc5e0383f306c26c6 100644 --- a/qml/components/PageHeader.qml +++ b/qml/components/PageHeader.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,20 +22,26 @@ */ import QtQuick 2.7 +import cz.nic.mobileDatovka 1.0 /* * Page header component. */ Rectangle { id: root + + /* These properties must be set by caller. */ property string title: "" + + signal backClicked() + anchors.top: parent.top width: parent.width height: headerHeight color: headerColor z: 1 Image { - id: backElement + id: backIcon anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: defaultMargin @@ -43,15 +49,36 @@ Rectangle { source: "qrc:/ui/back.svg" } Text { + id: backText anchors.verticalCenter: parent.verticalCenter - anchors.left: backElement.right + anchors.left: backIcon.right anchors.leftMargin: defaultMargin //font.bold: true color: datovkaPalette.text text: qsTr("Back") } - Text { + MouseArea { + function handleClick() { + backClicked() + } + + anchors.left: backIcon.left + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: backText.right + Accessible.role: Accessible.Button + Accessible.name: backText.text + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } + } + AccessibleText { anchors.centerIn: parent + height: parent.height + verticalAlignment: Text.AlignVCenter font.bold: true color: datovkaPalette.base text: title diff --git a/qml/components/ProgressBar.qml b/qml/components/ProgressBar.qml index 5afa85ed2565b2d83b9ec181da8c22b0bc61be24..e57b1840a87a7711b0fc7a2553c4d9883bef82d8 100644 --- a/qml/components/ProgressBar.qml +++ b/qml/components/ProgressBar.qml @@ -28,6 +28,7 @@ import QtQuick.Controls 2.1 Rectangle { id: root + visible: false property int statusBarTimer: 5000 anchors.left: parent.left @@ -35,19 +36,20 @@ Rectangle { anchors.right: parent.right height: headerHeight color: datovkaPalette.text + Column { anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.margins: defaultMargin spacing: formItemVerticalSpacing - Text { + AccessibleText { id: statusBarText color: datovkaPalette.base anchors.horizontalCenter: parent.horizontalCenter text: mainWindow.width + " x "+ mainWindow.height Connections { - target: isds + target: isds onStatusBarTextChanged: { root.visible = isVisible statusBarText.text = txt diff --git a/qml/components/ScrollableListView.qml b/qml/components/ScrollableListView.qml new file mode 100644 index 0000000000000000000000000000000000000000..6fbae03fa3f8a7fb10e436cdeb5aac636de02458 --- /dev/null +++ b/qml/components/ScrollableListView.qml @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 + +/* + * List view that can be scrolled programmatically. + */ +ListView { + id: root + + /* These properties must be set by caller. */ + property real delegateHeight: 1.0 + + /* + * Scroll list down. + */ + function scrollDown() { + var numViewed = Math.ceil(root.height / (2 * delegateHeight)) + if (numViewed < 1) { + numViewed = 1 + } + var indexAtTop = root.indexAt(1, 1 + root.contentY) + indexAtTop = (indexAtTop >= 0) ? (indexAtTop + numViewed) : 0 + root.positionViewAtIndex(indexAtTop, ListView.Beginning) + root.returnToBounds() + } + + /* + * Scroll list up. + */ + function scrollUp() { + var numViewed = Math.ceil(root.height / (2 * delegateHeight)) + if (numViewed < 1) { + numViewed = 1 + } + var indexAtBottom = root.indexAt(1, root.height - 2 + root.contentY) + indexAtBottom = (indexAtBottom >= 0) ? (indexAtBottom - numViewed) : 0 + root.positionViewAtIndex(indexAtBottom, ListView.End) + root.returnToBounds() + } +} diff --git a/qml/dialogues/FileDialogue.qml b/qml/dialogues/FileDialogue.qml index 86beb37ba7064f4208b05ff6b4dd55205ccd2578..95637d3eb7f7305c5fbe940b71dc15d1a4955206 100644 --- a/qml/dialogues/FileDialogue.qml +++ b/qml/dialogues/FileDialogue.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,9 +22,10 @@ */ import QtQuick 2.7 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Qt.labs.folderlistmodel 2.1 +import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.qmlInteraction 1.0 Dialog { @@ -33,7 +34,20 @@ Dialog { focus: true modal: true title: qsTr("Select path") - standardButtons: (selectedFileIndex != -1) ? (Dialog.Ok | Dialog.Cancel) : Dialog.Cancel + //standardButtons: (selectedFileIndex != -1) ? (Dialog.Ok | Dialog.Cancel) : Dialog.Cancel + + footer: DialogButtonBox { + AccessibleButton { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole + } + AccessibleButton { + text: qsTr("OK") + enabled: (selectedFileIndex != -1) + visible: (selectedFileIndex != -1) + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + } /* Place the dialogue in the centre. */ x: 2 * defaultMargin @@ -96,53 +110,51 @@ Dialog { contentItem: ColumnLayout { spacing: formItemVerticalSpacing - ComboBox { + AccessibleComboBox { anchors { left: parent.left; right: parent.right; } - textRole: "label" + accessibleDescription: qsTr("Select location type") model: ListModel { - ListElement { label: qsTr("Desktop"); value: "desktop" } - ListElement { label: qsTr("Documents"); value: "document" } - ListElement { label: qsTr("Downloads"); value: "download" } - ListElement { label: qsTr("Pictures"); value: "picture" } - ListElement { label: qsTr("Temp"); value: "temp" } + ListElement { label: qsTr("Desktop"); key: "desktop" } + ListElement { label: qsTr("Documents"); key: "document" } + ListElement { label: qsTr("Downloads"); key: "download" } + ListElement { label: qsTr("Pictures"); key: "picture" } + ListElement { label: qsTr("Temp"); key: "temp" } } onActivated: { var location = InteractionFilesystem.DESKTOP_LOCATION - if (model.get(currentIndex).value === "document") { + if (currentKey() === "document") { location = InteractionFilesystem.DOCUMENTS_LOCATION - } else if (model.get(currentIndex).value === "download") { + } else if (currentKey() === "download") { location = InteractionFilesystem.DOWNLOAD_LOCATION - } else if (model.get(currentIndex).value === "picture") { + } else if (currentKey() === "picture") { location = InteractionFilesystem.PICTURE_LOCATION - } else if (model.get(currentIndex).value === "temp") { + } else if (currentKey() === "temp") { location = InteractionFilesystem.TEMP_LOCATION } folderModel.folder = standardLocationUrl(location) } - } // ComboBox + } RowLayout { anchors { left: parent.left; right: parent.right; } spacing: formItemVerticalSpacing - Button { + AccessibleButton { id: upButton text: "<" - MouseArea { - anchors.fill: parent - onClicked: { - /* Navigate to parent folder. */ - if (folderModel.parentFolder !== "") { - folderModel.folder = folderModel.parentFolder - } + accessibleName: qsTr("Up") /* Needs to be specified as "<" is not read. */ + onClicked: { + /* Navigate to parent folder. */ + if (folderModel.parentFolder !== "") { + folderModel.folder = folderModel.parentFolder } } } - TextField { + AccessibleTextField { id: pathField Layout.fillWidth: true text: stripUrlPrefix(folderModel.folder) @@ -161,10 +173,13 @@ Dialog { } } } - } // TextField + } } // RowLayout - ListView { + ScrollableListView { id: fileList + + delegateHeight: imgHeightHeader + /* Fill remaining space with the list content. */ Layout.fillHeight: true Layout.fillWidth: true @@ -190,7 +205,7 @@ Dialog { (index === selectedFileIndex) ? datovkaPalette.highlight : datovkaPalette.window } width: fileList.width - height: imgHeightHeader + height: fileList.delegateHeight RowLayout { anchors.fill: parent Text { @@ -207,8 +222,21 @@ Dialog { } } MouseArea { - anchors.fill: parent - onClicked: { + function accessibleDescriptionText() { + var aText = ""; + if (fileIsDir) { + aText += qsTr("Open directory."); + } else { + if (isSelected(pathListModel, index) >= 0) { + aText += qsTr("File is selected.") + } else { + aText += qsTr("File is not selected.") + } + } + return aText; + } + + function handleClick() { if (fileIsDir) { /* Navigate to selected directory. */ folderModel.folder = fileURL @@ -233,6 +261,20 @@ Dialog { } } } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.description: accessibleDescriptionText() + Accessible.name: fileName + Accessible.onScrollDownAction: fileList.scrollDown() + Accessible.onScrollUpAction: fileList.scrollUp() + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } } } // Component diff --git a/qml/dialogues/InputDialogue.qml b/qml/dialogues/InputDialogue.qml index 9e6595700a7f9c9c0135d7222d11e6e5612d2cb1..c17fc2023a609a2235d91b66e01096a71c7c891d 100644 --- a/qml/dialogues/InputDialogue.qml +++ b/qml/dialogues/InputDialogue.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.1 +import cz.nic.mobileDatovka 1.0 /* * Input dialog for password or OTP code. @@ -44,16 +45,16 @@ Dialog { dPwdType = pwdType dUserName = userName root.title = title - roottext.text = text - rootpwd.clear() - rootpwd.placeholderText = placeholderText - rootpwd.echoMode = (hidePwd) ? TextInput.Password : TextInput.Normal + rootText.text = text + rootPwd.clear() + rootPwd.placeholderText = placeholderText + rootPwd.echoMode = (hidePwd) ? TextInput.Password : TextInput.Normal if (pwdType == "totp" || pwdType == "hotp") { - rootpwd.inputMethodHints = Qt.ImhPreferNumbers + rootPwd.inputMethodHints = Qt.ImhPreferNumbers } else { - rootpwd.inputMethodHints = Qt.ImhNone + rootPwd.inputMethodHints = Qt.ImhNone } - rootpwd.focus = true + rootPwd.focus = true root.open() } @@ -69,16 +70,16 @@ Dialog { standardButtons: Dialog.Ok | Dialog.Cancel contentItem: ColumnLayout { - Text { - id: roottext + AccessibleText { + id: rootText Layout.fillWidth: true Layout.minimumWidth: minimumInputSize text: "" Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline wrapMode: Text.WordWrap } - TextField { - id: rootpwd + AccessibleTextField { + id: rootPwd focus: true Layout.fillWidth: true Layout.minimumWidth: minimumInputSize @@ -90,6 +91,6 @@ Dialog { } } // ColumnLayout - onAccepted: finished(dIsdsAction, dPwdType, dUserName, rootpwd.text.toString()) + onAccepted: finished(dIsdsAction, dPwdType, dUserName, rootPwd.text.toString()) onRejected: canceled(dIsdsAction, dUserName) } diff --git a/qml/dialogues/MessageDialogue.qml b/qml/dialogues/MessageDialogue.qml index dc3920ced712fd54f99b822c71b5dd1573cf8abb..cab6c90997c018b9511598d298ca158dba31e0cd 100644 --- a/qml/dialogues/MessageDialogue.qml +++ b/qml/dialogues/MessageDialogue.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Dialogs 1.2 +import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.dialogues 1.0 /* @@ -72,7 +73,7 @@ Item { anchors.fill: parent - Text { /* Currently there is only a text information. */ + AccessibleText { /* Currently there is only a text information. */ id: importanceText objectName: "importanceText" @@ -91,7 +92,7 @@ Item { visible: dialogue.icon != Dialogues.NO_ICON } - Text { + AccessibleText { id: messageText objectName: "messageText" @@ -104,7 +105,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap } - Text { + AccessibleText { id: infoMessageText objectName: "infoMessageText" diff --git a/qml/dialogues/PasteInputDialogue.qml b/qml/dialogues/PasteInputDialogue.qml index 8b2a62dfdb6a03b7f677c298e0b048e45a656751..328ded177cf73d5123924474887ed6b96c8552c7 100644 --- a/qml/dialogues/PasteInputDialogue.qml +++ b/qml/dialogues/PasteInputDialogue.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -80,7 +80,7 @@ Item { anchors.fill: parent - Text { + AccessibleText { id: messageText objectName: "messageText" @@ -93,7 +93,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap } - TextField { + AccessibleTextField { id: textInput objectName: "textInput" diff --git a/qml/main.qml b/qml/main.qml index 5288222452cebf372f0296a9427c086346a55bcc..7a16be08790446a6e1ae8bc18b5b5adbe7b4528d 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -100,7 +100,6 @@ ApplicationWindow { property int acntListSpacing: defaultMargin * 4 property int formItemVerticalSpacing: defaultMargin property int formButtonHorizontalSpacing: defaultMargin * 5 - property real backMouseAreaRatio: 0.2 /* compare message delivery date with current date-90days */ property int deleteAfterDays: 90 @@ -182,7 +181,7 @@ ApplicationWindow { Column { anchors.centerIn: parent spacing: formItemVerticalSpacing - TextField { + AccessibleTextField { id: pinCodeInput height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -205,7 +204,7 @@ ApplicationWindow { } } } - Text { + AccessibleText { id: wrongPin font.bold: true visible: false @@ -213,7 +212,7 @@ ApplicationWindow { anchors.horizontalCenter: parent.horizontalCenter text: qsTr("Wrong PIN code!") } - Button { + AccessibleButton { text: qsTr("Enter") anchors.horizontalCenter: parent.horizontalCenter font.pointSize: defaultTextFont.font.pointSize @@ -228,14 +227,14 @@ ApplicationWindow { height: headerHeight anchors.horizontalCenter: parent.horizontalCenter } - Image{ + Image { id: datovkaLogo anchors.horizontalCenter: parent.horizontalCenter width: imgHeightHeader * 1.4 height: imgHeightHeader * 1.4 source: "qrc:/datovka.png" } - Text { + AccessibleText { id: versionLabel anchors.horizontalCenter: parent.horizontalCenter color: datovkaPalette.text diff --git a/qml/pages/PageAboutApp.qml b/qml/pages/PageAboutApp.qml index f0824a143028c5c940eab578cc53641f0ede5632..758b372a48b931772d2745e2f0b7b2bdc59aa64d 100644 --- a/qml/pages/PageAboutApp.qml +++ b/qml/pages/PageAboutApp.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,7 @@ import QtQuick 2.7 import QtQuick.Layouts 1.3 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.2 import cz.nic.mobileDatovka 1.0 Item { @@ -36,16 +36,10 @@ Item { PageHeader { id: headerBar title: qsTr("About Datovka") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } - } // PageHeader + } Flickable { id: flickable z: 0 @@ -54,6 +48,15 @@ Item { anchors.left: parent.left anchors.bottom: parent.bottom contentHeight: flickContent.implicitHeight + + /* + * TODO + * There are bugs in Qt preventing flicing on iOS when VoiceOver + * is activated: + * http://lists.qt-project.org/pipermail/accessibility/2015-March/000073.html + * https://bugreports.qt.io/browse/QTBUG-41980 + */ + Pane { id: flickContent anchors.fill: parent @@ -61,58 +64,33 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing * 2 - Text { - id: pageLabel - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - font.bold: true - color: datovkaPalette.text - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - text: qsTr("Datovka - free mobile Data-Box client") - } - Image{ + AccessibleImageButton { id: datovkaLogo anchors.horizontalCenter: parent.horizontalCenter width: imgHeightHeader * 1.4 height: imgHeightHeader * 1.4 source: "qrc:/datovka.png" - MouseArea { - anchors.fill: parent - onClicked: { - Qt.openUrlExternally("http://www.datovka.cz/") - } + accessibleName: qsTr("Open application home page.") + onClicked: { + Qt.openUrlExternally("http://www.datovka.cz/") } } - Text { - id: versionLabel - anchors.horizontalCenter: parent.horizontalCenter - font.bold: true - color: datovkaPalette.text - text: qsTr("Version") + ": " + settings.appVersion() - } - Text { - id: infoLabel01 + AccessibleText { + id: pageLabel anchors.horizontalCenter: parent.horizontalCenter - color: datovkaPalette.text width: parent.width - textFormat: TextEdit.RichText - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - text: qsTr("This application provides the means to access data boxes in the ISDS system. It enables you to download and to view the content of messages held within the data box. You can also, with some limitations, send messages from this application.") - } - Text { - id: infoLabel02 - anchors.horizontalCenter: parent.horizontalCenter + font.bold: true color: datovkaPalette.text - width: parent.width - textFormat: TextEdit.RichText horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap - text: qsTr("You may use this application only at your own risk. The association CZ.NIC is not the operator of the data box system. CZ.NIC is not liable for any damage that may be directly or indirectly caused by using this application.") + text: { + qsTr("Datovka - free mobile Data-Box client") + + "\n" + + qsTr("Version: %1").arg(settings.appVersion()) + } } - Text { - id: infoLabel03 + AccessibleText { + id: infoLabel anchors.horizontalCenter: parent.horizontalCenter color: datovkaPalette.text width: parent.width @@ -120,27 +98,31 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap onLinkActivated: Qt.openUrlExternally(link) - text: qsTr("If you have problems using this application or want help or you just want more information then start by reading through the information in the user manual or on the project web page.") - .arg("https://secure.nic.cz/files/datove_schranky/redirect/mobile-manual.html") - .arg("https://www.datovka.cz/cs/pages/mobilni-datovka.html") + text: { + qsTr("This application provides the means to access data boxes in the ISDS system. It enables you to download and to view the content of messages held within the data box. You can also, with some limitations, send messages from this application.") + + "

" + + qsTr("You may use this application only at your own risk. The association CZ.NIC is not the operator of the data box system. CZ.NIC is not liable for any damage that may be directly or indirectly caused by using this application.") + + "

" + + qsTr("If you have problems using this application or want help or you just want more information then start by reading through the information in the user manual or on the project web page.") + .arg("https://secure.nic.cz/files/datove_schranky/redirect/mobile-manual.html") + .arg("https://www.datovka.cz/cs/pages/mobilni-datovka.html") + } } - Text { + AccessibleText { id: cznicLabel anchors.horizontalCenter: parent.horizontalCenter font.bold: true color: datovkaPalette.text text: qsTr("Powered by") } - Image{ + AccessibleImageButton { id: cznicLogo anchors.horizontalCenter: parent.horizontalCenter sourceSize.height: imgHeightHeader source: "qrc:/cznic.png" - MouseArea { - anchors.fill: parent - onClicked: { - Qt.openUrlExternally("http://www.nic.cz/") - } + accessibleName: qsTr("Open home page of the CZ.NIC association.") + onClicked: { + Qt.openUrlExternally("http://www.nic.cz/") } } } // Column layout diff --git a/qml/pages/PageAccountDetail.qml b/qml/pages/PageAccountDetail.qml index dd001681188adb7b56d9b85bce9bbbe36458fd90..84ba6a7d901cf4c20424796007934bfcc2b310e5 100644 --- a/qml/pages/PageAccountDetail.qml +++ b/qml/pages/PageAccountDetail.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,33 +47,25 @@ Component { } Component.onDestruction: { - statusBar.visible = false + statusBar.visible = false } PageHeader { id: headerBar title: qsTr("Account info") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } - Image { + AccessibleImageButton { id: actionButton anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: defaultMargin sourceSize.height: imgHeightHeader source: "qrc:/ui/sync.svg" - MouseArea { - anchors.fill: parent - onClicked: { - isds.doIsdsAction("getAccountInfo", userName) - } + accessibleName: qsTr("Refresh information about the account.") + onClicked: { + isds.doIsdsAction("getAccountInfo", userName) } Connections { target: isds @@ -82,7 +74,8 @@ Component { } } } - } // PageHeader + } + Flickable { id: flickable z: 0 @@ -94,23 +87,25 @@ Component { Pane { id: flickContent anchors.fill: parent - Text { + AccessibleText { id: accountDetailText color: datovkaPalette.text width: parent.width textFormat: TextEdit.RichText wrapMode: Text.WordWrap } - } // Pane + } ScrollIndicator.vertical: ScrollIndicator {} - } // Flickable - Text { + } + + AccessibleText { id: emptyList visible: true color: datovkaPalette.text anchors.centerIn: parent text: qsTr("Account info has not been downloaded yet.") - } // Text + } + Connections { target: isds onDownloadAccountInfoFinishedSig: { diff --git a/qml/pages/PageAccountList.qml b/qml/pages/PageAccountList.qml index 3c8a0592b983a38073b517f23abc1390ea91a682..18f69a58b00a9daf1f8acf93a12bf808d5e2f189 100644 --- a/qml/pages/PageAccountList.qml +++ b/qml/pages/PageAccountList.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 +import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.messages 1.0 import cz.nic.mobileDatovka.models 1.0 @@ -77,21 +78,19 @@ Component { width: parent.width height: headerHeight color: mainHeaderBgColor - Image { + AccessibleImageButton { id: headerLogo anchors.left: parent.left anchors.leftMargin: defaultMargin anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/datovka@2x.png" - MouseArea { - anchors.fill: parent - onClicked: { - pageView.push(pageAboutApp, { - "pageView": pageView, - "statusBar": statusBar - }, StackView.Immediate) - } + accessibleName: qsTr("Show information about the application.") + onClicked: { + pageView.push(pageAboutApp, { + "pageView": pageView, + "statusBar": statusBar + }, StackView.Immediate) } } Row { @@ -99,58 +98,54 @@ Component { spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { + AccessibleImageButton { id: syncAllButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/sync-all.svg" - MouseArea { - anchors.fill: parent - onClicked: { - isds.doIsdsAction("syncAllAccounts", "") - } + accessibleName: qsTr("Synchronise all accounts.") + onClicked: { + isds.doIsdsAction("syncAllAccounts", "") } } - Image { + AccessibleImageButton { id: settingsButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/menu.svg" - MouseArea { - anchors.fill: parent - onClicked: { - pageView.push(pageMenuDatovkaSettings, { - "pageView": pageView, - "statusBar": statusBar, - "accountModel": accountModel - }, StackView.Immediate) - } + accessibleName: qsTr("Show application preferences.") + onClicked: { + pageView.push(pageMenuDatovkaSettings, { + "pageView": pageView, + "statusBar": statusBar, + "accountModel": accountModel + }, StackView.Immediate) } } } } - Text { + AccessibleTextButton { id: emptyList visible: false color: datovkaPalette.text anchors.centerIn: parent font.bold: true text: qsTr("Add a new account") - MouseArea { - anchors.fill: parent - onClicked: { - pageView.push(pageSettingsAccount, { - "pageView": pageView, - "statusBar": statusBar, - "acntName": "", - "userName": "", - "accountModel": accountModel - }, StackView.Immediate) - } + onClicked: { + pageView.push(pageSettingsAccount, { + "pageView": pageView, + "statusBar": statusBar, + "acntName": "", + "userName": "", + "accountModel": accountModel + }, StackView.Immediate) } } - ListView { + ScrollableListView { id: accountList + + delegateHeight: headerHeight * 3 + anchors.top: datovkaHeader.bottom anchors.bottom: parent.bottom clip: true @@ -176,7 +171,7 @@ Component { delegate: Item { id: accountItem width: parent.width - height: headerHeight * 3 + height: accountList.delegateHeight ColumnLayout { spacing: 0 width: parent.width @@ -234,8 +229,7 @@ Component { height: parent.height color: "transparent" MouseArea { - anchors.fill: parent - onClicked: { + function handleClick() { statusBar.visible = false pageView.push(pageMenuAccount, { "pageView": pageView, @@ -245,20 +239,31 @@ Component { "accountModel": accountModel }, StackView.Immediate) } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: !rTestAccount ? qsTr("Account '%1'.").arg(rAcntName) : qsTr("Testing account '%1'.").arg(rAcntName) + Accessible.onScrollDownAction: accountList.scrollDown() + Accessible.onScrollUpAction: accountList.scrollUp() + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } } - Image { + AccessibleImageButton { id: syncThisButton anchors.right: parent.right anchors.rightMargin: defaultMargin anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/sync.svg" - MouseArea { - anchors.fill: parent - onClicked: { - isds.doIsdsAction("syncOneAccount", rUserName) - } + accessibleName: qsTr("Synchronise account '%1'.").arg(rAcntName) + onClicked: { + isds.doIsdsAction("syncOneAccount", rUserName) } Connections { target: isds @@ -330,8 +335,7 @@ Component { } } MouseArea { - anchors.fill: parent - onClicked: { + function handleClick() { statusBar.visible = false pageView.push(pageMessageList, { "pageView": pageView, @@ -342,6 +346,19 @@ Component { "accountModel": accountModel }) } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Received messages of account '%1'. (Unread %2 of %3 received messages.)").arg(rAcntName).arg(rRcvdUnread).arg(rRcvdTotal) + Accessible.onScrollDownAction: accountList.scrollDown() + Accessible.onScrollUpAction: accountList.scrollUp() + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } Rectangle { anchors.bottom: parent.bottom @@ -399,8 +416,7 @@ Component { } } MouseArea { - anchors.fill: parent - onClicked: { + function handleClick() { statusBar.visible = false pageView.push(pageMessageList, { "pageView": pageView, @@ -411,6 +427,19 @@ Component { "accountModel": accountModel }) } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Sent messages of account '%1'. (Total of %2 sent messages.)").arg(rAcntName).arg(rSntTotal) + Accessible.onScrollDownAction: accountList.scrollDown() + Accessible.onScrollUpAction: accountList.scrollUp() + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } Rectangle { anchors.bottom: parent.bottom diff --git a/qml/pages/PageChangePassword.qml b/qml/pages/PageChangePassword.qml index dc3f5bff68090a9b2374e9166d9db0d16da67182..bc73afd171002bdcd6c07ecfa6fed451a7046d80 100644 --- a/qml/pages/PageChangePassword.qml +++ b/qml/pages/PageChangePassword.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -61,58 +61,50 @@ Item { PageHeader { id: headerBar title: qsTr("Change password") + ": " + userName - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } Row { anchors.verticalCenter: parent.verticalCenter spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { + AccessibleImageButton { id: actionButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/checkbox-marked-circle.svg" - MouseArea { - anchors.fill: parent - onClicked: { - if (oldPwd.text == "" || newPwd.text == "") { - errLineText.text = qsTr("Old password and new password must be filled!") - errLineText.visible = true - } else { - if (newPwd.text == newPwdAgain.text) { - if (isds.isCorrectPassword(newPwd.text.toString())) { - if (loginMethod == "totp" && otpCode.text == "") { - errLineText.text = qsTr("SMS code must be filled!") - errLineText.visible = true - } else if (loginMethod == "hotp" && otpCode.text == "") { - errLineText.text = qsTr("Security code must be filled!") - errLineText.visible = true + accessibleName: qsTr("Accept changes") + onClicked: { + if (oldPwd.text == "" || newPwd.text == "") { + errLineText.text = qsTr("Old password and new password must be filled!") + errLineText.visible = true + } else { + if (newPwd.text == newPwdAgain.text) { + if (isds.isCorrectPassword(newPwd.text.toString())) { + if (loginMethod == "totp" && otpCode.text == "") { + errLineText.text = qsTr("SMS code must be filled!") + errLineText.visible = true + } else if (loginMethod == "hotp" && otpCode.text == "") { + errLineText.text = qsTr("Security code must be filled!") + errLineText.visible = true + } else { + errLineText.visible = false + if (isds.changePassword(userName, oldPwd.text.toString(), newPwd.text.toString(), otpCode.text.toString())) { + settings.saveAllSettings(accountModel) + pageView.pop(StackView.Immediate) } else { - errLineText.visible = false - if (isds.changePassword(userName, oldPwd.text.toString(), newPwd.text.toString(), otpCode.text.toString())) { - settings.saveAllSettings(accountModel) - pageView.pop(StackView.Immediate) - } else { - errLineText.text = qsTr("Password change failed!") - errLineText.visible = true - } + errLineText.text = qsTr("Password change failed!") + errLineText.visible = true } - } else { - errLineText.text = qsTr("Wrong password format! The new password must contain at least 8 characters including at least 1 number and at least 1 upper-case letter.") - errLineText.visible = true - } + } } else { - errLineText.text = qsTr("The entered new passwords do not match!") + errLineText.text = qsTr("Wrong password format! The new password must contain at least 8 characters including at least 1 number and at least 1 upper-case letter.") errLineText.visible = true } + } else { + errLineText.text = qsTr("The entered new passwords do not match!") + errLineText.visible = true } } } @@ -134,7 +126,7 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - Text { + AccessibleText { id: topLineText anchors.horizontalCenter: parent.horizontalCenter color: datovkaPalette.mid @@ -146,7 +138,7 @@ Item { Column { anchors.horizontalCenter: parent.horizontalCenter spacing: formItemVerticalSpacing - TextField { + AccessibleTextField { id: oldPwd // Fit widest element. anchors.left: parent.left @@ -160,7 +152,7 @@ Item { horizontalAlignment: TextInput.AlignHCenter text: "" } - TextField { + AccessibleTextField { id: newPwd // Fit widest element. anchors.left: parent.left @@ -174,7 +166,7 @@ Item { horizontalAlignment: TextInput.AlignHCenter text: "" } - TextField { + AccessibleTextField { id: newPwdAgain // Widest element. anchors.horizontalCenter: parent.horizontalCenter @@ -187,7 +179,7 @@ Item { horizontalAlignment: TextInput.AlignHCenter text: "" } - Text { + AccessibleText { id: errLineText visible: false anchors.horizontalCenter: parent.horizontalCenter @@ -198,7 +190,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap } - Button { + AccessibleButton { id: sendSmsButton visible: false enabled: false @@ -227,7 +219,7 @@ Item { } } } - TextField { + AccessibleTextField { id: otpCode visible: false anchors.left: parent.left diff --git a/qml/pages/PageContactList.qml b/qml/pages/PageContactList.qml index 7e981b661da0c46581d1a66fb0d3f925c6c71952..a0a8ac5c6b20f2a354ae0255554754a74f2c9ed0 100644 --- a/qml/pages/PageContactList.qml +++ b/qml/pages/PageContactList.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,7 +46,7 @@ Item { messages.fillContactList(foundBoxModel, userName, currentBoxId) if (foundBoxModel.rowCount() === 0) { emptyList.visible = true - searchButton.visible = false + filterButton.visible = false } if (recipBoxModel != null) { foundBoxModel.selectEntries(recipBoxModel.boxIds(), true) @@ -73,90 +73,56 @@ Item { PageHeader { id: headerBar title: qsTr("Contacts") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } Row { anchors.verticalCenter: parent.verticalCenter spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { - id: searchButton + AccessibleImageButton { + id: filterButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/magnify.svg" - MouseArea { - anchors.fill: parent - onClicked: { - databoxList.anchors.top = filterBar.bottom - filterBar.visible = true - filter.forceActiveFocus() - Qt.inputMethod.show() - } + accessibleName: qsTr("Filter data boxes.") + onClicked: { + databoxList.anchors.top = filterBar.bottom + filterBar.visible = true + filterBar.filterField.forceActiveFocus() + Qt.inputMethod.show() } } } } // PageHeader - Rectangle { + FilterBar { id: filterBar visible: false z: 1 anchors.top: headerBar.bottom width: parent.width - height: filter.height - color: (filter.text.length == 0) ? datovkaPalette.alternateBase : + height: filterField.height + color: (filterField.text.length == 0) ? datovkaPalette.alternateBase : (databoxList.count > 0) ? "#afffaf" : "#ffafaf" - border.color: filter.activeFocus ? "#0066ff" : "#bdbebf" - TextField { - id: filter - anchors.top: parent.top - width: parent.width - close.width - placeholderText: qsTr("Set filter") - font.pointSize: defaultTextFont.font.pointSize - inputMethodHints: Qt.ImhNoPredictiveText - /* - * Using explicit top, left, ... padding because plain padding - * does not centre the text properly. - */ - topPadding: 8.0 - bottomPadding: 8.0 - leftPadding: 8.0 - rightPadding: 8.0 - /* TODO - remove background if used materials */ - background: Rectangle { - anchors.fill: parent - color: "transparent" - border.color: "transparent" - } - onTextChanged: { - proxyDataboxModel.setFilterRegExpStr(filter.text) - } + border.color: filterField.activeFocus ? "#0066ff" : "#bdbebf" + + placeholderText: qsTr("Set filter") + fontPointSize: defaultTextFont.font.pointSize + buttonImageHeight: imgHeight + buttonAccessibleName: qsTr("Clear and hide filter field") + + onTextChanged: { + proxyDataboxModel.setFilterRegExpStr(text) } - Image { - id: close - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: imgHeight - source: "qrc:/ui/remove.svg" - MouseArea { - anchors.fill: parent - onClicked: { - filterBar.visible = false - filter.clear() - databoxList.anchors.top = headerBar.bottom - Qt.inputMethod.hide() - } - } + + onClearClicked: { + filterBar.visible = false + databoxList.anchors.top = headerBar.bottom + Qt.inputMethod.hide() } - } // Rectangle - Text { + } + AccessibleText { id: emptyList visible: false color: datovkaPalette.text diff --git a/qml/pages/PageDataboxDetail.qml b/qml/pages/PageDataboxDetail.qml index 649c424c454e76c853132f1cb9f4d7d1ab0b4784..5e1d90bc07e35a775ffd64b9d914ac918a4d45c0 100644 --- a/qml/pages/PageDataboxDetail.qml +++ b/qml/pages/PageDataboxDetail.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,25 +40,15 @@ Component { Component.onCompleted: { isds.doIsdsAction("findDatabox", userName) } - Connections { - target: isds - onRunFindDataboxSig: { - databoxDetailText.text = isds.findDatabox(userName, dbID, dbType) - } - } + PageHeader { id: headerBar title: qsTr("Databox info") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } - } // PageHeader + } + Flickable { id: flickable z: 0 @@ -70,15 +60,22 @@ Component { Pane { id: flickContent anchors.fill: parent - Text { + AccessibleText { id: databoxDetailText color: datovkaPalette.text width: parent.width textFormat: TextEdit.RichText wrapMode: Text.WordWrap } - } // Pane + } ScrollIndicator.vertical: ScrollIndicator {} - } // Flickable + } + + Connections { + target: isds + onRunFindDataboxSig: { + databoxDetailText.text = isds.findDatabox(userName, dbID, dbType) + } + } } } diff --git a/qml/pages/PageDataboxSearch.qml b/qml/pages/PageDataboxSearch.qml index 222b97ea89ab30ebb4ab5bec5404762e1bba342e..06312f9777353c1f7394f373091fe3de5fcc70f7 100644 --- a/qml/pages/PageDataboxSearch.qml +++ b/qml/pages/PageDataboxSearch.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,47 +78,37 @@ Item { PageHeader { id: headerBar title: qsTr("Find databox") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } Row { anchors.verticalCenter: parent.verticalCenter spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { - id: searchButton + AccessibleImageButton { + id: filterButton visible: false anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/magnify.svg" - MouseArea { - anchors.fill: parent - onClicked: { - databoxList.anchors.top = filterBar.bottom - filterBar.visible = true - filter.forceActiveFocus() - Qt.inputMethod.show() - } + accessibleName: qsTr("Filter data boxes.") + onClicked: { + databoxList.anchors.top = filterBar.bottom + filterBar.visible = true + filterBar.filterField.forceActiveFocus() + Qt.inputMethod.show() } } - Image { + AccessibleImageButton { id: actionButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/account-search.svg" - MouseArea { - anchors.fill: parent - onClicked: { - searchPhraseText.focus = false - searchPanel.doSearchRequest() - } + accessibleName: qsTr("Search data boxes.") + onClicked: { + searchPhraseText.focus = false + searchPanel.doSearchRequest() } } } @@ -134,66 +124,36 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - ComboBox { + AccessibleComboBox { id: searchScopeComboBox - currentIndex: 0 width: parent.width - height: inputItemHeight - textRole: "label" - font.pointSize: defaultTextFont.font.pointSize - delegate: ItemDelegate { - width: searchScopeComboBox.width - height: inputItemHeight - contentItem: Text { - text: label - color: datovkaPalette.text - font.pointSize: defaultTextFont.font.pointSize - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - } - highlighted: searchScopeComboBox.highlightedIndex == index - } + accessibleDescription: qsTr("Select type of sought data box") model: ListModel { id: searchScopeComboBoxModel - ListElement { label: qsTr("All databoxes"); value: "ALL" } - ListElement { label: qsTr("OVM"); value: "OVM" } - ListElement { label: qsTr("PO"); value: "PO" } - ListElement { label: qsTr("PFO"); value: "PFO" } - ListElement { label: qsTr("FO"); value: "FO" } + ListElement { label: qsTr("All databoxes"); key: "ALL" } + ListElement { label: qsTr("OVM"); key: "OVM" } + ListElement { label: qsTr("PO"); key: "PO" } + ListElement { label: qsTr("PFO"); key: "PFO" } + ListElement { label: qsTr("FO"); key: "FO" } } - onCurrentIndexChanged: searchScope = searchScopeComboBoxModel.get(currentIndex).value + onCurrentIndexChanged: searchScope = currentKey() } - ComboBox { + AccessibleComboBox { id: searchTypeComboBox - currentIndex: 0 width: parent.width - height: inputItemHeight - textRole: "label" - font.pointSize: defaultTextFont.font.pointSize - delegate: ItemDelegate { - width: searchTypeComboBox.width - height: inputItemHeight - contentItem: Text { - text: label - color: datovkaPalette.text - font.pointSize: defaultTextFont.font.pointSize - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - } - highlighted: searchTypeComboBox.highlightedIndex == index - } + accessibleDescription: qsTr("Select entries to search in") model: ListModel { id: searchTypeComboBoxModel - ListElement { label: qsTr("All fields"); value: "GENERAL" } - ListElement { label: qsTr("Address"); value: "ADDRESS" } - ListElement { label: qsTr("IČ"); value: "ICO" } - ListElement { label: qsTr("Databox ID"); value: "DBID" } + ListElement { label: qsTr("All fields"); key: "GENERAL" } + ListElement { label: qsTr("Address"); key: "ADDRESS" } + ListElement { label: qsTr("IČ"); key: "ICO" } + ListElement { label: qsTr("Databox ID"); key: "DBID" } } - onCurrentIndexChanged: searchType = searchTypeComboBoxModel.get(currentIndex).value + onCurrentIndexChanged: searchType = currentKey() } - TextField { + AccessibleTextField { id: searchPhraseText - placeholderText: qsTr("Enter phrase") + placeholderText: qsTr("Enter sought phrase") focus: true width: parent.width font.pointSize: defaultTextFont.font.pointSize @@ -237,8 +197,8 @@ Item { color: enabled ? headerColor : datovkaPalette.mid } MouseArea { - anchors.fill: parent - onClicked: { + /* TODO -- Make this a separate component (image button?). */ + function handleClick() { if (page > 0) { page = page-1 } @@ -247,9 +207,20 @@ Item { foundBoxModel.selectEntries(recipBoxModel.boxIds(), true) } } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Previous") + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } } - Text { + AccessibleText { id: searchResultText color: datovkaPalette.text text: "" @@ -276,72 +247,57 @@ Item { color: enabled ? headerColor : datovkaPalette.mid } MouseArea { - anchors.fill: parent - onClicked: { + /* TODO -- Make this a separate component (image button?). */ + function handleClick() { page = page+1 isds.findDataboxFulltext(userName, foundBoxModel, searchTextTmp, searchTypeTmp, searchScopeTmp, page) if (recipBoxModel != null) { foundBoxModel.selectEntries(recipBoxModel.boxIds(), true) } } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Next") + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } } // Rectangle } // Item } // Column } // Pane - Rectangle { + FilterBar { id: filterBar visible: false z: 1 anchors.top: searchPanel.bottom width: parent.width - height: filter.height - color: (filter.text.length == 0) ? datovkaPalette.alternateBase : + height: filterField.height + color: (filterField.text.length == 0) ? datovkaPalette.alternateBase : (databoxList.count > 0) ? "#afffaf" : "#ffafaf" - border.color: filter.activeFocus ? "#0066ff" : "#bdbebf" - TextField { - id: filter - anchors.top: parent.top - width: parent.width - close.width - placeholderText: qsTr("Set filter") - font.pointSize: defaultTextFont.font.pointSize - inputMethodHints: Qt.ImhNoPredictiveText - /* - * Using explicit top, left, ... padding because plain padding - * does not centre the text properly. - */ - topPadding: 8.0 - bottomPadding: 8.0 - leftPadding: 8.0 - rightPadding: 8.0 - /* TODO - remove background if used materials */ - background: Rectangle { - anchors.fill: parent - color: "transparent" - border.color: "transparent" - } - onTextChanged: { - proxyDataboxModel.setFilterRegExpStr(filter.text) - } + border.color: filterField.activeFocus ? "#0066ff" : "#bdbebf" + + placeholderText: qsTr("Set filter") + fontPointSize: defaultTextFont.font.pointSize + buttonImageHeight: imgHeight + buttonAccessibleName: qsTr("Clear and hide filter field") + + onTextChanged: { + proxyDataboxModel.setFilterRegExpStr(text) } - Image { - id: close - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: imgHeight - source: "qrc:/ui/remove.svg" - MouseArea { - anchors.fill: parent - onClicked: { - filterBar.visible = false - filter.clear() - databoxList.anchors.top = searchPanel.bottom - Qt.inputMethod.hide() - } - } + + onClearClicked: { + filterBar.visible = false + databoxList.anchors.top = searchPanel.bottom + Qt.inputMethod.hide() } - } // Rectangle - Text { + } + AccessibleText { id: emptyList visible: false color: datovkaPalette.text @@ -409,9 +365,10 @@ Item { isds.findDataboxFulltext(userName, foundBoxModel, searchPhraseText.text, searchType, searchScope, page) if (foundBoxModel.rowCount() <= 0) { emptyList.visible = true - searchButton.visible = false + filterButton.visible = false } else { - searchButton.visible = true + emptyList.visible = false + filterButton.visible = true } if (recipBoxModel != null) { foundBoxModel.selectEntries(recipBoxModel.boxIds(), true) diff --git a/qml/pages/PageMenuAccount.qml b/qml/pages/PageMenuAccount.qml index 5c3f3907883b9ee403d33c8bfc3fa4cfd01b3c10..2ecba80c1a507826fb2857bc938831b1ebd68b8a 100644 --- a/qml/pages/PageMenuAccount.qml +++ b/qml/pages/PageMenuAccount.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 import cz.nic.mobileDatovka 1.0 Component { @@ -41,164 +40,120 @@ Component { PageHeader { id: headerBar title: qsTr("Account properties") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { + onBackClicked: { + pageView.pop(StackView.Immediate) + } + } + + /* Object (associative array) holding functions. */ + property var funcs: { + "accSett": function callAccSett() { + pageView.replace(pageSettingsAccount, { + "pageView": pageView, + "statusBar": statusBar, + "acntName": acntName, + "userName": userName, + "accountModel": accountModel + }, StackView.Immediate) + }, + "viewAcntInfo": function callViewAcntInfo() { + pageView.replace(pageAccountDetail, { + "pageView": pageView, + "statusBar": statusBar, + "acntName": acntName, + "userName": userName + }, StackView.Immediate) + }, + "createMsg": function callCreateMsg() { + pageView.replace(pageSendMessage, { + "pageView": pageView, + "statusBar": statusBar, + "acntName" : acntName, + "userName": userName, + "action": "new" + }, StackView.Immediate) + }, + "findBox": function callFindBox() { + pageView.replace(pageDataboxSearch, { + "pageView": pageView, + "statusBar": statusBar, + "userName": userName, + }, StackView.Immediate) + }, + "cleanDb": function callCleanDb() { + files.deleteFileDb(userName) + }, + "changePwd": function callChangePwd() { + pageView.replace(pageChangePassword, { + "pageView": pageView, + "statusBar": statusBar, + "acntName": acntName, + "userName": userName, + "accountModel": accountModel + }, StackView.Immediate) + }, + "delAcnt": function callDelAcnt() { + if (accounts.removeAccount(accountModel, userName, true)) { + settings.saveAllSettings(accountModel) pageView.pop(StackView.Immediate) } } } + ListModel { id: accountMenuListModel ListElement { - index: 0 - name: qsTr("Account settings") image: "qrc:/ui/settings.svg" + showEntry: true showNext: true + name: qsTr("Account settings") + funcName: "accSett" } ListElement { - index: 1 - name: qsTr("View account info") image: "qrc:/ui/account-box.svg" + showEntry: true showNext: true + name: qsTr("View account info") + funcName: "viewAcntInfo" } ListElement { - index: 6 - name: qsTr("Create message") image: "qrc:/ui/pencil-box-outline.svg" + showEntry: true showNext: true + name: qsTr("Create message") + funcName: "createMsg" } ListElement { - index: 5 - name: qsTr("Find databox") image: "qrc:/ui/account-search.svg" + showEntry: true showNext: true + name: qsTr("Find databox") + funcName: "findBox" } ListElement { - index: 2 - name: qsTr("Clean up the file database") image: "qrc:/ui/database.svg" + showEntry: true showNext: false + name: qsTr("Clean up the file database") + funcName: "cleanDb" } ListElement { - index: 3 - name: qsTr("Change password") image: "qrc:/ui/account-key.svg" + showEntry: true showNext: false + name: qsTr("Change password") + funcName: "changePwd" } ListElement { - index: 4 - name: qsTr("Delete account") image: "qrc:/ui/account-remove.svg" + showEntry: true showNext: false + name: qsTr("Delete account") + funcName: "delAcnt" } } - Component { - id: accountMenuComponent - Rectangle { - color: datovkaPalette.base - height: headerHeight - width: parent.width - Image { - id: menuImage - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: defaultMargin - sourceSize.height: imgHeight - source: image - } - ColorOverlay { - anchors.fill: menuImage - source: menuImage - color: datovkaPalette.text - } - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: menuImage.right - anchors.leftMargin: defaultMargin - color: datovkaPalette.text - text: name - } - Rectangle { - id: next2 - visible: showNext - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - height: parent.height - width: parent.width * 0.07 - color: parent.color - Image { - id: nextImage2 - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: defaultMargin - sourceSize.height: navImgHeight - source: "qrc:/ui/next.svg" - } - ColorOverlay { - anchors.fill: nextImage2 - source: nextImage2 - color: datovkaPalette.text - } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (index == 0) { - pageView.replace(pageSettingsAccount, { - "pageView": pageView, - "statusBar": statusBar, - "acntName": acntName, - "userName": userName, - "accountModel": accountModel - }, StackView.Immediate) - } else if (index == 1) { - pageView.replace(pageAccountDetail, { - "pageView": pageView, - "statusBar": statusBar, - "acntName": acntName, - "userName": userName - }, StackView.Immediate) - } else if (index == 2) { - files.deleteFileDb(userName) - } else if (index == 3) { - pageView.replace(pageChangePassword, { - "pageView": pageView, - "statusBar": statusBar, - "acntName": acntName, - "userName": userName, - "accountModel": accountModel - }, StackView.Immediate) - } else if (index == 4) { - if (accounts.removeAccount(accountModel, userName, true)) { - settings.saveAllSettings(accountModel) - pageView.pop(StackView.Immediate) - } - } else if (index == 5) { - pageView.replace(pageDataboxSearch, { - "pageView": pageView, - "statusBar": statusBar, - "userName": userName, - }, StackView.Immediate) - } else if (index == 6) { - pageView.replace(pageSendMessage, { - "pageView": pageView, - "statusBar": statusBar, - "acntName" : acntName, - "userName": userName, - "action": "new" - }, StackView.Immediate) - } else { - pageView.pop(StackView.Immediate) - } - } - } - } - } - ListView { + + AccessibleMenu { id: accountMenuList anchors.top: headerBar.bottom anchors.bottom: parent.bottom @@ -206,8 +161,9 @@ Component { clip: true spacing: 1 opacity: 1 + + funcArr: funcs model: accountMenuListModel - delegate: accountMenuComponent } } } diff --git a/qml/pages/PageMenuDatovkaSettings.qml b/qml/pages/PageMenuDatovkaSettings.qml index 4b3a1b2f3c9a4d7e8a7543a1248672fa2d95f76a..8a078696c6859449e7b51d7a82c751ff617b5afe 100644 --- a/qml/pages/PageMenuDatovkaSettings.qml +++ b/qml/pages/PageMenuDatovkaSettings.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 import cz.nic.mobileDatovka 1.0 Component { @@ -40,175 +39,133 @@ Component { if (accountModel.rowCount() <= 0) { // Hide some menu items // Note: remove argument is ListModel item index - settingsListModel.remove(1) + settingsMenuListModel.remove(1) } } PageHeader { id: headerBar title: qsTr("Settings") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } } + + /* Object (associative array) holding functions. */ + property var funcs: { + "addAcnt": function callAddAcnt() { + pageView.replace(pageSettingsAccount, { + "pageView": pageView, + "statusBar": statusBar, + "acntName": "", + "userName": "", + "accountModel": accountModel + }, StackView.Immediate) + }, + "searchMsg": function callSearchMsg() { + pageView.replace(pageMessageSearch, { + "pageView": pageView, + "statusBar": statusBar + }, StackView.Immediate) + }, + "settGeneral": function callSettGeneral() { + pageView.replace(pageSettingsGeneral, { + "pageView": pageView, + "statusBar": statusBar, + "accountModel": accountModel + }, StackView.Immediate) + }, + "settSync": function callSettSync() { + pageView.replace(pageSettingsSync, { + "pageView": pageView, + "statusBar": statusBar, + "accountModel": accountModel + }, StackView.Immediate) + }, + "settStorage": function callSettStorage() { + pageView.replace(pageSettingsStorage, { + "pageView": pageView, + "statusBar": statusBar, + "accountModel": accountModel + }, StackView.Immediate) + }, + "settSecPin": function callSettSecPin() { + pageView.replace(pageSettingsPin, { + "pageView": pageView, + "statusBar": statusBar, + "accountModel": accountModel + }, StackView.Immediate) + }, + "userGuide": function callUserGuide() { + Qt.openUrlExternally("https://secure.nic.cz/files/datove_schranky/redirect/mobile-manual.html") + pageView.pop(StackView.Immediate) + } + } + ListModel { - id: settingsListModel + id: settingsMenuListModel ListElement { - index: 0 - name: qsTr("Add account") image: "qrc:/ui/account-plus.svg" + showEntry: true showNext: true + name: qsTr("Add account") + funcName: "addAcnt" } ListElement { - index: 1 - name: qsTr("Search message") image: "qrc:/ui/magnify.svg" + showEntry: true showNext: true + name: qsTr("Search message") + funcName: "searchMsg" } ListElement { - index: 2 - name: qsTr("General") image: "qrc:/ui/settings.svg" + showEntry: true showNext: true + name: qsTr("General") + funcName: "settGeneral" } ListElement { - index: 3 - name: qsTr("Synchronization") image: "qrc:/ui/sync-all.svg" + showEntry: true showNext: true + name: qsTr("Synchronization") + funcName: "settSync" } ListElement { - index: 4 - name: qsTr("Storage") image: "qrc:/ui/database.svg" + showEntry: true showNext: true + name: qsTr("Storage") + funcName: "settStorage" } ListElement { - index: 5 - name: qsTr("Security and PIN") image: "qrc:/ui/key-variant.svg" + showEntry: true showNext: true + name: qsTr("Security and PIN") + funcName: "settSecPin" } ListElement { - index: 6 - name: qsTr("User Guide") image: "qrc:/ui/information.svg" + showEntry: true showNext: false + name: qsTr("User Guide") + funcName: "userGuide" } } - Component { - id: settingsComponent - Rectangle { - color: datovkaPalette.base - height: headerHeight - width: parent.width - Image { - id: msgSentImage - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: defaultMargin - sourceSize.height: imgHeight - source: image - } - ColorOverlay { - anchors.fill: msgSentImage - source: msgSentImage - color: datovkaPalette.text - } - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: msgSentImage.right - anchors.leftMargin: defaultMargin - color: datovkaPalette.text - text: name - } - Rectangle { - visible: showNext - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - height: parent.height - width: parent.width * 0.07 - color: parent.color - Image { - id: nextImage - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: defaultMargin - sourceSize.height: navImgHeight - source: "qrc:/ui/next.svg" - } - ColorOverlay { - anchors.fill: nextImage - source: nextImage - color: datovkaPalette.text - } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (index == 0) { - pageView.replace(pageSettingsAccount, { - "pageView": pageView, - "statusBar": statusBar, - "acntName": "", - "userName": "", - "accountModel": accountModel - }, StackView.Immediate) - } else if (index == 1) { - pageView.replace(pageMessageSearch, { - "pageView": pageView, - "statusBar": statusBar - }, StackView.Immediate) - } else if (index == 2) { - pageView.replace(pageSettingsGeneral, { - "pageView": pageView, - "statusBar": statusBar, - "accountModel": accountModel - }, StackView.Immediate) - } else if (index == 3) { - pageView.replace(pageSettingsSync, { - "pageView": pageView, - "statusBar": statusBar, - "accountModel": accountModel - }, StackView.Immediate) - } else if (index == 4) { - pageView.replace(pageSettingsStorage, { - "pageView": pageView, - "statusBar": statusBar, - "accountModel": accountModel - }, StackView.Immediate) - } else if (index == 5) { - pageView.replace(pageSettingsPin, { - "pageView": pageView, - "statusBar": statusBar, - "accountModel": accountModel - }, StackView.Immediate) - } else if (index == 6) { - Qt.openUrlExternally("https://secure.nic.cz/files/datove_schranky/redirect/mobile-manual.html") - pageView.pop(StackView.Immediate) - } else { - pageView.pop(StackView.Immediate) - } - } - } - } - } - ListView { - id: settingsList + + AccessibleMenu { + id: settingsMenuList anchors.top: headerBar.bottom anchors.bottom: parent.bottom width: parent.width clip: true spacing: 1 opacity: 1 - model: settingsListModel - delegate: settingsComponent + + funcArr: funcs + model: settingsMenuListModel } } } diff --git a/qml/pages/PageMenuMessage.qml b/qml/pages/PageMenuMessage.qml index bb6b30556c9d13c2843e5aa9cb3b9832d37ded8a..77158f4c0a0d45f8598d9e879bddc64c164eacdd 100644 --- a/qml/pages/PageMenuMessage.qml +++ b/qml/pages/PageMenuMessage.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.messages 1.0 import cz.nic.mobileDatovka.models 1.0 @@ -45,119 +44,73 @@ Component { property var messageModel: null Component.onCompleted: { - messageMenuListModel.setProperty(3, "showIt", canDeleteMsg) + var index = messageMenuList.get_model_index(messageMenuListModel, "funcName", "delMsg"); + if (index >= 0) { + messageMenuListModel.setProperty(index, "showEntry", canDeleteMsg) + } } + PageHeader { id: headerBar - title: qsTr("Message") + ": " + msgId - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + title: qsTr("Message") + ": " + msgId + onBackClicked: { + pageView.pop(StackView.Immediate) + } + } + + /* Object (associative array) holding functions. */ + property var funcs: { + "downlAttch": function callDownlAttch() { + isds.doIsdsAction("downloadMessage", userName) + pageView.pop(StackView.Immediate) + }, + "markRead": function callMarkRead() { + messages.markMessageAsLocallyRead(messageModel, userName, msgId, true) + pageView.pop(StackView.Immediate) + }, + "markUnread": function callMarkUnread() { + messages.markMessageAsLocallyRead(messageModel, userName, msgId, false) + pageView.pop(StackView.Immediate) + }, + "delMsg": function callDelMsg() { + messages.deleteMessageFromDbs(accountModel, messageModel, userName, msgId) + pageView.pop(StackView.Immediate) } } + ListModel { id: messageMenuListModel ListElement { - index: 0 - showIt: true - name: qsTr("Download attachments") image: "qrc:/ui/datovka-email-download.svg" + showEntry: true showNext: false + name: qsTr("Download attachments") + funcName: "downlAttch" } ListElement { - index: 1 - showIt: true - name: qsTr("Mark as read") image: "qrc:/ui/email-open-outline.svg" + showEntry: true showNext: false + name: qsTr("Mark as read") + funcName: "markRead" } ListElement { - index: 2 - showIt: true - name: qsTr("Mark as unread") image: "qrc:/ui/email-outline.svg" + showEntry: true showNext: false + name: qsTr("Mark as unread") + funcName: "markUnread" } ListElement { - index: 3 - showIt: true - name: qsTr("Delete message") image: "qrc:/ui/delete.svg" + showEntry: true showNext: false + name: qsTr("Delete message") + funcName: "delMsg" } } - Component { - id: messageMenuComponent - Rectangle { - visible: showIt - color: datovkaPalette.base - height: headerHeight - width: parent.width - Image { - id: menuImage - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: defaultMargin - sourceSize.height: imgHeight - source: image - } - ColorOverlay { - anchors.fill: menuImage - source: menuImage - color: datovkaPalette.text - } - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: menuImage.right - anchors.leftMargin: defaultMargin - color: datovkaPalette.text - text: name - } - Rectangle { - id: next2 - visible: showNext - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - height: parent.height - width: parent.width * 0.07 - color: parent.color - Image { - id: nextImage2 - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: defaultMargin - sourceSize.height: navImgHeight - source: "qrc:/ui/next.svg" - } - ColorOverlay { - anchors.fill: nextImage2 - source: nextImage2 - color: datovkaPalette.text - } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (index == 0) { - isds.doIsdsAction("downloadMessage", userName) - } else if (index == 1) { - messages.markMessageAsLocallyRead(messageModel, userName, msgId, true) - } else if (index == 2) { - messages.markMessageAsLocallyRead(messageModel, userName, msgId, false) - } else if (index == 3) { - messages.deleteMessageFromDbs(accountModel, messageModel, userName, msgId) - } - pageView.pop(StackView.Immediate) - } - } - } - } - ListView { + + AccessibleMenu { id: messageMenuList anchors.top: headerBar.bottom anchors.bottom: parent.bottom @@ -165,9 +118,11 @@ Component { clip: true spacing: 1 opacity: 1 + + funcArr: funcs model: messageMenuListModel - delegate: messageMenuComponent } + Connections { target: isds onRunDownloadMessageSig: { diff --git a/qml/pages/PageMenuMessageDetail.qml b/qml/pages/PageMenuMessageDetail.qml index 9b51057163e05ea527ec05ffdfc386355d4feb06..cff0bac6401dceb66d8af460b98f0b575e72a27e 100644 --- a/qml/pages/PageMenuMessageDetail.qml +++ b/qml/pages/PageMenuMessageDetail.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.messages 1.0 import cz.nic.mobileDatovka.models 1.0 @@ -47,169 +46,133 @@ Component { if (msgType == MessageType.TYPE_SENT) { // Hide some menu items for sent messages // Note: remove argument is ListModel item index - messageDetailMenuListModel.remove(1) + var index = messageDetailMenuList.get_model_index(messageDetailMenuListModel, "funcName", "reply"); + if (index >= 0) { + messageDetailMenuListModel.setProperty(index, "showEntry", false) + //messageDetailMenuListModel.remove(index) + } } } PageHeader { id: headerBar - title: qsTr("Message") + ": " + msgId - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) + title: qsTr("Message") + ": " + msgId + onBackClicked: { + pageView.pop(StackView.Immediate) + } + } + + /* Object (associative array) holding functions. */ + property var funcs: { + "downlAttch": function callDownlAttch() { + isds.doIsdsAction("downloadMessage", userName) + pageView.pop(StackView.Immediate) + }, + "reply": function callReply() { + pageView.replace(pageSendMessage, { + "pageView": pageView, + "statusBar": statusBar, + "acntName" : acntName, + "userName": userName, + "msgId": msgId, + "msgType": msgType, + "action": "reply" + }, StackView.Immediate) + }, + "forward": function callForward() { + pageView.replace(pageSendMessage, { + "pageView": pageView, + "statusBar": statusBar, + "acntName" : acntName, + "userName": userName, + "msgId": msgId, + "msgType": msgType, + "action": "fwdzfo" + }, StackView.Immediate) + }, + "useTemplate": function callUseTemplate() { + pageView.replace(pageSendMessage, { + "pageView": pageView, + "statusBar": statusBar, + "acntName" : acntName, + "userName": userName, + "msgId": msgId, + "msgType": msgType, + "action": "template" + }, StackView.Immediate) + }, + "sendEmail": function callSendEmail() { + files.sendAttachmentsWithEmail(userName, msgId) + pageView.pop(StackView.Immediate) + }, + "saveAttachs": function callSaveAttachs() { + files.saveAttachmentsToDiskDb(userName, msgId) + pageView.pop(StackView.Immediate) + }, + "delAttachs": function callDelAttachs() { + if (files.deleteAttachmentsFromDb(userName, msgId)) { + if (attachmentModel != null) { + attachmentModel.clearAll() + } } + pageView.pop(StackView.Immediate) } } + ListModel { id: messageDetailMenuListModel ListElement { - index: 0 - name: qsTr("Download attachments") image: "qrc:/ui/datovka-email-download.svg" + showEntry: true showNext: false + name: qsTr("Download attachments") + funcName: "downlAttch" } ListElement { - index: 1 - name: qsTr("Reply") image: "qrc:/ui/reply.svg" + showEntry: true showNext: true + name: qsTr("Reply") + funcName: "reply" } ListElement { - index: 2 - name: qsTr("Forward") image: "qrc:/ui/forward.svg" + showEntry: true showNext: true + name: qsTr("Forward") + funcName: "forward" } ListElement { - index: 3 - name: qsTr("Use as template") image: "qrc:/ui/datovka-email-sample.svg" + showEntry: true showNext: true + name: qsTr("Use as template") + funcName: "useTemplate" } ListElement { - index: 4 - name: qsTr("Send attachments by email") image: "qrc:/ui/attach-to-mail.svg" + showEntry: true showNext: false + name: qsTr("Send attachments by email") + funcName: "sendEmail" } ListElement { - index: 5 - name: qsTr("Save attachments") image: "qrc:/ui/save-to-disk.svg" + showEntry: true showNext: false + name: qsTr("Save attachments") + funcName: "saveAttachs" } ListElement { - index: 6 - name: qsTr("Delete attachments") image: "qrc:/ui/delete.svg" + showEntry: true showNext: false + name: qsTr("Delete attachments") + funcName: "delAttachs" } } - Component { - id: messageDetailMenuComponent - Rectangle { - height: headerHeight - width: parent.width - Image { - id: menuImage - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: defaultMargin - sourceSize.height: imgHeight - source: image - } - ColorOverlay { - anchors.fill: menuImage - source: menuImage - color: datovkaPalette.text - } - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: menuImage.right - anchors.leftMargin: defaultMargin - color: datovkaPalette.text - text: name - } - Rectangle { - visible: showNext - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - height: parent.height - width: parent.width * 0.07 - color: parent.color - Image { - id: nextImage - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: defaultMargin - sourceSize.height: navImgHeight - source: "qrc:/ui/next.svg" - } - ColorOverlay { - anchors.fill: nextImage - source: nextImage - color: datovkaPalette.text - } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (index == 0) { - isds.doIsdsAction("downloadMessage", userName) - pageView.pop(StackView.Immediate) - } else if (index == 1) { - pageView.replace(pageSendMessage, { - "pageView": pageView, - "statusBar": statusBar, - "acntName" : acntName, - "userName": userName, - "msgId": msgId, - "msgType": msgType, - "action": "reply" - }, StackView.Immediate) - } else if (index == 2) { - pageView.replace(pageSendMessage, { - "pageView": pageView, - "statusBar": statusBar, - "acntName" : acntName, - "userName": userName, - "msgId": msgId, - "msgType": msgType, - "action": "fwdzfo" - }, StackView.Immediate) - } else if (index == 3) { - pageView.replace(pageSendMessage, { - "pageView": pageView, - "statusBar": statusBar, - "acntName" : acntName, - "userName": userName, - "msgId": msgId, - "msgType": msgType, - "action": "template" - }, StackView.Immediate) - } else if (index == 4) { - files.sendAttachmentsWithEmail(userName, msgId) - pageView.pop(StackView.Immediate) - } else if (index == 5) { - files.saveAttachmentsToDiskDb(userName, msgId) - pageView.pop(StackView.Immediate) - } else if (index == 6) { - if (files.deleteAttachmentsFromDb(userName, msgId)) { - if (attachmentModel != null) { - attachmentModel.clearAll() - } - } - pageView.pop(StackView.Immediate) - } - } - } - } - } - ListView { + + AccessibleMenu { id: messageDetailMenuList anchors.top: headerBar.bottom anchors.bottom: parent.bottom @@ -217,8 +180,9 @@ Component { clip: true spacing: 1 opacity: 1 + + funcArr: funcs model: messageDetailMenuListModel - delegate: messageDetailMenuComponent } } } diff --git a/qml/pages/PageMenuMessageList.qml b/qml/pages/PageMenuMessageList.qml index 7feb3c80633b1e9d4f0e6f4fc2a7ff641dfaa982..35796ea642e139a55c465fd2466f3d1b02619cca 100644 --- a/qml/pages/PageMenuMessageList.qml +++ b/qml/pages/PageMenuMessageList.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 import cz.nic.mobileDatovka 1.0 import cz.nic.mobileDatovka.messages 1.0 @@ -43,92 +42,42 @@ Component { PageHeader { id: headerBar title: qsTr("Message operations") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } } + + /* Object (associative array) holding functions. */ + property var funcs: { + "markRead": function callMarkRead() { + messages.markMessagesAsLocallyRead(messageModel, userName, msgType, true) + pageView.pop(StackView.Immediate) + }, + "markUnread": function callMarkUnread() { + messages.markMessagesAsLocallyRead(messageModel, userName, msgType, false) + pageView.pop(StackView.Immediate) + } + } + ListModel { id: messagesMenuListModel ListElement { - index: 0 - name: qsTr("Mark messages as read") image: "qrc:/ui/email-open-outline.svg" + showEntry: true showNext: false + name: qsTr("Mark messages as read") + funcName: "markRead" } ListElement { - index: 1 - name: qsTr("Mark messages as unread") image: "qrc:/ui/email-outline.svg" + showEntry: true showNext: false + name: qsTr("Mark messages as unread") + funcName: "markUnread" } } - Component { - id: messagesMenuComponent - Rectangle { - color: datovkaPalette.base - height: headerHeight - width: parent.width - Image { - id: menuImage - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: defaultMargin - sourceSize.height: imgHeight - source: image - } - ColorOverlay { - anchors.fill: menuImage - source: menuImage - color: datovkaPalette.text - } - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: menuImage.right - anchors.leftMargin: defaultMargin - color: datovkaPalette.text - text: name - } - Rectangle { - visible: showNext - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - height: parent.height - width: parent.width * 0.07 - color: parent.color - Image { - id: nextImage - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: defaultMargin - sourceSize.height: navImgHeight - source: "qrc:/ui/next.svg" - } - ColorOverlay { - anchors.fill: nextImage - source: nextImage - color: datovkaPalette.text - } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (index == 0) { - messages.markMessagesAsLocallyRead(messageModel, userName, msgType, true) - } else if (index == 1) { - messages.markMessagesAsLocallyRead(messageModel, userName, msgType, false) - } - pageView.pop(StackView.Immediate) - } - } - } - } - ListView { + + AccessibleMenu { id: messagesMenuList anchors.top: headerBar.bottom anchors.bottom: parent.bottom @@ -136,8 +85,9 @@ Component { clip: true spacing: 1 opacity: 1 + + funcArr: funcs model: messagesMenuListModel - delegate: messagesMenuComponent } } } diff --git a/qml/pages/PageMessageDetail.qml b/qml/pages/PageMessageDetail.qml index 4235c2584368c292983e7a38d1d6c638d523a5c7..c93b1a86787255486856e6d9de899c9faba7515c 100644 --- a/qml/pages/PageMessageDetail.qml +++ b/qml/pages/PageMessageDetail.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -135,85 +135,71 @@ Component { } return str } - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - if (typeof(statusBar) != "undefined") { - statusBar.visible = false - } - pageView.pop() + onBackClicked: { + if (typeof(statusBar) != "undefined") { + statusBar.visible = false } + pageView.pop() } Row { anchors.verticalCenter: parent.verticalCenter spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { + AccessibleImageButton { id: msgEmailButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/reply.svg" - MouseArea { - anchors.fill: parent - onClicked: { - files.sendAttachmentEmailZfo(attachmentModel, zfoId, msgAnnotation, emailBody) - } + accessibleName: qsTr("Email attachments") + onClicked: { + files.sendAttachmentEmailZfo(attachmentModel, zfoId, msgAnnotation, emailBody) } } - Image { + AccessibleImageButton { id: attachmentSaveButon anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/save-to-disk.svg" - MouseArea { - anchors.fill: parent - onClicked: { - if (!fromLocalDb) { - files.saveAttachmentsToDiskZfo(attachmentModel, zfoId) - } else { - files.saveAttachmentsToDiskDb(userName, zfoId) - } + accessibleName: qsTr("Save attachments.") + onClicked: { + if (!fromLocalDb) { + files.saveAttachmentsToDiskZfo(attachmentModel, zfoId) + } else { + files.saveAttachmentsToDiskDb(userName, zfoId) } } } - Image { + AccessibleImageButton { id: msgDownloadButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/datovka-email-download.svg" - MouseArea { - anchors.fill: parent - onClicked: { - if (fromLocalDb) { - isds.doIsdsAction("downloadMessage", userName) - } + accessibleName: qsTr("Download message") + onClicked: { + if (fromLocalDb) { + isds.doIsdsAction("downloadMessage", userName) } } } - Image { + AccessibleImageButton { id: attachmentMenuButon anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/menu.svg" - MouseArea { - anchors.fill: parent - onClicked: { - if (fromLocalDb) { - pageView.push(pageMenuMessageDetail, { - "pageView": pageView, - "statusBar": statusBar, - "acntName": acntName, - "userName": userName, - "msgType": msgType, - "msgId": zfoId, - "messageModel": messageModel, - "attachmentModel" : attachmentModel - }, StackView.Immediate) - } + accessibleName: qsTr("Show menu of available operations") + onClicked: { + if (fromLocalDb) { + pageView.push(pageMenuMessageDetail, { + "pageView": pageView, + "statusBar": statusBar, + "acntName": acntName, + "userName": userName, + "msgType": msgType, + "msgId": zfoId, + "messageModel": messageModel, + "attachmentModel" : attachmentModel + }, StackView.Immediate) } } } @@ -230,7 +216,7 @@ Component { Pane { id: flickContent anchors.fill: parent - Text { + AccessibleText { id: messageDetailText color: datovkaPalette.text width: parent.width @@ -252,7 +238,7 @@ Component { id: attachmentLabel width: parent.width height: textFontSizeInPixels * 3 - Text { + AccessibleText { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: defaultMargin * 2 @@ -267,8 +253,11 @@ Component { color: datovkaPalette.dark } } - ListView { + ScrollableListView { id: attachmentList + + delegateHeight: headerHeight + anchors.top: attachmentLabel.bottom anchors.bottom: parent.bottom clip: true @@ -285,7 +274,7 @@ Component { delegate: Rectangle { id: attachmentItem width: parent.width - height: headerHeight + height: attachmentList.delegateHeight color: datovkaPalette.base Image { id: imageAttachment @@ -340,8 +329,7 @@ Component { } } MouseArea { - anchors.fill: parent - onClicked: { + function handleClick() { locker.ignoreNextSuspension() if (files.isZfoFile(rFileName)) { console.log("Attachment is ZFO: " + rFileName) @@ -365,6 +353,19 @@ Component { } } } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Open attachment '%1'.").arg(rFileName) + Accessible.onScrollDownAction: attachmentList.scrollDown() + Accessible.onScrollUpAction: attachmentList.scrollUp() + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } Rectangle { anchors.top: parent.bottom @@ -390,7 +391,7 @@ Component { } ScrollIndicator.vertical: ScrollIndicator {} } // Listview - Text { + AccessibleTextButton { id: emptyList visible: false color: datovkaPalette.text @@ -401,17 +402,14 @@ Component { verticalAlignment: Text.AlignVCenter wrapMode: Text.Wrap text: fromLocalDb ? qsTr("Attachments have not been downloaded yet.\nClick the icon or this text for their download.") : qsTr("No attachments present.") - MouseArea { - anchors.fill: parent - onClicked: { - if (fromLocalDb) { - isds.doIsdsAction("downloadMessage", userName) - } + onClicked: { + if (fromLocalDb) { + isds.doIsdsAction("downloadMessage", userName) } } } // Text } // Rectangle - Text { + AccessibleText { id: errorNotification visible: false color: datovkaPalette.text diff --git a/qml/pages/PageMessageList.qml b/qml/pages/PageMessageList.qml index 64571250ff5bcf45a2e70b52634790b20254243f..aac19993d94db764b807527ad88e2b33dfb57fa9 100644 --- a/qml/pages/PageMessageList.qml +++ b/qml/pages/PageMessageList.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,7 +50,7 @@ Component { if (messageModel.rowCount() === 0) { emptyList.visible = true /* Don't use visible property as it hides the element. */ - settingsButton.visible = false + menuButton.visible = false searchButton.visible = false } @@ -58,7 +58,7 @@ Component { /* Don't use message settings for sent messages */ if (msgType == MessageType.TYPE_SENT) { - settingsButton.visible = false + menuButton.visible = false } } @@ -86,111 +86,75 @@ Component { PageHeader { id: headerBar title: (msgType == MessageType.TYPE_RECEIVED) ? qsTr("Received messages") : qsTr("Sent messages"); - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop() - } + onBackClicked: { + pageView.pop() } Row { anchors.verticalCenter: parent.verticalCenter spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { + AccessibleImageButton { id: searchButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/magnify.svg" - MouseArea { - anchors.fill: parent - onClicked: { - messageList.anchors.top = filterBar.bottom - filterBar.visible = true - filter.forceActiveFocus() - Qt.inputMethod.show() - } + accessibleName: qsTr("Filter messages.") + onClicked: { + messageList.anchors.top = filterBar.bottom + filterBar.visible = true + filterBar.filterField.forceActiveFocus() + Qt.inputMethod.show() } } - Image { - id: settingsButton + AccessibleImageButton { + id: menuButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/menu.svg" - MouseArea { - anchors.fill: parent - onClicked: { - statusBar.visible = false - Qt.inputMethod.hide() - pageView.push(pageMenuMessageList, { - "pageView": pageView, - "statusBar": statusBar, - "acntName": acntName, - "userName": userName, - "msgType": msgType, - "messageModel": messageModel - }, StackView.Immediate) - } + accessibleName: qsTr("Show menu of available operations.") + onClicked: { + statusBar.visible = false + Qt.inputMethod.hide() + pageView.push(pageMenuMessageList, { + "pageView": pageView, + "statusBar": statusBar, + "acntName": acntName, + "userName": userName, + "msgType": msgType, + "messageModel": messageModel + }, StackView.Immediate) } } } } - Rectangle { + FilterBar { id: filterBar visible: false z: 1 anchors.top: headerBar.bottom width: parent.width - height: filter.height - color: (filter.text.length == 0) ? datovkaPalette.alternateBase : + height: filterField.height + color: (filterField.text.length == 0) ? datovkaPalette.alternateBase : (messageList.count > 0) ? "#afffaf" : "#ffafaf" - border.color: filter.activeFocus ? "#0066ff" : "#bdbebf" - TextField { - id: filter - anchors.top: parent.top - width: parent.width - close.width - placeholderText: qsTr("Set filter") - font.pointSize: defaultTextFont.font.pointSize - inputMethodHints: Qt.ImhNoPredictiveText - /* - * Using explicit top, left, ... padding because plain padding - * does not centre the text properly. - */ - topPadding: 8.0 - bottomPadding: 8.0 - leftPadding: 8.0 - rightPadding: 8.0 - /* TODO - remove background if used materials */ - background: Rectangle { - anchors.fill: parent - color: "transparent" - border.color: "transparent" - } - onTextChanged: { - proxyMessageModel.setFilterRegExpStr(filter.text) - } + border.color: filterField.activeFocus ? "#0066ff" : "#bdbebf" + + placeholderText: qsTr("Set filter") + fontPointSize: defaultTextFont.font.pointSize + buttonImageHeight: imgHeight + buttonAccessibleName: qsTr("Clear and hide filter field") + + onTextChanged: { + proxyMessageModel.setFilterRegExpStr(text) } - Image { - id: close - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: imgHeight - source: "qrc:/ui/remove.svg" - MouseArea { - anchors.fill: parent - onClicked: { - filterBar.visible = false - filter.clear() - messageList.anchors.top = headerBar.bottom - Qt.inputMethod.hide() - } - } + + onClearClicked: { + filterBar.visible = false + messageList.anchors.top = headerBar.bottom + Qt.inputMethod.hide() } - } // Rectangle - Text { + } + AccessibleText { id: emptyList visible: false color: datovkaPalette.text @@ -198,9 +162,9 @@ Component { width: parent.width horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap - text: (filter.text.length == 0) ? + text: (filterBar.filterField.text.length == 0) ? qsTr("No messages or have not been downloaded yet.") : - qsTr("No message found that matches filter text '%1'.").arg(filter.text) + qsTr("No message found that matches filter text '%1'.").arg(filterBar.filterField.text) } MessageList { id: messageList @@ -217,10 +181,10 @@ Component { onCountChanged: { if (messageList.count == 0) { emptyList.visible = true - settingsButton.enabled = false + menuButton.enabled = false } else { emptyList.visible = false - settingsButton.enabled = true + menuButton.enabled = true } } onMsgClicked: { diff --git a/qml/pages/PageMessageSearch.qml b/qml/pages/PageMessageSearch.qml index b2453361d6fd08c37be785e9fd5f25e8cdff7a82..83060c6bfb9499e3a82225e95a7e54e1639d1c9f 100644 --- a/qml/pages/PageMessageSearch.qml +++ b/qml/pages/PageMessageSearch.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,33 +58,25 @@ Item { PageHeader { id: headerBar title: qsTr("Search message") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } Row { anchors.verticalCenter: parent.verticalCenter spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { + AccessibleImageButton { id: actionButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/magnify.svg" - MouseArea { - anchors.fill: parent - onClicked: { - // Set textfield focus on false = hide input keyboard - // and obtain textfield value - searchPhraseText.focus = false - emptyList.visible = searchPanel.doSearchRequest() - } + accessibleName: qsTr("Search messages.") + onClicked: { + // Set textfield focus on false = hide input keyboard + // and obtain textfield value + searchPhraseText.focus = false + emptyList.visible = searchPanel.doSearchRequest() } } } @@ -100,42 +92,26 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - ComboBox { + AccessibleComboBox { id: searchOptionComboBox - currentIndex: 0 width: parent.width - height: inputItemHeight - textRole: "label" - font.pointSize: defaultTextFont.font.pointSize - delegate: ItemDelegate { - width: searchOptionComboBox.width - height: inputItemHeight - contentItem: Text { - text: label - color: datovkaPalette.text - font.pointSize: defaultTextFont.font.pointSize - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - } - highlighted: searchOptionComboBox.highlightedIndex == index - } + accessibleDescription: qsTr("Select type of sought messages") model: ListModel { id: searchOptionComboBoxModel - // Don't change position of list elements and keys. - /* Cannot have an expression here becacuse QML complains that it cannot use script for property value. */ - ListElement { label: qsTr("All messages"); value: -1 } - ListElement { label: qsTr("Received messages"); value: MessageType.TYPE_RECEIVED } - ListElement { label: qsTr("Sent messages"); value: MessageType.TYPE_SENT } + /* Cannot have an expression here because QML complains that it cannot use script for property key. */ + ListElement { label: qsTr("All messages"); key: -1 } + ListElement { label: qsTr("Received messages"); key: MessageType.TYPE_RECEIVED } + ListElement { label: qsTr("Sent messages"); key: MessageType.TYPE_SENT } } onCurrentIndexChanged: { - if (searchOptionComboBoxModel.get(currentIndex).value == -1) { + if (currentKey() == -1) { soughtMsgType = MessageType.TYPE_RECEIVED | MessageType.TYPE_SENT } else { - soughtMsgType = searchOptionComboBoxModel.get(currentIndex).value + soughtMsgType = currentKey() } } } - TextField { + AccessibleTextField { id: searchPhraseText placeholderText: qsTr("Enter phrase") focus: true diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index cef90b0fea4eb59f4d86f3a1e9885372a462f2a8..00278eeed91c26c74851eff98ab988da6250288c 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -191,32 +191,24 @@ Item { PageHeader { id: headerBar title: qsTr("Create message") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } - OverlaidImage { + AccessibleOverlaidImageButton { id: actionButton anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: defaultMargin image.sourceSize.height: imgHeightHeader image.source: "qrc:/ui/send-msg.svg" - MouseArea { - anchors.fill: parent - onClicked: { - actionButton.enabled = false - mainPanel.doSendRequest() - } + accessibleName: qsTr("Send message") + onClicked: { + actionButton.enabled = false + mainPanel.doSendRequest() } } } // PageHeader - Text { + AccessibleText { id: errorText visible: false color: datovkaPalette.text @@ -263,7 +255,7 @@ Item { width: parent.width Repeater { model: [qsTr("General"), qsTr("Recipients"), qsTr("Attachments"), qsTr("Additional")] - TabButton { + AccessibleTabButton { text: modelData width: Math.max(100, tabBar.width / 4) } @@ -291,7 +283,7 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - Text { + AccessibleText { color: datovkaPalette.mid wrapMode: Text.Wrap horizontalAlignment: Text.AlignHCenter @@ -301,24 +293,24 @@ Item { Text { text: " " } - Text { + AccessibleText { color: datovkaPalette.text font.bold: true text: qsTr("Sender account") } - Text { + AccessibleText { color: datovkaPalette.text text: acntName + " (" + userName + ")" } Text { text: " " } - Text { + AccessibleText { color: datovkaPalette.text font.bold: true text: qsTr("Subject") } - TextField { + AccessibleTextField { id: dmAnnotation width: parent.width height: inputItemHeight @@ -339,33 +331,33 @@ Item { mainPanel.areReguiredFieldsFilled() } } - Switch { + AccessibleSwitch { id: dmPublishOwnID text: qsTr("Include sender identification") font.pointSize: defaultTextFont.font.pointSize checked: true } - Switch { + AccessibleSwitch { id: dmPersonalDelivery text: qsTr("Personal delivery") font.pointSize: defaultTextFont.font.pointSize checked: false } - Switch { + AccessibleSwitch { id: dmAllowSubstDelivery visible: false text: qsTr("Allow acceptance through fiction") font.pointSize: defaultTextFont.font.pointSize checked: true } - Switch { + AccessibleSwitch { id: dmOVM visible: false text: qsTr("Send as OVM") font.pointSize: defaultTextFont.font.pointSize checked: true } - Switch { + AccessibleSwitch { id: initPDZ visible: false text: qsTr("Pay transfer charges for reply") @@ -375,7 +367,7 @@ Item { (initPDZ.checked) ? dmType = "I" : dmType = "" } } - Switch { + AccessibleSwitch { id: replyPDZ visible: false text: qsTr("Use transfer charges from recipient") @@ -399,7 +391,7 @@ Item { Row { spacing: formItemVerticalSpacing * 5 anchors.horizontalCenter: parent.horizontalCenter - Button { + AccessibleButton { id: addContact height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -413,7 +405,7 @@ Item { }, StackView.Immediate) } } - Button { + AccessibleButton { id: findDS height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -454,7 +446,7 @@ Item { //----ATTACHMENT SECTION------------ Item { id: tabAttachments - Button { + AccessibleButton { id: addFile height: inputItemHeight anchors.horizontalCenter: parent.horizontalCenter @@ -469,11 +461,10 @@ Item { Rectangle { id: attachmentItem width: parent.width - height: listItemHeight + height: attachmentListSend.delegateHeight color: datovkaPalette.base MouseArea { - anchors.fill: parent - onClicked: { + function handleClick() { // fileId is set and is valid, use files from database if (rFileId > 0) { if (files.isZfoFile(rFileName)) { @@ -503,12 +494,23 @@ Item { } } } + + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Open file '%1'.").arg(rFileName) + Accessible.onPressAction: { + handleClick() + } + onClicked: { + handleClick() + } } Image { id: imageAttachment anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - sourceSize.height: listItemHeight * 0.8 + sourceSize.height: attachmentListSend.delegateHeight * 0.8 source: rFileIcon } Item { @@ -558,9 +560,21 @@ Item { color: datovkaPalette.windowText } MouseArea { + function handleClick() { + sendMsgAttachmentModel.removeItem(index) + } + anchors.fill: parent + + Accessible.role: Accessible.Button + Accessible.name: qsTr("Remove file '%1' from list.").arg(rFileName) + Accessible.onScrollDownAction: attachmentListSend.scrollDown() + Accessible.onScrollUpAction: attachmentListSend.scrollUp() + Accessible.onPressAction: { + handleClick() + } onClicked: { - sendMsgAttachmentModel.removeItem(index) + handleClick() } } } // Rectangle @@ -572,8 +586,11 @@ Item { } } // Rectangle } // Component - ListView { + ScrollableListView { id: attachmentListSend + + delegateHeight: listItemHeight + anchors.top: addFile.bottom anchors.bottom: parent.bottom clip: true @@ -605,58 +622,58 @@ Item { anchors.left: parent.left spacing: formItemVerticalSpacing //---Mandate--------------------- - Text { + AccessibleText { font.bold: true text: qsTr("Mandate") } Row { width: parent.width spacing: defaultMargin - TextField { + AccessibleTextField { id: dmLegalTitleLaw width: mandateFieldLenght height: inputItemHeight maximumLength: 4 font.pointSize: defaultTextFont.font.pointSize } - Text { + AccessibleText { anchors.verticalCenter: parent.verticalCenter text: "/" } - TextField { + AccessibleTextField { id: dmLegalTitleYear width: mandateFieldLenght height: inputItemHeight maximumLength: 4 font.pointSize: defaultTextFont.font.pointSize } - Text { + AccessibleText { anchors.verticalCenter: parent.verticalCenter text: "§" } - TextField { + AccessibleTextField { id: dmLegalTitleSect width: mandateFieldLenght height: inputItemHeight maximumLength: 4 font.pointSize: defaultTextFont.font.pointSize } - Text { + AccessibleText { anchors.verticalCenter: parent.verticalCenter text: qsTr("par.") } - TextField { + AccessibleTextField { id: dmLegalTitlePar width: mandateFieldLenght / 2 height: inputItemHeight maximumLength: 2 font.pointSize: defaultTextFont.font.pointSize } - Text { + AccessibleText { anchors.verticalCenter: parent.verticalCenter text: qsTr("let.") } - TextField { + AccessibleTextField { id: dmLegalTitlePoint width: mandateFieldLenght / 2 height: inputItemHeight @@ -665,11 +682,11 @@ Item { } } //---Our ref.number--------------------- - Text { + AccessibleText { font.bold: true text: qsTr("Our reference number") } - TextField { + AccessibleTextField { id: dmSenderRefNumber height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -688,11 +705,11 @@ Item { } } //---Our file mark--------------------- - Text { + AccessibleText { font.bold: true text: qsTr("Our file mark") } - TextField { + AccessibleTextField { id: dmSenderIdent height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -711,11 +728,11 @@ Item { } } //---Your ref.number--------------------- - Text { + AccessibleText { font.bold: true text: qsTr("Your reference number") } - TextField { + AccessibleTextField { id: dmRecipientRefNumber height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -734,11 +751,11 @@ Item { } } //---Your file mark--------------------- - Text { + AccessibleText { font.bold: true text: qsTr("Your file mark") } - TextField { + AccessibleTextField { id: dmRecipientIdent height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -757,11 +774,11 @@ Item { } } //---To hands--------------------- - Text { + AccessibleText { font.bold: true text: qsTr("To hands") } - TextField { + AccessibleTextField { id: dmToHands height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize diff --git a/qml/pages/PageSettingsAccount.qml b/qml/pages/PageSettingsAccount.qml index a3add3ca5f5eb21c411b046461f4bd530f2530c4..905899ed5ddc23324dfb23ac0dbb70c7c05dc787 100644 --- a/qml/pages/PageSettingsAccount.qml +++ b/qml/pages/PageSettingsAccount.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -68,51 +68,43 @@ Item { PageHeader { id: headerBar title: qsTr("New account") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } Row { anchors.verticalCenter: parent.verticalCenter spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { + AccessibleImageButton { id: actionButton anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/checkbox-marked-circle.svg" - MouseArea { - anchors.fill: parent - onClicked: { - if (isNewAccount) { - // Create a new account context data and add to model. - if (accounts.createAccount(accountModel, sLoginMethod, + accessibleName: qsTr("Accept changes") + onClicked: { + if (isNewAccount) { + // Create a new account context data and add to model. + if (accounts.createAccount(accountModel, sLoginMethod, + accountNameTextField.text.toString(), + userNameTextField.text.toString(), + passwordTextField.text.toString(), + testAccount.checked, rememberPassword.checked, + useLS.checked, certPathLabelId.text.toString())) { + // Login to new account. + isds.doIsdsAction("addNewAccount", userNameTextField.text.toString()) + } + } else { + // Update account context data. + if (accounts.updateAccount(accountModel, sLoginMethod, accountNameTextField.text.toString(), userNameTextField.text.toString(), passwordTextField.text.toString(), testAccount.checked, rememberPassword.checked, useLS.checked, certPathLabelId.text.toString())) { - // Login to new account. - isds.doIsdsAction("addNewAccount", userNameTextField.text.toString()) - } - } else { - // Update account context data. - if (accounts.updateAccount(accountModel, sLoginMethod, - accountNameTextField.text.toString(), - userNameTextField.text.toString(), - passwordTextField.text.toString(), - testAccount.checked, rememberPassword.checked, - useLS.checked, certPathLabelId.text.toString())) { - settings.saveAllSettings(accountModel) - } - pageView.pop(StackView.Immediate) + settings.saveAllSettings(accountModel) } + pageView.pop(StackView.Immediate) } } Connections { @@ -159,40 +151,25 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - Text { + AccessibleText { id: loginMethodLabel color: datovkaPalette.text text: qsTr("Login method") } - ComboBox { + AccessibleComboBox { id: loginMethodComboBox - currentIndex: 0 width: parent.width - height: inputItemHeight - textRole: "label" - font.pointSize: defaultTextFont.font.pointSize - delegate: ItemDelegate { - width: loginMethodComboBox.width - height: inputItemHeight - contentItem: Text { - text: label - color: datovkaPalette.text - font.pointSize: defaultTextFont.font.pointSize - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - } - highlighted: loginMethodComboBox.highlightedIndex == index - } + accessibleDescription: qsTr("Select login method") model: ListModel { id: loginMethodModel - // Don't change position of list elements, keys and values. - ListElement { label: qsTr("Username + Password"); value: "pwd" } - ListElement { label: qsTr("Certificate + Password"); value: "cert" } - ListElement { label: qsTr("Password + Security code"); value: "hotp" } - ListElement { label: qsTr("Password + Security SMS"); value: "totp" } + /* Key values must be equivalent to constants defined in C++ code! */ + ListElement { label: qsTr("Username + Password"); key: "pwd" } // USERNAME_PWD_ONLY + ListElement { label: qsTr("Certificate + Password"); key: "cert" } // USERNAME_PWD_CERT + ListElement { label: qsTr("Password + Security code"); key: "hotp" } // USERNAME_PWD_HOTP + ListElement { label: qsTr("Password + Security SMS"); key: "totp" } // USERNAME_PWD_TOTP } onCurrentIndexChanged: { - sLoginMethod = loginMethodModel.get(currentIndex).value + sLoginMethod = currentKey() if (sLoginMethod === "cert") { certificateLabel.visible = true certPathButtonId.visible = true @@ -204,13 +181,13 @@ Item { } } } - Text { + AccessibleText { id: certificateLabel visible: false color: datovkaPalette.text text: qsTr("Certificate") } - Text { + AccessibleText { id: certPathLabelId visible: false color: datovkaPalette.text @@ -218,7 +195,7 @@ Item { wrapMode: Text.WrapAnywhere text: "" } - Button { + AccessibleButton { id: certPathButtonId visible: false text: qsTr("Choose file") @@ -228,11 +205,11 @@ Item { fileDialogue.raise("Select certificate file", "*.pem", true) } } - Text { + AccessibleText { color: datovkaPalette.text text: qsTr("Account title") } - TextField { + AccessibleTextField { id: accountNameTextField width: parent.width height: inputItemHeight @@ -250,11 +227,11 @@ Item { } } } - Text { + AccessibleText { color: datovkaPalette.text text: qsTr("Username") } - TextField { + AccessibleTextField { id: userNameTextField width: parent.width height: inputItemHeight @@ -273,11 +250,11 @@ Item { } } } - Text { + AccessibleText { color: datovkaPalette.text text: qsTr("Password") } - TextField { + AccessibleTextField { id: passwordTextField width: parent.width height: inputItemHeight @@ -297,31 +274,31 @@ Item { } } } - Switch { + AccessibleSwitch { id: rememberPassword text: qsTr("Remember password") font.pointSize: defaultTextFont.font.pointSize checked: true } - Switch { + AccessibleSwitch { id: testAccount text: qsTr("Test account") font.pointSize: defaultTextFont.font.pointSize } - Text { + AccessibleText { id: testAccountLabel color: datovkaPalette.mid width: parent.width text: qsTr("Test accounts are used to access the ISDS testing environment.") wrapMode: Text.Wrap } - Switch { + AccessibleSwitch { id: useLS font.pointSize: defaultTextFont.font.pointSize text: qsTr("Use local storage (database)") checked: true } - Text { + AccessibleText { color: datovkaPalette.mid width: parent.width text: (useLS.checked ? @@ -340,7 +317,7 @@ Item { testAccount.checked = isTestAccount useLS.checked = storeToDisk certPathLabelId.text = certPath - loginMethodComboBox.currentIndex = loginMethod + loginMethodComboBox.selectCurrentKey(loginMethod) userNameTextField.enabled = false testAccount.enabled = false headerBar.title = qsTr("Account settings") diff --git a/qml/pages/PageSettingsGeneral.qml b/qml/pages/PageSettingsGeneral.qml index 46760be351cd64797aeeea2eaa8629473756d75f..4d5de6b331da410deef525fef93d317f4692616f 100644 --- a/qml/pages/PageSettingsGeneral.qml +++ b/qml/pages/PageSettingsGeneral.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,14 +35,7 @@ Item { property var accountModel: null Component.onCompleted: { - var lang = settings.language() - var langugeComboBoxIndex = 0; - if (lang === "cs") { - langugeComboBoxIndex = 1; - } else if (lang === "en") { - langugeComboBoxIndex = 2; - } - languageComboBoxId.currentIndex = langugeComboBoxIndex + languageComboBox.selectCurrentKey(settings.language()) fontSizeSpinBox.setVal(settings.fontSize()) } @@ -53,16 +46,10 @@ Item { PageHeader { id: headerBar title: qsTr("General settings") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } - } // PageHeader + } Flickable { id: flickable z: 0 @@ -78,79 +65,54 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - Text { + AccessibleText { id: languageLabel color: datovkaPalette.text text: qsTr("Language") } - ComboBox { - id: languageComboBoxId - currentIndex: 0 + AccessibleComboBox { + id: languageComboBox width: parent.width - height: inputItemHeight - textRole: "label" - font.pointSize: defaultTextFont.font.pointSize - delegate: ItemDelegate { - width: languageComboBoxId.width - height: inputItemHeight - contentItem: Text { - text: label - color: datovkaPalette.text - font.pointSize: defaultTextFont.font.pointSize - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - } - highlighted: languageComboBoxId.highlightedIndex == index - } + accessibleDescription: qsTr("Select language") model: ListModel { id: langMethodModel - // Don't change position of list elements, keys and values. - ListElement { label: qsTr("System"); value: "system" } - ListElement { label: qsTr("Czech"); value: "cs" } - ListElement { label: qsTr("English"); value: "en" } + /* Don't change keys and labels. */ + ListElement { label: qsTr("System"); key: "system" } + ListElement { label: qsTr("Czech"); key: "cs" } + ListElement { label: qsTr("English"); key: "en" } } - onCurrentIndexChanged: settings.setLanguage(langMethodModel.get(currentIndex).value) + onCurrentIndexChanged: settings.setLanguage(currentKey()) } - Text { + AccessibleText { color: datovkaPalette.mid width: parent.width text: qsTr("Note: Language will be changed after application restart.") wrapMode: Text.Wrap } - Text { + AccessibleText { id: fontSizeLabel color: datovkaPalette.text width: parent.width text: qsTr("Font size and application scale") } - SpinBox { + AccessibleSpinBox { /* Actually holds font pixel size. */ id: fontSizeSpinBox - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize + property int dflt: 16 from: 14 to: 20 stepSize: 1 + + accessibleDescription: qsTr("Set application font size") + /* * Cannot use onValueChanged here because this resets * the font size to minimal value on spin box creation. */ onValueModified: settings.setFontSize(fontSizeSpinBox.val()) - function val() { - return value - } - function setVal(v) { - if (v < from) { - value = from - } else if (v > to) { - value = to - } else { - value = v - } - } } - Text { + AccessibleText { color: datovkaPalette.mid width: parent.width text: qsTr("Note: Font size will be changed after application restart. Default is %1.").arg(fontSizeSpinBox.dflt) diff --git a/qml/pages/PageSettingsPin.qml b/qml/pages/PageSettingsPin.qml index 39cb2eb78f19d167472e4415223b9a19caf878c6..d1f1b186aeaffde0794e91f63d894bebd180369c 100644 --- a/qml/pages/PageSettingsPin.qml +++ b/qml/pages/PageSettingsPin.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -119,20 +119,12 @@ Item { PageHeader { id: headerBar title: qsTr("PIN Settings") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - onClicked: { - if (actionName != "unspecified") { - /* Navigate to initial action. */ - actionName = "unspecified" - } else { - pageView.pop(StackView.Immediate) - } - } + onBackClicked: { + if (actionName != "unspecified") { + /* Navigate to initial action. */ + actionName = "unspecified" + } else { + pageView.pop(StackView.Immediate) } } Row { @@ -140,25 +132,46 @@ Item { spacing: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin - Image { + AccessibleImageButton { id: acceptElement anchors.verticalCenter: parent.verticalCenter sourceSize.height: imgHeightHeader source: "qrc:/ui/checkbox-marked-circle.svg" - MouseArea { - anchors.fill: parent - onClicked: { - if (actionName == "unspecified") { - if (currentPIN == "") { - settings.setInactivityInterval(0) - locker.setInactivityInterval(0) - } else { - settings.setInactivityInterval(lockIntervalSpinBox.val()) - locker.setInactivityInterval(lockIntervalSpinBox.val()) + accessibleName: qsTr("Accept changes") + onClicked: { + if (actionName == "unspecified") { + if (currentPIN == "") { + settings.setInactivityInterval(0) + locker.setInactivityInterval(0) + } else { + settings.setInactivityInterval(lockIntervalSpinBox.val()) + locker.setInactivityInterval(lockIntervalSpinBox.val()) + } + pageView.pop(StackView.Immediate) + /* set new pin code */ + } else if (actionName == "new") { + if (pinConfirmField1.text == "" || pinConfirmField2.text == "") { + errLineText.text = qsTr("Error: Both new PIN code fields must be filled in!") + errLineText.visible = true + if (pinConfirmField1.text == "") { + pinConfirmField1.focus = true + } else if (pinConfirmField2.text == "") { + pinConfirmField2.focus = true } + } else if (pinConfirmField2.text != pinConfirmField1.text) { + pinConfirmField2.focus = true + errLineText.text = qsTr("Error: Newly entered PIN codes are different!") + errLineText.visible = true + } else { + errLineText.visible = false + settings.updatePinSettings(pinConfirmField1.text.toString()) + settings.saveAllSettings(accountModel) pageView.pop(StackView.Immediate) - /* set new pin code */ - } else if (actionName == "new") { + } + /* change current pin code */ + } else if (actionName == "change") { + if (pinValueField.text == currentPIN) { + errLineText.visible = false if (pinConfirmField1.text == "" || pinConfirmField2.text == "") { errLineText.text = qsTr("Error: Both new PIN code fields must be filled in!") errLineText.visible = true @@ -177,49 +190,26 @@ Item { settings.saveAllSettings(accountModel) pageView.pop(StackView.Immediate) } - /* change current pin code */ - } else if (actionName == "change") { - if (pinValueField.text == currentPIN) { - errLineText.visible = false - if (pinConfirmField1.text == "" || pinConfirmField2.text == "") { - errLineText.text = qsTr("Error: Both new PIN code fields must be filled in!") - errLineText.visible = true - if (pinConfirmField1.text == "") { - pinConfirmField1.focus = true - } else if (pinConfirmField2.text == "") { - pinConfirmField2.focus = true - } - } else if (pinConfirmField2.text != pinConfirmField1.text) { - pinConfirmField2.focus = true - errLineText.text = qsTr("Error: Newly entered PIN codes are different!") - errLineText.visible = true - } else { - errLineText.visible = false - settings.updatePinSettings(pinConfirmField1.text.toString()) - settings.saveAllSettings(accountModel) - pageView.pop(StackView.Immediate) - } - } else { - pinValueField.text = "" - pinValueField.focus = true - errLineText.text = qsTr("Error: Current PIN code is wrong!") - errLineText.visible = true - } - /* remove/disable current pin code */ - } else if (actionName == "disable") { - if (pinValueField.text == currentPIN) { - errLineText.visible = false - settings.updatePinSettings("") - settings.saveAllSettings(accountModel) - settings.setInactivityInterval(0) - locker.setInactivityInterval(0) - pageView.pop(StackView.Immediate) - } else { - pinValueField.text = "" - pinValueField.focus = true - errLineText.text = qsTr("Error: Current PIN code is wrong!") - errLineText.visible = true - } + } else { + pinValueField.text = "" + pinValueField.focus = true + errLineText.text = qsTr("Error: Current PIN code is wrong!") + errLineText.visible = true + } + /* remove/disable current pin code */ + } else if (actionName == "disable") { + if (pinValueField.text == currentPIN) { + errLineText.visible = false + settings.updatePinSettings("") + settings.saveAllSettings(accountModel) + settings.setInactivityInterval(0) + locker.setInactivityInterval(0) + pageView.pop(StackView.Immediate) + } else { + pinValueField.text = "" + pinValueField.focus = true + errLineText.text = qsTr("Error: Current PIN code is wrong!") + errLineText.visible = true } } } @@ -241,7 +231,7 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - Text { + AccessibleText { id: topLineText anchors.horizontalCenter: parent.horizontalCenter color: datovkaPalette.mid @@ -250,7 +240,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap } - Button { + AccessibleButton { id: setPinButton height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -260,7 +250,7 @@ Item { actionName = "new" } } - Button { + AccessibleButton { id: changePinButton height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -270,7 +260,7 @@ Item { actionName = "change" } } - Button { + AccessibleButton { id: disablePinButton height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize @@ -280,7 +270,7 @@ Item { actionName = "disable" } } - TextField { + AccessibleTextField { id: pinValueField anchors.horizontalCenter: parent.horizontalCenter height: inputItemHeight @@ -292,7 +282,7 @@ Item { horizontalAlignment: TextInput.AlignHCenter text: "" } - TextField { + AccessibleTextField { id: pinConfirmField1 anchors.horizontalCenter: parent.horizontalCenter height: inputItemHeight @@ -304,7 +294,7 @@ Item { horizontalAlignment: TextInput.AlignHCenter text: "" } - TextField { + AccessibleTextField { id: pinConfirmField2 anchors.horizontalCenter: parent.horizontalCenter height: inputItemHeight @@ -316,7 +306,7 @@ Item { horizontalAlignment: TextInput.AlignHCenter text: "" } - Text { + AccessibleText { id: errLineText anchors.horizontalCenter: parent.horizontalCenter font.bold: true @@ -326,7 +316,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap } - Text { + AccessibleText { id: lockIntervalLabel color: datovkaPalette.text anchors.horizontalCenter: parent.horizontalCenter @@ -334,15 +324,18 @@ Item { text: qsTr("Lock after seconds of inactivity") horizontalAlignment: Text.AlignHCenter } - SpinBoxZeroMax { + AccessibleSpinBoxZeroMax { /* Holds value in seconds. */ id: lockIntervalSpinBox + anchors.horizontalCenter: parent.horizontalCenter - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize + /* Must be a non-decreasing list ending with infinity. */ items: [15, 30, 60, 90, 120, 150, 180, qsTr("don't lock")] dfltIdx: 0 + + accessibleDescription: qsTr("Select the number of seconds after which the application is going to be locked.") + onValueModified: { /* Enable accept button. Always visible? */ acceptElement.visible = val() != settings.inactivityInterval() diff --git a/qml/pages/PageSettingsStorage.qml b/qml/pages/PageSettingsStorage.qml index 0103518dfbc1835c88b8d0fa38a3ba986c6a5945..5880d3631e89516a1c851b2c2f269e4c0c1026e6 100644 --- a/qml/pages/PageSettingsStorage.qml +++ b/qml/pages/PageSettingsStorage.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,16 +60,10 @@ Item { PageHeader { id: headerBar title: qsTr("Storage settings") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } - } // PageHeader + } Flickable { id: flickable z: 0 @@ -85,22 +79,24 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - Text { + AccessibleText { id: messageLifeLabel color: datovkaPalette.text text: qsTr("Number of days to keep messages") } - SpinBoxZeroMax { + AccessibleSpinBoxZeroMax { /* Holds value in days. */ id: messageLifeSpinBox - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize + /* Must be a non-decreasing list ending with infinity. */ items: [90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, qsTr("don't delete")] dfltIdx: 0 + + accessibleDescription: qsTr("Select the number of days how long downloaded messages are kept in local storage after download.") + onValueModified: settings.setMessageLifeDays(messageLifeSpinBox.val()) } - Text { + AccessibleText { color: datovkaPalette.mid width: parent.width text: if (messageLifeSpinBox.val() === 0) { @@ -110,22 +106,24 @@ Item { } wrapMode: Text.Wrap } - Text { + AccessibleText { id: attachLifeLabel color: datovkaPalette.text text: qsTr("Number of days to keep attachments") } - SpinBoxZeroMax { + AccessibleSpinBoxZeroMax { /* Holds value in days. */ id: attachLifeSpinBox - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize + /* Must be a non-decreasing list ending with infinity. */ items: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, qsTr("like messages")] dfltIdx: 1 + + accessibleDescription: qsTr("Select the number of days how long message content is kept in local storage after download.") + onValueModified: settings.setAttachmentLifeDays(attachLifeSpinBox.val()) } - Text { + AccessibleText { color: datovkaPalette.mid width: parent.width text: if (attachLifeSpinBox.val() === 0) { @@ -135,44 +133,46 @@ Item { } wrapMode: Text.Wrap } - Text { + AccessibleText { id: zfoLifeLabel color: datovkaPalette.text text: qsTr("ZFO storage size limit in MB") } - SpinBox { + AccessibleSpinBox { /* Holds value in MBs. */ id: zfoDbSizeSpinBox - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize + from: 0 to: 1000 stepSize: 100 - onValueModified: settings.setZfoDbSizeMBs(zfoDbSizeSpinBox.value) + + accessibleDescription: qsTr("Specify the maximal amount of memory for preserving recently downloaded messages.") + + onValueModified: settings.setZfoDbSizeMBs(zfoDbSizeSpinBox.val()) } - Text { + AccessibleText { color: datovkaPalette.mid width: parent.width - text: if (zfoDbSizeSpinBox.value === 0) { + text: if (zfoDbSizeSpinBox.val() === 0) { qsTr("Message ZFO data won't be automatically stored.") } else { - qsTr("Maximum size of stored message ZFO data is set to %1 MB. Default is %2 MB.").arg(zfoDbSizeSpinBox.value).arg(200) + qsTr("Maximum size of stored message ZFO data is set to %1 MB. Default is %2 MB.").arg(zfoDbSizeSpinBox.val()).arg(200) } wrapMode: Text.Wrap } - Text { + AccessibleText { id: dbPathLabel color: datovkaPalette.text text: qsTr("Databases location") } - Text { + AccessibleText { id: dbPathText color: datovkaPalette.mid width: parent.width wrapMode: Text.Wrap text: "" } - Button { + AccessibleButton { id: dbPathButton font.pointSize: defaultTextFont.font.pointSize height: inputItemHeight @@ -190,7 +190,7 @@ Item { } } } - Button { + AccessibleButton { id: dbResetPathButton text: qsTr("Set default") font.pointSize: defaultTextFont.font.pointSize @@ -208,12 +208,12 @@ Item { } } } - Text { + AccessibleText { id: vacuumLabel color: datovkaPalette.text text: qsTr("Clean up all databases") } - Button { + AccessibleButton { id: vacuumButton font.pointSize: defaultTextFont.font.pointSize height: inputItemHeight @@ -222,7 +222,7 @@ Item { files.vacuumFileDbs() } } - Text { + AccessibleText { id: vacuumText color: datovkaPalette.mid width: parent.width diff --git a/qml/pages/PageSettingsSync.qml b/qml/pages/PageSettingsSync.qml index 59d3839c105d086962d9ab9b452a66883fcb6291..11511c28b0c536985d0e3bc8637da1e0aa2fd36a 100644 --- a/qml/pages/PageSettingsSync.qml +++ b/qml/pages/PageSettingsSync.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,16 +46,10 @@ Item { PageHeader { id: headerBar title: qsTr("Synchronization settings") - MouseArea { - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: parent.width * backMouseAreaRatio - onClicked: { - pageView.pop(StackView.Immediate) - } + onBackClicked: { + pageView.pop(StackView.Immediate) } - } // PageHeader + } Flickable { id: flickable z: 0 @@ -71,14 +65,14 @@ Item { anchors.right: parent.right anchors.left: parent.left spacing: formItemVerticalSpacing - Switch { + AccessibleSwitch { id: downloadOnlyNewMsgs text: qsTr("Download only newer messages") font.pointSize: defaultTextFont.font.pointSize checked: true onClicked: settings.setDownloadOnlyNewMsgs(downloadOnlyNewMsgs.checked) } - Text { + AccessibleText { id: downloadOnlyNewDescription color: datovkaPalette.mid width: parent.width @@ -87,13 +81,13 @@ Item { qsTr("All available messages (including those in the data vault) will be downloaded.")) wrapMode: Text.Wrap } - Switch { + AccessibleSwitch { id: downloadCompleteMsgs text: qsTr("Download complete messages") font.pointSize: defaultTextFont.font.pointSize onClicked: settings.setDownloadCompleteMsgs(downloadCompleteMsgs.checked) } - Text { + AccessibleText { color: datovkaPalette.mid width: parent.width text: (downloadCompleteMsgs.checked ? diff --git a/res/locale/datovka_cs.ts b/res/locale/datovka_cs.ts index be2c8f1f2264346c9d5226f3bed7ec2fb98bac9f..08f840687ed1d104dc04eb9b4b9c040270799388 100644 --- a/res/locale/datovka_cs.ts +++ b/res/locale/datovka_cs.ts @@ -1,69 +1,132 @@ + + AccessibleSpinBox + + + + Decrease value '%1'. + Snížit hodnotu '%1'. + + + + + Increase value '%1'. + Zvýšit hodnotu '%1'. + + + + AccessibleSpinBoxZeroMax + + + + max + max + + + + + Decrease value '%1'. + Snížit hodnotu '%1'. + + + + + Increase value '%1'. + Zvýšit hodnotu '%1'. + + Accounts - + Remove account: %1 Odstranit účet: %1 - + Do you want to remove the account '%1'? Opravdu chcete odstranit účet '%1'? - + Note: It will also remove all related local databases and account information. Poznámka: Budou odstraněny také všechny odpovídající lokální databáze a informace o účtu. - + Problem while updating account Problém při vytváření účtu - + Account name has not been specified! Název účtu nebyl vyplněn! - + This field must be filled in. Tato položka musí být vyplněna. - + Problem while creating account Problém při vytváření účtu - + User name, password or account name has not been specified! Uživatelské jméno, heslo nebo jméno účtu nebylo vyplněno! - + These fields must be filled in. Tyto položky musí být vyplněny. - + Creating account: %1 Vytváření účtu: %1 - + Account '%1' could not be created. Účet '%1' nemohl být vytvořen. - + Account with user name '%1' already exists. Účet s uživatelským jménem '%1' již existuje. + + DataboxList + + + + View details about data box '%1' (identifier '%2'). + Zobrazit informace o datové schránce '%1' (identifikátor '%2'). + + + + + Deselect data box '%1' (identifier '%2'). + Zrušit výběr datové schránky '%1' (identifikátor '%2'). + + + + + Select data box '%1' (identifier '%2'). + Vybrat datovou schránku '%1' (identifikátor '%2'). + + + + + Remove data box '%1' (identifier '%2'). + Odebrat datovou schránku '%1' (identifikátor '%2'). + + DbWrapper @@ -236,41 +299,83 @@ FileDialogue - - + + Select path Vybrat cestu - - + + + Cancel + Zrušit + + + + + OK + OK + + + + + Select location type + Vyberte typ umístění + + + + Desktop Plocha - - + + Documents Dokumenty - - + + Downloads Stažené - - + + Pictures Obrázky - - + + Temp Dočasné + + + + Up + Nahoru + + + + + Open directory. + Otevřít adresář. + + + + + File is selected. + Soubor je vybrán. + + + + + File is not selected. + Soubor není vybrán. + Files @@ -352,8 +457,8 @@ InputDialogue - - + + QML input dialog QML vstupní dialog @@ -1008,30 +1113,57 @@ MessageDialogue - - + + Question Dotaz - - + + Information Informace - - + + Warning Varování - - + + Critical Kritické + + MessageList + + + + Unread message from sender + Nepřečtená zpráva od odesílatele + + + + + Read message from sender + Přečtená zpráva od odesílatele + + + + + Message to receiver + Zpráva příjemci + + + + + Subject + Předmět + + Messages @@ -1167,16 +1299,22 @@ O Datovce - - + + Datovka - free mobile Data-Box client Datovka - svobodný mobilní klient pro Datové schránky - - - Version - Verze + + + Open application home page. + Otevřít domovskou stránku aplikace. + + + + + Version: %1 + Verze: %1 @@ -1185,23 +1323,29 @@ Tato aplikace poskytuje přístup k datovým schránkám v systému ISDS. Dovoluje Vám stahovat a prohlížet obsah zpráv uvnitř datové schránky. Můžete také, s jistými omezeními, z této aplikace zprávy odesílat. - - + + You may use this application only at your own risk. The association CZ.NIC is not the operator of the data box system. CZ.NIC is not liable for any damage that may be directly or indirectly caused by using this application. Tuto aplikaci můžete používat pouze na vlastní nebezpečí. Sdružení CZ.NIC není provozovatelem systému datových schránek. CZ.NIC neručí za zádné škody, které mohou být přímo či nepřímo způsobeny použitím této aplikace. - - + + If you have problems using this application or want help or you just want more information then start by reading through the information in the <a href="%1">user manual</a> or on the <a href="%2">project web page</a>. Máte-li potíže s používáním této aplikace, potřebujete-li pomoc, či jen chcete další informace, tak začněte pročítáním informací v <a href="%1">uživatelské příručce</a> nebo na <a href="%2">webových stránkách projektu</a>. - - + + Powered by Vyvíjeno + + + + Open home page of the CZ.NIC association. + Otevřít domovskou stránku sdružení CZ.NIC. + PageAccountDetail @@ -1212,8 +1356,14 @@ Informace o účtu - - + + + Refresh information about the account. + Obnovit informace o účtu. + + + + Account info has not been downloaded yet. Informace o účtu nebyly doposud staženy. @@ -1221,23 +1371,71 @@ PageAccountList - - + + + Show information about the application. + Zobrazit informace o aplikaci. + + + + + Synchronise all accounts. + Synchronizovat všechny účty. + + + + + Show application preferences. + Zobrazit nastavení aplikace. + + + + Add a new account Přidat nový účet - - + + + Account '%1'. + Účet '%1'. + + + + + Testing account '%1'. + Testovací účet '%1'. + + + + + Synchronise account '%1'. + Synchronizovat účet '%1'. + + + + Received messages Přijaté zprávy - - + + + Received messages of account '%1'. (Unread %2 of %3 received messages.) + Přijaté zprávy účtu '%1'. (Nepřečteno %2 z %3 přijatých zpráv.) + + + + Sent messages Odeslané zprávy + + + + Sent messages of account '%1'. (Total of %2 sent messages.) + Odeslané zprávy účtu '%1'. (Celkem %2 odeslaných zpráv.) + PageChangePassword @@ -1278,92 +1476,98 @@ Změnit heslo - - + + + Accept changes + Přijmout změny + + + + Old password and new password must be filled! Staré heslo a nové heslo musí být vyplněno! - - + + SMS code must be filled! SMS kód musí být vyplněn! - - + + Security code must be filled! Bezpečnostní kód musí být vyplněn! - - + + Password change failed! Změna hesla se nezdařila! - - + + Wrong password format! The new password must contain at least 8 characters including at least 1 number and at least 1 upper-case letter. Špatný formát hesla! Nové heslo musí obsahovat nejméně 8 znaků, alespoň 1 číslici a alespoň 1 velké písmeno. - - + + The entered new passwords do not match! Nově zadaná hesla si neodpovídají! - - + + Enter the current password and a new password. Zadejte aktuální heslo a nové heslo. - - + + Current password Aktuální heslo - - + + New password Nové heslo - - + + Confirm the new password Potvrďte nové heslo - - + + Send SMS code Zaslat kód SMS - - + + Enter current password for SMS code sending! Zadejte aktuální heslo pro odesílání SMS kódu! - - + + SMS code was sent... SMS kód byl odeslán... - - + + SMS code cannot be sent! Wrong current password or ISDS server is out of service. SMS kód nelze odeslat! Chybné aktuální heslo nebo ISDS server je mimo provoz. - - + + Enter OTP code Zadejte OTP kód @@ -1377,14 +1581,26 @@ Kontakty - - + + + Filter data boxes. + Filtrovat datové schránky. + + + + Set filter Nastavit filtr - - + + + Clear and hide filter field + Vymazat a schovat pole filtru + + + + No databox found in contacts. Nenalezeny žádné schránky. @@ -1392,8 +1608,8 @@ PageDataboxDetail - - + + Databox info Informace o schránce @@ -1407,88 +1623,130 @@ Vyhledat schránku - - + + + Enter sought phrase + Zadejte hledaný text + + + + + Previous + Předchozí + + + + + Next + Následující + + + + Set filter Nastavit filtr - - + + All databoxes Všechny schránky - - + + + Filter data boxes. + Filtrovat datové schránky. + + + + + Search data boxes. + Vyhledat datové schránky. + + + + + Select type of sought data box + Vyberte typ hledané datové schránky + + + + OVM OVM - - + + PO PO - - + + PFO PFO - - + + FO FO - - + + + Select entries to search in + Vyberte položky, ve kterých se bude vyhledávat + + + + All fields Všechny položky - - + + Address Adresa - - + + - - + + Databox ID ID schránky - - - Enter phrase - Zadejte text + + + Clear and hide filter field + Vymazat a schovat pole filtru - - + + No databox found for given search criteria. Nenalezena žádná schránka pro zadaná kritéria. - - - - + + + + Found Nalezeno - - + + Shown Zobrazeno @@ -1496,8 +1754,8 @@ PageHeader - - + + Back Zpět @@ -1505,50 +1763,50 @@ PageMenuAccount - - + + Account properties Vlastnosti účtu - - + + Account settings Nastavení účtu - - + + View account info Zobrazit info o účtu - - + + Create message Vytvořit zprávu - - + + Find databox Vyhledat schránku - - + + Clean up the file database Vyčistit databázi souborů - - + + Change password Změnit heslo - - + + Delete account Odstranit účet @@ -1556,50 +1814,50 @@ PageMenuDatovkaSettings - - + + Settings Nastavení - - + + Add account Přidat účet - - + + Search message Hledat zprávu - - + + General Obecné - - + + Synchronization Synchronizace - - + + Storage Úložiště - - + + Security and PIN Zabezpečení a PIN - - + + User Guide Uživatelská příručka @@ -1607,32 +1865,32 @@ PageMenuMessage - - + + Message Zpráva - - + + Download attachments Stáhnout přílohy - - + + Mark as read Označit jako přečtené - - + + Mark as unread Označit jako nepřečtené - - + + Delete message Smazat zprávu @@ -1640,50 +1898,50 @@ PageMenuMessageDetail - - + + Message Zpráva - - + + Download attachments Stáhnout přílohy - - + + Reply Odpověď - - + + Forward Přeposlat - - + + Use as template Použít jako šablonu - - + + Send attachments by email Odeslat přílohy emailem - - + + Save attachments Uložit přílohy - - + + Delete attachments Smazat přílohy @@ -1691,20 +1949,20 @@ PageMenuMessageList - - + + Message operations Operace se zprávami - - + + Mark messages as read Označit vše jako přečtené - - + + Mark messages as unread Označit vše jako nepřečtené @@ -1754,22 +2012,52 @@ ID zprávy - - + + + Email attachments + Poslat přílohy emailem + + + + + Save attachments. + Uložit přílohy. + + + + + Download message + Stáhnout zprávu + + + + + Show menu of available operations + Zobrazit menu s dostupnými operacemi + + + + Attachments Přílohy - - + + + Open attachment '%1'. + Otevřít přílohu '%1'. + + + + Attachments have not been downloaded yet. Click the icon or this text for their download. Přílohy nebyly doposud staženy. Klikněte na ikonu nebo na tento text pro jejich stažení. - - + + No attachments present. Přílohy nejsou přítomny. @@ -1789,20 +2077,38 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Přijaté zprávy - - + + + Filter messages. + Filtrovat zprávy. + + + + + Show menu of available operations. + Zobrazit menu s dostupnými operacemi. + + + + Set filter Nastavit filtr - - + + + Clear and hide filter field + Vymazat a schovat pole filtru + + + + No messages or have not been downloaded yet. Žádné zprávy nebo doposud nebyly žádné staženy. - - + + No message found that matches filter text '%1'. Nenalezena žádná zpráva odpovídající textu filtru '%1'. @@ -1816,32 +2122,44 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Hledat zprávu - - + + + Search messages. + Hledat zprávy. + + + + + Select type of sought messages + Vyberte typ hledaných zpráv + + + + All messages Všechny zprávy - - + + Received messages Přijaté zprávy - - + + Sent messages Odeslané zprávy - - + + Enter phrase Zadejte text - - + + No message found for given search phrase. Nenalezena žádná zpráva pro uvedený hledaný text. @@ -1885,200 +2203,218 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Vytvořit zprávu - - + + + Send message + Odeslat zprávu + + + + Error during message creation. Chyba při vytváření zprávy. - - + + General Obecné - - + + Recipients Příjemci - - + + Attachments Přílohy - - + + Please fill in the subject of the message, select at least one recipient data box and specify at least one attachment file before sending the message. Před odesláním zprávy vyplňte prosím předmět zprávy, vyberte alespoň jednu datovou schránku příjemce a uveďtě alespoň jeden soubor s přílohou. - - + + Sender account Účet odesílatele - - + + Subject Předmět - - + + Enter subject Zadejte předmět - - + + Include sender identification Přidat identifikaci odesílatele - - + + Personal delivery Doručení do vlastních rukou - - + + Allow acceptance through fiction Povolit doručení fikcí - - + + Send as OVM Poslat v režimu OVM - - + + Pay transfer charges for reply Zaplatit poplatky za odpověď - - + + Add Přidat - - + + Find Najít - - + + Add file Přidat soubor - - + + Select files Vybrat soubory - - + + + Open file '%1'. + Otevřít soubor '%1'. + + + + Local database Lokální databáze - - + + + Remove file '%1' from list. + Odebrat soubor '%1' ze seznamu. + + + + Mandate Pověření - - + + Our reference number Naše číslo jednací - - + + Our file mark Naše spisová značka - - + + Your reference number Vaše číslo jednací - - + + Your file mark Vaše spisová značka - - + + To hands K rukám - - + + par. odst. - - + + Additional Doplňující - - + + Use transfer charges from recipient Využít platbu příjemce za odpověď - - + + let. písm. - - + + Enter our reference number Zadejte jednací číslo - - + + Enter our file mark Zadejte spisovou značku - - + + Enter your reference number Zadejte jednací číslo - - + + Enter your file mark Zadejte spisovou značku - - + + Enter name Zadejte jméno @@ -2086,20 +2422,20 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. PageSettingsAccount - - + + Certificate Certifikát - - + + Choose file Vybrat soubor - - + + Login method Způsob přihlašování @@ -2110,104 +2446,116 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Nový účet - - + + Username + Password Jméno + Heslo - - + + Certificate + Password Certifikát + Heslo - - + + Password + Security code Heslo + Bezpečnostní kód - - + + Password + Security SMS Heslo + SMS kód - - + + Account title Pojmenování účtu - - + + Username Uživatelské jméno - - + + Password Heslo - - + + Enter the login name Zadejte přihlašovací jméno - - + + + Accept changes + Přijmout změny + + + + + Select login method + Vyberte přihlašovací metodu + + + + Enter custom account name Zadejte vlastní název účtu - - + + Enter the password Zadejte heslo - - + + Remember password Zapamatovat si heslo - - + + Test account Testovací účet - - + + Test accounts are used to access the ISDS testing environment. Testovací účty jsou využívány k přístupu do testovacího prostředí ISDS. - - + + Use local storage (database) Použít lokální úložiště (databázi) - - + + Messages and attachments will be locally stored. No active internet connection is needed to access locally stored data. Zprávy a přílohy budou lokálně ukládány. K přístupu lokálně uloženým datům není potřeba aktivního připojení k internetu. - - + + Messages and attachments will be stored only temporarily in memory. These data will be lost on application exit. Zprávy a přílohy budou ukládány pouze dočasně v paměti. Tyto data budou při vypnutí aplikace zahozena. - - + + Account settings Nastavení účtu @@ -2215,50 +2563,62 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. PageSettingsGeneral - - + + General settings Obecné nastavení - - + + Language Jazyk - - + + + Select language + Vyberte jazyk + + + + System Ze systému - - + + Czech Český - - + + English Anglický - - + + Note: Language will be changed after application restart. Poznámka: Jazyk bude změněn až po restartu aplikace. - - + + Font size and application scale Velikost písma a měřítko - - + + + Set application font size + Nastavit velikost fontu + + + + Note: Font size will be changed after application restart. Default is %1. Poznámka: Změna velikosti se projeví až po restartu aplikace. Výchozí hodnota je %1. @@ -2304,77 +2664,89 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Nastavení PINu - - - - + + + Accept changes + Přijmout změny + + + + + + Error: Both new PIN code fields must be filled in! Chyba: Obě pole s novým PIN kódem musí být vyplněna! - - - - + + + + Error: Newly entered PIN codes are different! Chyba: Nově zadané PIN kódy se liší! - - - - + + + + Error: Current PIN code is wrong! Chyba: Současný PIN kód je nesprávný! - - + + Set PIN Nastavit PIN - - + + Change PIN Změnit PIN - - + + Disable PIN Zrušit PIN - - + + Current PIN code Současný PIN kód - - + + New PIN code Nový PIN kód - - + + Confirm new PIN code Potvrdit nový PIN kód - - + + Lock after seconds of inactivity Zamknout po sekundách neaktivity - - + + don't lock nezamykat + + + + Select the number of seconds after which the application is going to be locked. + Vybrat dobu (v sekundách), za jak dlouho se aplikace uzamkne. + PageSettingsStorage @@ -2385,59 +2757,77 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Nastavení úložiště - - + + Number of days to keep messages Počet dní pro uchování zpráv - - + + don't delete nemazat - - + + + Select the number of days how long downloaded messages are kept in local storage after download. + Vybrat dobu (ve dnech), jak dlouho po stažení budou zprávy uchovávány v lokálním úložišti. + + + + Messages won't automatically be deleted from the local storage. Zprávy nebudou automaticky mazány z místního úložiště. - - + + Messages will be locally stored for a period of %1 days since their acceptance time. Default is %2 days. Zprávy budou uchovávány po dobu %1 dnů od doby dodání. Výchozí hodnota je %2 dnů. - - + + Number of days to keep attachments Počet dní pro uchování příloh - - + + like messages jako zprávy - - + + + Select the number of days how long message content is kept in local storage after download. + Vybrat dobu (ve dnech), jak dlouho po stažení bude v lokálním úložišti uchováván obsah zpráv. + + + + Attachments won't be deleted from the local storage as long as the corresponding messages won't be deleted. Přílohy nebudou smazány s místního úložiště po dobu, po kterou nebudou smazány odpovídající zprávy. - - + + Attachments will be locally stored, but no longer than corresponding messages, for a period of %1 days since their download time. Default is %2 days. Přílohy budou uchovány po dobu %1 dnů od jejich stažení, ale ne déle než odpovídající zprávy. Výchozí hodnota je %2 dnů. - - + + ZFO storage size limit in MB Velikost úložiště ZFO v MB + + + + Specify the maximal amount of memory for preserving recently downloaded messages. + Určit maximální množství paměti určené pro uchovávání nedávno stažených zpráv. + @@ -2496,38 +2886,38 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Nastavení synchronizace - - + + Download only newer messages Stahovat pouze novější zprávy - - + + Only messages which are not older than 90 days will be downloaded. Budou stahovány jen zprávy, které nejsou starší 90 dnů. - - + + All available messages (including those in the data vault) will be downloaded. Budou stahovány všechny zprávy (včetně těch v datovém trezoru). - - + + Download complete messages Stahovat kompletní zprávy - - + + Complete messages will be downloaded during account synchronisation. This may be slow. V průběhu synchronizace účtů budou stahovány kompletní zprávy. To může být pomalé. - - + + Only message envelopes will be downloaded during account synchronisation. V průběhu synchronizace účtů budou stahovány pouze obálky zpráv. @@ -2966,32 +3356,32 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. ID schránky - + Data box application Aplikace pro datové schránky - + ZFO file to be viewed. Soubor zfo, který má být zobrazen. - + Last synchronisation: %1 Poslední synchronizace: %1 - + Security problem Bezpečnostní problém - + OpenSSL support is required! Je nezbytná podpora OpenSSL! - + The device does not support OpenSSL. The application won't work correctly. Vaše zařízení nepodporuje OpenSSL. Aplikace nebude pracovat správně. @@ -3011,15 +3401,6 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. Interní chyba - - SpinBoxZeroMax - - - - max - max - - TaskFindDataboxFulltext @@ -3048,26 +3429,26 @@ Klikněte na ikonu nebo na tento text pro jejich stažení. main - - + + Version Verze - - + + Enter PIN code Zadejte PIN kód - - + + Wrong PIN code! Špatný PIN kód! - - + + Enter Vstoupit diff --git a/res/locale/datovka_en.ts b/res/locale/datovka_en.ts index ca329ff2c2c194d3b2f4af39e0486adea663671e..5d3e2de7656a26447391152e7f10f04536259429 100644 --- a/res/locale/datovka_en.ts +++ b/res/locale/datovka_en.ts @@ -1,69 +1,132 @@ + + AccessibleSpinBox + + + + Decrease value '%1'. + + + + + + Increase value '%1'. + + + + + AccessibleSpinBoxZeroMax + + + + max + + + + + + Decrease value '%1'. + + + + + + Increase value '%1'. + + + Accounts - + Remove account: %1 - + Do you want to remove the account '%1'? - + Note: It will also remove all related local databases and account information. - + Problem while updating account - + Account name has not been specified! - + This field must be filled in. - + Problem while creating account - + User name, password or account name has not been specified! - + These fields must be filled in. - + Creating account: %1 - + Account '%1' could not be created. - + Account with user name '%1' already exists. + + DataboxList + + + + View details about data box '%1' (identifier '%2'). + + + + + + Deselect data box '%1' (identifier '%2'). + + + + + + Select data box '%1' (identifier '%2'). + + + + + + Remove data box '%1' (identifier '%2'). + + + DbWrapper @@ -236,41 +299,83 @@ FileDialogue - - + + Select path - - + + + Cancel + + + + + + OK + + + + + + Select location type + + + + + Desktop - - + + Documents - - + + Downloads - - + + Pictures - - + + Temp + + + + Up + + + + + + Open directory. + + + + + + File is selected. + + + + + + File is not selected. + + Files @@ -352,8 +457,8 @@ InputDialogue - - + + QML input dialog @@ -1007,31 +1112,58 @@ MessageDialogue - - - - Question - - - Information + Question - Warning + Information + Warning + + + + + Critical + + MessageList + + + + Unread message from sender + + + + + + Read message from sender + + + + + + Message to receiver + + + + + + Subject + + + Messages @@ -1167,15 +1299,21 @@ - - + + Datovka - free mobile Data-Box client - - - Version + + + Open application home page. + + + + + + Version: %1 @@ -1185,23 +1323,29 @@ - - + + You may use this application only at your own risk. The association CZ.NIC is not the operator of the data box system. CZ.NIC is not liable for any damage that may be directly or indirectly caused by using this application. - - + + If you have problems using this application or want help or you just want more information then start by reading through the information in the <a href="%1">user manual</a> or on the <a href="%2">project web page</a>. - - + + Powered by + + + + Open home page of the CZ.NIC association. + + PageAccountDetail @@ -1212,8 +1356,14 @@ - - + + + Refresh information about the account. + + + + + Account info has not been downloaded yet. @@ -1221,23 +1371,71 @@ PageAccountList - - + + + Show information about the application. + + + + + + Synchronise all accounts. + + + + + + Show application preferences. + + + + + Add a new account - - + + + Account '%1'. + + + + + + Testing account '%1'. + + + + + + Synchronise account '%1'. + + + + + Received messages - - + + + Received messages of account '%1'. (Unread %2 of %3 received messages.) + + + + + Sent messages + + + + Sent messages of account '%1'. (Total of %2 sent messages.) + + PageChangePassword @@ -1278,92 +1476,98 @@ - - + + + Accept changes + + + + + Old password and new password must be filled! - - + + SMS code must be filled! - - + + Security code must be filled! - - + + Password change failed! - - + + Wrong password format! The new password must contain at least 8 characters including at least 1 number and at least 1 upper-case letter. - - + + The entered new passwords do not match! - - + + Enter the current password and a new password. - - + + Current password - - + + New password - - + + Confirm the new password - - + + Send SMS code - - + + Enter current password for SMS code sending! - - + + SMS code was sent... - - + + SMS code cannot be sent! Wrong current password or ISDS server is out of service. - - + + Enter OTP code @@ -1377,14 +1581,26 @@ - - + + + Filter data boxes. + + + + + Set filter - - + + + Clear and hide filter field + + + + + No databox found in contacts. @@ -1392,8 +1608,8 @@ PageDataboxDetail - - + + Databox info @@ -1407,88 +1623,130 @@ - - + + + Enter sought phrase + + + + + + Previous + + + + + + Next + + + + + Set filter - - + + All databoxes - - + + + Filter data boxes. + + + + + + Search data boxes. + + + + + + Select type of sought data box + + + + + OVM - - + + PO - - + + PFO - - + + FO - - + + + Select entries to search in + + + + + All fields - - + + Address - - + + - - + + Databox ID - - - Enter phrase + + + Clear and hide filter field - - + + No databox found for given search criteria. - - - - + + + + Found - - + + Shown @@ -1496,8 +1754,8 @@ PageHeader - - + + Back @@ -1505,50 +1763,50 @@ PageMenuAccount - - + + Account properties - - + + Account settings - - + + View account info - - + + Create message - - + + Find databox - - + + Clean up the file database - - + + Change password - - + + Delete account @@ -1556,50 +1814,50 @@ PageMenuDatovkaSettings - - + + Settings - - + + Add account - - + + Search message - - + + General - - + + Synchronization - - + + Storage - - + + Security and PIN - - + + User Guide @@ -1607,32 +1865,32 @@ PageMenuMessage - - + + Message - - + + Download attachments - - + + Mark as read - - + + Mark as unread - - + + Delete message @@ -1640,50 +1898,50 @@ PageMenuMessageDetail - - + + Message - - + + Download attachments - - + + Reply - - + + Forward - - + + Use as template - - + + Send attachments by email - - + + Save attachments - - + + Delete attachments @@ -1691,20 +1949,20 @@ PageMenuMessageList - - + + Message operations - - + + Mark messages as read - - + + Mark messages as unread @@ -1754,21 +2012,51 @@ - - + + + Email attachments + + + + + + Save attachments. + + + + + + Download message + + + + + + Show menu of available operations + + + + + Attachments - - + + + Open attachment '%1'. + + + + + Attachments have not been downloaded yet. Click the icon or this text for their download. - - + + No attachments present. @@ -1788,20 +2076,38 @@ Click the icon or this text for their download. - - + + + Filter messages. + + + + + + Show menu of available operations. + + + + + Set filter - - + + + Clear and hide filter field + + + + + No messages or have not been downloaded yet. - - + + No message found that matches filter text '%1'. @@ -1815,32 +2121,44 @@ Click the icon or this text for their download. - - + + + Search messages. + + + + + + Select type of sought messages + + + + + All messages - - + + Received messages - - + + Sent messages - - + + Enter phrase - - + + No message found for given search phrase. @@ -1884,200 +2202,218 @@ Click the icon or this text for their download. - - + + + Send message + + + + + Error during message creation. - - + + General - - + + Recipients - - + + Attachments - - + + Additional - - + + Sender account - - + + Subject - - + + Enter subject - - + + Include sender identification - - + + Personal delivery - - + + Allow acceptance through fiction - - + + Send as OVM - - + + Pay transfer charges for reply - - + + Add - - + + Find - - + + Add file - - + + Select files - - + + + Open file '%1'. + + + + + Local database - - + + + Remove file '%1' from list. + + + + + Mandate - - + + Our reference number - - + + Our file mark - - + + Your reference number - - + + Your file mark - - + + To hands - - + + par. - - + + Please fill in the subject of the message, select at least one recipient data box and specify at least one attachment file before sending the message. - - + + Use transfer charges from recipient - - + + let. - - + + Enter our reference number - - + + Enter our file mark - - + + Enter your reference number - - + + Enter your file mark - - + + Enter name @@ -2085,20 +2421,20 @@ Click the icon or this text for their download. PageSettingsAccount - - + + Certificate - - + + Choose file - - + + Login method @@ -2109,104 +2445,116 @@ Click the icon or this text for their download. - - + + + Accept changes + + + + + + Select login method + + + + + Username + Password - - + + Certificate + Password - - + + Password + Security code - - + + Password + Security SMS - - + + Enter custom account name - - + + Username - - + + Enter the login name - - + + Password - - + + Enter the password - - + + Account title - - + + Remember password - - + + Test account - - + + Test accounts are used to access the ISDS testing environment. - - + + Use local storage (database) - - + + Messages and attachments will be locally stored. No active internet connection is needed to access locally stored data. - - + + Messages and attachments will be stored only temporarily in memory. These data will be lost on application exit. - - + + Account settings @@ -2214,50 +2562,62 @@ Click the icon or this text for their download. PageSettingsGeneral - - + + General settings - - + + Language - - + + + Select language + + + + + System - - + + Czech - - + + English - - + + Note: Language will be changed after application restart. - - + + Font size and application scale - - + + + Set application font size + + + + + Note: Font size will be changed after application restart. Default is %1. @@ -2303,77 +2663,89 @@ Click the icon or this text for their download. - - - - + + + Accept changes + + + + + + + Error: Both new PIN code fields must be filled in! - - - - + + + + Error: Newly entered PIN codes are different! - - - - + + + + Error: Current PIN code is wrong! - - + + Set PIN - - + + Change PIN - - + + Disable PIN - - + + Current PIN code - - + + New PIN code - - + + Confirm new PIN code - - + + Lock after seconds of inactivity - - + + don't lock + + + + Select the number of seconds after which the application is going to be locked. + + PageSettingsStorage @@ -2384,59 +2756,77 @@ Click the icon or this text for their download. - - + + Number of days to keep messages - - + + don't delete - - + + + Select the number of days how long downloaded messages are kept in local storage after download. + + + + + Messages won't automatically be deleted from the local storage. - - + + Messages will be locally stored for a period of %1 days since their acceptance time. Default is %2 days. - - + + Number of days to keep attachments - - + + like messages - - + + + Select the number of days how long message content is kept in local storage after download. + + + + + Attachments won't be deleted from the local storage as long as the corresponding messages won't be deleted. - - + + Attachments will be locally stored, but no longer than corresponding messages, for a period of %1 days since their download time. Default is %2 days. - - + + ZFO storage size limit in MB + + + + Specify the maximal amount of memory for preserving recently downloaded messages. + + @@ -2495,38 +2885,38 @@ Click the icon or this text for their download. - - + + Download only newer messages - - + + Only messages which are not older than 90 days will be downloaded. - - + + All available messages (including those in the data vault) will be downloaded. - - + + Download complete messages - - + + Complete messages will be downloaded during account synchronisation. This may be slow. - - + + Only message envelopes will be downloaded during account synchronisation. @@ -2965,32 +3355,32 @@ Click the icon or this text for their download. - + Data box application - + ZFO file to be viewed. - + Last synchronisation: %1 - + Security problem - + OpenSSL support is required! - + The device does not support OpenSSL. The application won't work correctly. @@ -3010,15 +3400,6 @@ Click the icon or this text for their download. - - SpinBoxZeroMax - - - - max - - - TaskFindDataboxFulltext @@ -3047,26 +3428,26 @@ Click the icon or this text for their download. main - - + + Version - - + + Enter PIN code - - + + Wrong PIN code! - - + + Enter diff --git a/res/qml.qrc b/res/qml.qrc index b3e1512abf0d7f0d01a0754f6ebdc3be30e6b0fd..6fe75e7c387e565a7ab03359b876220f8515d0a3 100644 --- a/res/qml.qrc +++ b/res/qml.qrc @@ -100,13 +100,26 @@ datovka.png cznic.png ui/datovka@2x.png + ../qml/components/AccessibleButton.qml + ../qml/components/AccessibleComboBox.qml + ../qml/components/AccessibleImageButton.qml + ../qml/components/AccessibleMenu.qml + ../qml/components/AccessibleOverlaidImageButton.qml + ../qml/components/AccessibleSpinBox.qml + ../qml/components/AccessibleSpinBoxZeroMax.qml + ../qml/components/AccessibleSwitch.qml + ../qml/components/AccessibleTabButton.qml + ../qml/components/AccessibleText.qml + ../qml/components/AccessibleTextButton.qml + ../qml/components/AccessibleTextField.qml ../qml/components/DataboxList.qml + ../qml/components/FilterBar.qml ../qml/components/InputLineMenu.qml ../qml/components/MessageList.qml ../qml/components/OverlaidImage.qml ../qml/components/PageHeader.qml ../qml/components/ProgressBar.qml - ../qml/components/SpinBoxZeroMax.qml + ../qml/components/ScrollableListView.qml ../qml/dialogues/InputDialogue.qml ../qml/dialogues/FileDialogue.qml ../qml/dialogues/MessageDialogue.qml diff --git a/src/accounts.cpp b/src/accounts.cpp index 465674a8fbf5c6dd951b213132abccecbe55d3ed..252fac91cafd1561612b3bb928af61a3e9baba2f 100644 --- a/src/accounts.cpp +++ b/src/accounts.cpp @@ -90,24 +90,8 @@ void Accounts::getAccountData(const QString &userName) const AcntData &acntData(AccountListModel::globAccounts[userName]); - /* Set login method model index for combobox in QML page. - * See AccountSettingsPage.qml for logim method order. Default is: - * 0 .. Username + Password - * 1 .. Certificate + Password - * 2 .. Password + Security code - * 3 .. Password + Security SMS - */ - int qmlLoginMethodIndex = 0; - if (acntData.loginMethod() == USERNAME_PWD_CERT) { - qmlLoginMethodIndex = 1; - } else if (acntData.loginMethod() == USERNAME_PWD_HOTP) { - qmlLoginMethodIndex = 2; - } else if (acntData.loginMethod() == USERNAME_PWD_TOTP) { - qmlLoginMethodIndex = 3; - } - emit sendAccountData(acntData.accountName(), userName, - qmlLoginMethodIndex, acntData.password(), acntData.isTestAccount(), + acntData.loginMethod(), acntData.password(), acntData.isTestAccount(), acntData.rememberPwd(), acntData.storeToDisk(), acntData.certPath()); } diff --git a/src/accounts.h b/src/accounts.h index 731bfc04673fd8511d1b21a7d4857c1781458164..7586431144f3132271fd467bfeae8e87390c3fdd 100644 --- a/src/accounts.h +++ b/src/accounts.h @@ -156,7 +156,7 @@ signals: * @param[in] certPath Certificate path (can be null). */ void sendAccountData(QString acntName, QString userName, - int loginMethod, QString password, bool isTestAccount, + QString loginMethod, QString password, bool isTestAccount, bool rememberPwd, bool storeToDisk, QString certPath); /*! diff --git a/src/main.cpp b/src/main.cpp index 9f0b27b8650c7777378850ea8f0ee6358413c456..abffacd6a5cfb460fb4feedf964ad24e822e13b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 CZ.NIC + * Copyright (C) 2014-2018 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,6 +52,7 @@ #include "src/qml_interaction/interaction_filesystem.h" #include "src/qml_interaction/interaction_zfo_file.h" #include "src/qml_interaction/message_envelope.h" +#include "src/qml_interaction/string_manipulation.h" #include "src/settings.h" #include "src/setwrapper.h" #include "src/sqlite/db_tables.h" @@ -122,13 +123,26 @@ const struct QmlTypeEntry qmlPages[] = { */ static const struct QmlTypeEntry qmlComponents[] = { + { "AccessibleButton", 1, 0 }, + { "AccessibleComboBox", 1, 0 }, + { "AccessibleImageButton", 1, 0 }, + { "AccessibleMenu", 1, 0 }, + { "AccessibleOverlaidImageButton", 1, 0 }, + { "AccessibleSpinBox", 1, 0 }, + { "AccessibleSpinBoxZeroMax", 1, 0 }, + { "AccessibleSwitch", 1, 0 }, + { "AccessibleTabButton", 1, 0 }, + { "AccessibleText", 1, 0 }, + { "AccessibleTextButton", 1, 0 }, + { "AccessibleTextField", 1, 0 }, { "DataboxList", 1, 0 }, + { "FilterBar", 1, 0 }, { "InputLineMenu", 1, 0 }, { "MessageList", 1, 0 }, { "OverlaidImage", 1, 0 }, { "PageHeader", 1, 0 }, { "ProgressBar", 1, 0 }, - { "SpinBoxZeroMax", 1, 0 }, + { "ScrollableListView", 1, 0 }, { NULL, 0, 0 } }; @@ -273,6 +287,7 @@ int main(int argc, char *argv[]) Files files; IsdsWrapper isds; GlobalSettingsQmlWrapper settings; + StringManipulation strManipulation; Zfo zfo; /* Connect slot for isds cxt delete when account was deleted or updated */ @@ -332,6 +347,7 @@ int main(int argc, char *argv[]) ctx->setContextProperty("accounts", &accounts); ctx->setContextProperty("files", &files); ctx->setContextProperty("settings", &settings); + ctx->setContextProperty("strManipulation", &strManipulation); ctx->setContextProperty("locker", &locker); ctx->setContextProperty("interactionZfoFile", &interactionZfoFile); ctx->setContextProperty("dlgEmitter", QmlDlgHelper::dlgEmitter); diff --git a/src/qml_interaction/string_manipulation.cpp b/src/qml_interaction/string_manipulation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..928d422f87ab60f1e4b1a3a21f75a8740fbc51c3 --- /dev/null +++ b/src/qml_interaction/string_manipulation.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#include /* QQuickTextDocument? */ + +#include "src/qml_interaction/string_manipulation.h" + +QString StringManipulation::removeRich(const QString &inStr) +{ + QTextDocument doc; + doc.setHtml(inStr); + + return doc.toPlainText(); +} diff --git a/src/qml_interaction/string_manipulation.h b/src/qml_interaction/string_manipulation.h new file mode 100644 index 0000000000000000000000000000000000000000..c4854937c3e97e9a12dae2747bea442298b23394 --- /dev/null +++ b/src/qml_interaction/string_manipulation.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#ifndef _STRING_MANIPULATION_H_ +#define _STRING_MANIPULATION_H_ + +#include +#include + +/*! + * @brief Convenience class for string manipulation. + */ +class StringManipulation : public QObject { + Q_OBJECT + +public: + /*! + * @brief Converts rich text to plain text. + * + * @param[in] inStr String that may contain rich text tags. + * @return Plain text. + */ + Q_INVOKABLE static + QString removeRich(const QString &inStr); +}; + +#endif /* _STRING_MANIPULATION_H_ */