Commit f1f3acdd authored by Urs Fleisch's avatar Urs Fleisch
Browse files

QML: Settings page.

parent 101726f8
import QtQuick 2.2
//import "ComponentsQtQuick" //@!Ubuntu
import Ubuntu.Components 1.1 //@Ubuntu
import Ubuntu.Components.Popups 1.0 //@Ubuntu
import Ubuntu.Components.ListItems 1.0 //@Ubuntu
import Kid3App 1.0
Page {
id: page
signal clicked(int index)
property list<SettingsElement> model
Item {
anchors.fill: parent
Component {
id: booleanDelegate
Standard {
text: _modelData.name
control: CheckBox {
id: checkField
checked: _modelData.value
onClicked: _modelData.value = checked
}
}
}
Component {
id: stringDelegate
Standard {
text: _modelData.name
control: TextField {
width: constants.gu(45)
text: _modelData.value
onAccepted: {
focus = false
}
onActiveFocusChanged: {
if (!activeFocus) {
_modelData.value = text
}
}
}
}
}
Component {
id: stringSelectionDelegate
Standard {
text: _modelData.name
control: ComboBox {
width: constants.gu(45)
dropDownParent: root
currentText: _modelData.value
model: _modelData.dropDownModel
onCurrentTextChanged: _modelData.value = currentText
}
}
}
Component {
id: numberDelegate
Standard {
text: _modelData.name
control: TextField {
width: constants.gu(45)
text: _modelData.value
onAccepted: {
focus = false
}
onActiveFocusChanged: {
if (!activeFocus) {
var nr = parseInt(text)
if (!isNaN(nr)) {
_modelData.value = nr
}
}
}
}
}
}
Component {
id: numberSelectionDelegate
Standard {
text: _modelData.name
control: ComboBox {
width: constants.gu(45)
dropDownParent: root
currentIndex: _modelData.value
model: _modelData.dropDownModel
onCurrentIndexChanged: _modelData.value = currentIndex
}
}
}
Component {
id: clickDelegate
Standard {
text: _modelData.name
progression: true
onClicked: page.clicked(_index)
}
}
ListView {
id: listView
anchors.fill: parent
model: page.model
delegate: Loader {
width: ListView.view.width
height: constants.rowHeight
property int _index: index
property variant _modelData: modelData
sourceComponent:
if (typeof modelData.value === "boolean")
booleanDelegate
else if (typeof modelData.value === "string")
if (modelData.dropDownModel)
stringSelectionDelegate
else
stringDelegate
else if (typeof modelData.value === "number")
if (modelData.dropDownModel)
numberSelectionDelegate
else
numberDelegate
else
clickDelegate
}
}
}
}
......@@ -27,6 +27,9 @@ set(qml_QML_SRCS
NumberTracksDialog.qml
FilterPage.qml
BatchImportPage.qml
AbstractSettingsPage.qml
SettingsElement.qml
SettingsPage.qml
ComponentsQtQuick/ActionList.qml
ComponentsQtQuick/Action.qml
ComponentsQtQuick/ActionSelectionPopover.qml
......
......@@ -8,7 +8,8 @@ Item {
property alias currentText: selectedItemText.text
property alias currentIndex: dropDown.currentIndex
height: constants.gu(4)
implicitWidth: constants.gu(25)
implicitHeight: constants.gu(4)
Rectangle {
id: selectedItem
......
......@@ -4,18 +4,23 @@ Rectangle {
id: emptyListItem
property bool selected: false
property bool __acceptEvents: true
property alias __mouseArea: mouseArea
signal clicked()
width: parent.width
width: parent ? parent.width : constants.gu(31)
height: constants.rowHeight
color: selected
? constants.palette.highlight : constants.palette.window
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: {
emptyListItem.clicked()
if (emptyListItem.__acceptEvents) {
emptyListItem.clicked()
}
}
}
}
......@@ -14,8 +14,15 @@ Item {
if (currentPage) {
currentPage.visible = false
}
stack.push(page)
currentPage = page
if (page.createObject) {
currentPage = page.createObject(pageStack)
} else if (typeof page === "string") {
currentPage = Qt.createComponent(page).createObject(pageStack);
} else {
currentPage = page
}
var pageArray = [ page, currentPage ]
stack.push(pageArray)
currentPage.visible = true
elements = stack
}
......@@ -24,9 +31,18 @@ Item {
if (currentPage) {
var stack = elements
currentPage.visible = false
var pageArray = stack[stack.length - 1]
if (pageArray[0].createObject || typeof pageArray[0] === "string") {
if (pageArray[1].destroy) {
// Strange, only possible with QtQuick 2.
pageArray[1].destroy()
} else if (currentPage.destroy) {
currentPage.destroy()
}
}
stack.pop()
if (stack.length > 0) {
currentPage = stack[stack.length - 1]
currentPage = stack[stack.length - 1][1]
currentPage.visible = true
} else {
currentPage = null
......
import QtQuick 2.2
Empty {
id: emptyListItem
id: listItem
property alias text: textLabel.text
property bool progression
property alias control: controlContainer.control
__acceptEvents: false
Text {
id: textLabel
anchors.left: parent.left
anchors.right: progressionLabel.left
anchors.right: controlContainer.left
anchors.verticalCenter: parent.verticalCenter
anchors.margins: constants.margins
color: selected
? constants.palette.highlightedText :constants.palette.text
}
Item {
id: controlContainer
property Item control
width: control ? control.width : undefined
height: control ? control.height : undefined
anchors.right: progressionLabel.left
anchors.verticalCenter: parent.verticalCenter
anchors.margins: constants.margins
onControlChanged: {
if (control) control.parent = controlContainer;
}
Connections {
target: listItem.__mouseArea
onClicked: {
if (control && listItem.__mouseArea.mouseX < progressionLabel.x) {
if (control.enabled && control.hasOwnProperty("clicked"))
control.clicked();
} else {
listItem.clicked();
}
}
}
}
Text {
id: progressionLabel
anchors.right: parent.right
......
......@@ -6,7 +6,8 @@ FocusScope {
property alias text: textInput.text
signal accepted()
height: constants.rowHeight
implicitWidth: constants.gu(25)
implicitHeight: constants.rowHeight
Rectangle {
anchors.fill: parent
color: constants.editColor
......
import QtQuick 2.2
QtObject {
property string name
property variant value
property variant dropDownModel
}
import QtQuick 2.2
//import "ComponentsQtQuick" //@!Ubuntu
import Ubuntu.Components 1.1 //@Ubuntu
import Ubuntu.Components.Popups 1.0 //@Ubuntu
import Ubuntu.Components.ListItems 1.0 //@Ubuntu
import Kid3App 1.0
AbstractSettingsPage {
id: page
title: qsTr("Settings")
model: [
SettingsElement { name: qsTr("Tags") },
SettingsElement { name: qsTr("Files") },
SettingsElement { name: qsTr("Plugins") }
]
onClicked: pageStack.push(
[ tagsPage, filesPage, pluginsPage ][index])
Component {
id: tagsPage
AbstractSettingsPage {
title: qsTr("Tags")
visible: false
model: [
SettingsElement {
name: qsTr("Mark truncated fields")
},
SettingsElement {
name: qsTr("ID3v1 text encoding")
dropDownModel: configs.tagConfig().getTextEncodingV1Names()
},
SettingsElement {
name: qsTr("ID3v2 text encoding")
dropDownModel: configs.tagConfig().getTextEncodingNames()
},
SettingsElement {
name: qsTr("Use track/total number of tracks format")
},
SettingsElement {
name: qsTr("Genre as text instead of numeric string")
},
SettingsElement {
name: qsTr("Version used for new ID3v2 tags")
dropDownModel: configs.tagConfig().getId3v2VersionNames()
},
SettingsElement {
name: qsTr("Ogg/Vorbis comment field name")
dropDownModel: configs.tagConfig().getCommentNames()
},
SettingsElement {
name: qsTr("Ogg/Vorbis picture field name")
dropDownModel: configs.tagConfig().getPictureNames()
},
SettingsElement {
name: qsTr("Mark if picture larger than maxium size")
},
SettingsElement {
name: qsTr("Picture maximum size (bytes)")
},
SettingsElement {
name: qsTr("Show only custom genres")
},
SettingsElement {
name: qsTr("Case conversion")
dropDownModel: configs.tagFormatConfig().getCaseConversionNames()
},
SettingsElement {
name: qsTr("Locale")
dropDownModel: configs.tagFormatConfig().getLocaleNames()
},
SettingsElement {
name: qsTr("String replacement")
}
]
onActiveChanged: {
var tagCfg = configs.tagConfig()
var fmtCfg = configs.tagFormatConfig()
if (active) {
model[0].value = tagCfg.markTruncations
model[1].value = tagCfg.textEncodingV1Index
model[2].value = tagCfg.textEncoding
model[3].value = tagCfg.enableTotalNumberOfTracks
model[4].value = tagCfg.genreNotNumeric
model[5].value = tagCfg.id3v2Version
model[6].value = tagCfg.commentName
model[7].value = tagCfg.pictureNameIndex
model[8].value = tagCfg.markOversizedPictures
model[9].value = tagCfg.maximumPictureSize
model[10].value = tagCfg.onlyCustomGenres
model[11].value = fmtCfg.caseConversion
model[12].value =
model[12].dropDownModel.indexOf(fmtCfg.localeName) === -1
? model[12].dropDownModel[0] : fmtCfg.localeName
model[13].value = fmtCfg.strRepEnabled
} else {
tagCfg.markTruncations = model[0].value
tagCfg.textEncodingV1Index = model[1].value
tagCfg.textEncoding = model[2].value
tagCfg.enableTotalNumberOfTracks = model[3].value
tagCfg.genreNotNumeric = model[4].value
tagCfg.id3v2Version = model[5].value
tagCfg.commentName = model[6].value
tagCfg.pictureNameIndex = model[7].value
tagCfg.markOversizedPictures = model[8].value
tagCfg.maximumPictureSize = model[9].value
tagCfg.onlyCustomGenres = model[10].value
fmtCfg.caseConversion = model[11].value
fmtCfg.localeName =
model[12].dropDownModel.indexOf(model[12].value) > 0
? model[12].value : ""
fmtCfg.strRepEnabled = model[13].value
}
}
}
}
Component {
id: filesPage
AbstractSettingsPage {
title: qsTr("Files")
visible: false
model: [
SettingsElement {
name: qsTr("Load last-opened files")
},
SettingsElement {
name: qsTr("Preserve file timestamp")
},
SettingsElement {
name: qsTr("Mark changes")
},
SettingsElement {
name: qsTr("Format while editing")
},
SettingsElement {
name: qsTr("Case conversion")
dropDownModel: configs.filenameFormatConfig().getCaseConversionNames()
},
SettingsElement {
name: qsTr("Locale")
dropDownModel: configs.filenameFormatConfig().getLocaleNames()
},
SettingsElement {
name: qsTr("String replacement")
},
SettingsElement {
name: qsTr("Filename for cover")
},
SettingsElement {
name: qsTr("To filename format")
dropDownModel: configs.fileConfig().toFilenameFormats
},
SettingsElement {
name: qsTr("From filename format")
dropDownModel: configs.fileConfig().fromFilenameFormats
}
]
onActiveChanged: {
var fileCfg = configs.fileConfig()
var fmtCfg = configs.filenameFormatConfig()
if (active) {
model[0].value = fileCfg.loadLastOpenedFile
model[1].value = fileCfg.preserveTime
model[2].value = fileCfg.markChanges
model[3].value = fmtCfg.formatWhileEditing
model[4].value = fmtCfg.caseConversion
model[5].value =
model[5].dropDownModel.indexOf(fmtCfg.localeName) === -1
? model[5].dropDownModel[0] : fmtCfg.localeName
model[6].value = fmtCfg.strRepEnabled
model[7].value = fileCfg.defaultCoverFileName
model[8].value = fileCfg.toFilenameFormat
model[9].value = fileCfg.fromFilenameFormat
} else {
fileCfg.loadLastOpenedFile = model[0].value
fileCfg.preserveTime = model[1].value
fileCfg.markChanges = model[2].value
fmtCfg.formatWhileEditing = model[3].value
fmtCfg.caseConversion = model[4].value
fmtCfg.localeName =
model[5].dropDownModel.indexOf(model[5].value) > 0
? model[5].value : ""
fmtCfg.strRepEnabled = model[6].value
fileCfg.defaultCoverFileName = model[7].value
fileCfg.toFilenameFormat = model[8].value
fileCfg.fromFilenameFormat = model[9].value
}
}
}
}
Component {
id: pluginsPage
AbstractSettingsPage {
title: qsTr("Plugins")
visible: false
onActiveChanged: {
var tagCfg = configs.tagConfig()
var importCfg = configs.importConfig()
if (active) {
var disabledTagPlugins = tagCfg.disabledPlugins
var disabledImportPlugins = importCfg.disabledPlugins
for (var i = 0; i < model.length; ++i) {
var name = model[i].name
model[i].value =
disabledTagPlugins.indexOf(name) === -1 &&
disabledImportPlugins.indexOf(name) === -1
}
} else {
var availableTagPlugins = tagCfg.availablePlugins
var availableImportPlugins = importCfg.availablePlugins
var disabledTagPlugins = []
var disabledImportPlugins = []
for (var i = 0; i < model.length; ++i) {
if (model[i].value === false) {
var name = model[i].name
if (availableTagPlugins.indexOf(name) !== -1) {
disabledTagPlugins.push(name)
} else if (availableImportPlugins.indexOf(name) !== -1) {
disabledImportPlugins.push(name)
}
}
}
tagCfg.disabledPlugins = disabledTagPlugins
importCfg.disabledPlugins = disabledImportPlugins
}
}
Component.onCompleted: {
// A deep copy is necessary in QtQuick 2 because of QTBUG-33149
// (concat does not work with QStringList) and to avoid modification of
// the original list in the config.
var availablePlugins = configs.tagConfig().availablePlugins.slice()
availablePlugins = availablePlugins.concat(
configs.importConfig().availablePlugins)
var settingsModel = []
var elementComponent = Qt.createComponent("SettingsElement.qml")
for (var i = 0; i < availablePlugins.length; ++i) {
var elementObj = elementComponent.createObject(null)
elementObj.name = availablePlugins[i]
settingsModel.push(elementObj)
}
model = settingsModel
}
}
}
}
......@@ -96,6 +96,10 @@ MainView {
popover: mainMenuPopover
}
actions: ActionList {
Action {
text: qsTr("Settings")
onTriggered: pageStack.push(settingsPage)
}
Action {
text: qsTr("Apply Filename Format")
onTriggered: app.applyFilenameFormat()
......@@ -180,6 +184,10 @@ MainView {
id: batchImportPage
visible: false
}
SettingsPage {
id: settingsPage
visible: false
}
}
Component.onCompleted: {
......
......@@ -27,8 +27,8 @@
#include "formatconfig.h"
#include "config.h"
#include <QString>
#include <QStringList>
#include <QLocale>
#include <QCoreApplication>
#include "generalconfig.h"
#include "frame.h"
......@@ -403,6 +403,40 @@ void FormatConfig::setFormatWhileEditing(bool formatWhileEditing)
}
}
/**
* String list of case conversion names.
*/
QStringList FormatConfig::getCaseConversionNames()
{
static const char* const names[NumCaseConversions] = {
QT_TRANSLATE_NOOP("@default", "No changes"),
QT_TRANSLATE_NOOP("@default", "All lowercase"),
QT_TRANSLATE_NOOP("@default", "All uppercase"),
QT_TRANSLATE_NOOP("@default", "First letter uppercase"),
QT_TRANSLATE_NOOP("@default", "All first letters uppercase")
};
QStringList strs;
#if QT_VERSION >= 0x040700
strs.reserve(NumCaseConversions);
#endif
for (int i = 0; i < NumCaseConversions; ++i) {
strs.append(QCoreApplication::translate("@default", names[i]));
}
return strs;
}
/**
* String list of locale names.
*/
QStringList FormatConfig::getLocaleNames()
{
return QStringList() << tr("None")
#if QT_VERSION >= 0x040800
<< QLocale().uiLanguages()
#endif
;
}
int FilenameFormatConfig::s_index = -1;
......