...
 
Commits (12)
......@@ -1477,19 +1477,20 @@ Tag Database::importedTagsTag() const
}
QString Tag::name() const {
return m_instance->d_func()->findTag(m_id).name;
return isValid() ? m_instance->d_func()->findTag(m_id).name : QString();
}
QString Tag::icon() const {
return m_instance->d_func()->findTag(m_id).icon;
return isValid() ? m_instance->d_func()->findTag(m_id).icon : QString();
}
bool Tag::isCategory() const {
return m_instance->d_func()->findTag(m_id).isCategory;
return isValid() ? m_instance->d_func()->findTag(m_id).isCategory : true;
}
Tag Tag::parent() const {
return Tag(m_instance->d_func()->findTag(m_id).parent);
return isValid() ?
Tag(m_instance->d_func()->findTag(m_id).parent) : Tag();
}
float Tag::popularity() const {
......
......@@ -23,7 +23,7 @@ QtObject {
property var createTag: Action {
text: qsTr("Create new tag…")
onTriggered: PopupUtils.open(Qt.resolvedUrl("TagEditDialog.qml"), this, {
"parentTag": (tagView.currentIndex.valid ? _tagModel.data(tagView.currentIndex, TagModel.TagRole) : _tagModel.uncategorizedTag),
"parentTag": tagView.activeTag.valid ? tagView.activeTag : null,
})
}
......@@ -47,18 +47,18 @@ QtObject {
}
property var deleteTag: Action {
enabled: tagView.currentIndex.valid
enabled: tagView.activeTag.valid
text: qsTr("Edit tag…")
onTriggered: PopupUtils.open(Qt.resolvedUrl("TagEditDialog.qml"), this, {
"tag": _tagModel.data(tagView.currentIndex, TagModel.TagRole),
"tag": tagView.activeTag,
})
}
property var editTag: Action {
enabled: tagView.currentIndex.valid
enabled: tagView.activeTag.valid
text: qsTr("Delete tag")
onTriggered: PopupUtils.open(Qt.resolvedUrl("TagDeleteConfirmation.qml"), this, {
"tag": _tagModel.data(tagView.currentIndex, TagModel.TagRole),
"tag": tagView.activeTag,
})
}
......
......@@ -17,7 +17,7 @@ ColumnLayout {
Layout.minimumWidth: 400
title: qsTr("Import from:")
Column {
ColumnLayout {
anchors.fill: parent
spacing: 10
......@@ -28,6 +28,7 @@ ColumnLayout {
}
Repeater {
id: folderRepeater
model: FolderModel {}
Button {
Layout.fillWidth: true
......@@ -35,6 +36,14 @@ ColumnLayout {
onClicked: importer.importFolder = "file://" + model.path
}
}
Label {
Layout.fillWidth: true
visible: folderRepeater.count == 0
text: qsTr("Removable devices will appear here")
wrapMode: Text.Wrap
font.italic: true
}
}
}
......
import Imaginario 1.0
import QtQuick 2.5
Item {
id: root
property var selectedTags: []
property var deselectedTags: []
property alias model: repeater.model
property alias count: repeater.count
property bool multiSelection: true
signal contextMenuRequested(var mouse)
implicitHeight: flow.childrenRect.height
MouseArea {
id: flow
property int rowSpacing: 2
property int wordSpacing: 4
property int maxHeight: 22
anchors.fill: parent
onWidthChanged: reflowTimer.start()
visible: false
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
var item = childAt(mouse.x, mouse.y)
console.log("Item is " + item)
if (!item) return
root.selectedTags = [ item.tag ]
if (mouse.button == Qt.RightButton) {
root.contextMenuRequested(mouse)
}
}
Repeater {
id: repeater
delegate: Rectangle {
property bool selected: root.isSelected(index)
property var tag: model.tag
width: Math.min(label.implicitWidth + 2, flow.width)
implicitHeight: label.implicitHeight + 2
color: selected ? palette.highlight : palette.base
Text {
id: label
anchors { fill: parent; margins: 1 }
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: model.name
font.pixelSize: Math.round(Math.max(flow.maxHeight * model.popularity, 8))
elide: Text.ElideMiddle
color: selected ? palette.highlightedText : palette.text
}
}
onItemAdded: reflowTimer.start()
onItemRemoved: reflowTimer.start()
}
Timer {
id: reflowTimer
interval: 10
onTriggered: flow.reflow()
}
function reflow() {
var rows = []
var rowWidth = 0
var rowHeight = 0
var rowIndex = 0
var rowItems = []
/* First, compute the lines' widths */
for (var i = 0; i < repeater.count; i++) {
var item = repeater.itemAt(i)
if (!item) continue;
if (rowWidth + item.width + flow.wordSpacing > flow.width) {
rows[rowIndex] = {
"width": rowWidth,
"height": rowHeight,
"items": rowItems
}
rowIndex++
rowWidth = 0
rowHeight = 0
rowItems = []
}
rowWidth += item.width + flow.wordSpacing
rowItems.push(item)
if (item.implicitHeight > rowHeight) rowHeight = item.implicitHeight
}
if (rowItems.length > 0) {
rows[rowIndex] = {
"width": rowWidth,
"height": rowHeight,
"items": rowItems
}
}
var y = 0
for (var i = 0; i < rows.length; i++) {
var row = rows[i]
var x = Math.round((flow.width - row.width) / 2)
for (var j = 0; j < row.items.length; j++) {
row.items[j].x = x
row.items[j].y = y
row.items[j].height = row.height
x += row.items[j].width + flow.wordSpacing
}
y += row.height + flow.rowSpacing
}
flow.visible = true
}
}
Component.onCompleted: computeInitialSelection()
function computeInitialSelection() {
if (!model.photosForUsageCount) return
var numItems = model.photosForUsageCount.length
if (numItems == 0) return
var newSelectedList = []
for (var i = 0; i < model.count; i++) {
if (model.get(i, "usageCount") == numItems) {
newSelectedList.push(model.get(i, "tag"))
}
}
selectedTags = newSelectedList
}
function isSelected(index) {
return selectedTags.indexOf(model.get(index, "tag")) >= 0
}
function isDeselected(index) {
return deselectedTags.indexOf(model.get(index, "tag")) >= 0
}
function toggleSelection(tag, tristate) {
var i = selectedTags.indexOf(tag)
if (!multiSelection && i < 0) {
selectedTags = [ tag ]
return
}
var j = deselectedTags.indexOf(tag)
var newSelectedList = selectedTags
var newDeselectedList = deselectedTags
if (i >= 0) {
newSelectedList.splice(i, 1)
newDeselectedList.push(tag)
} else if (j >= 0) {
newDeselectedList.splice(j, 1)
if (!tristate) newSelectedList.push(tag)
} else {
newSelectedList.push(tag)
}
selectedTags = newSelectedList
deselectedTags = newDeselectedList
}
function itemAt(x, y) {
return flow.childAt(x, y)
}
function tagAt(x, y) {
var item = flow.childAt(x, y)
return item ? item.tag : Utils.invalidTag
}
SystemPalette { id: palette }
}
import Imaginario 1.0
import QtQuick 2.5
import QtQuick.Controls 1.4
ScrollView {
id: root
property var activeTag: cloud.selectedTags.length == 1 ?
cloud.selectedTags[0] : Utils.invalidTag
property var contextMenu: null
signal itemsDropped(var tag)
Item {
width: root.viewport.width
implicitHeight: cloud.implicitHeight
TagCloud {
id: cloud
anchors.fill: parent
multiSelection: false
model: TagModel {
allTags: true
}
onContextMenuRequested: root.contextMenu.popup()
}
DropArea {
id: dropArea
anchors.fill: parent
keys: [ "x-imaginario/item" ]
onEntered: updateDropArea(drag)
onPositionChanged: updateDropArea(drag)
onExited: disableDrop()
onDropped: {
disableDrop()
var dropTag = cloud.tagAt(drop.x, drop.y)
if (!dropTag.valid) return
if (drop.keys.indexOf("x-imaginario/item") >= 0) {
root.itemsDropped(dropTag)
}
}
function disableDrop() {
highlight.parent = dropArea
}
function updateDropArea(drag) {
var item = cloud.itemAt(drag.x, drag.y)
if (item) {
highlight.parent = item
} else {
disableDrop()
}
}
Rectangle {
id: highlight
border { width: 1; color: palette.highlight }
color: Qt.rgba(border.color.r, border.color.g, border.color.b, 0.3)
visible: parent != dropArea
anchors.fill: parent
}
}
SystemPalette { id: palette }
}
}
......@@ -10,13 +10,15 @@ import "."
Dialog {
id: root
property var parentTag: tag ? tag.parent : null
property var parentTag: null
property var tag: null
title: tag ? qsTr("Edit tag %1").arg(tag.name) : qsTr("Create new tag")
standardButtons: StandardButton.Ok | StandardButton.Cancel
property string tagIcon: tag ? tag.iconSource : ""
property var __parentTag: tag && tag.valid ? tag.parent :
(parentTag && parentTag.valid ? parentTag : tagModel.uncategorizedTag)
onVisibleChanged: if (visible) tagTextEntry.forceActiveFocus()
onAccepted: root.save()
......@@ -107,10 +109,9 @@ Dialog {
textRole: "text"
Component.onCompleted: {
// FIXME introduce a way to create an invalid tag
tagListModel.append({
"text": qsTr("No parent"),
"tag": tagModel.tag("invalid tag"),
"tag": Utils.invalidTag,
})
for (var i = 0; i < tagModel.count; i++) {
var t = tagModel.get(i, "tag")
......@@ -118,7 +119,7 @@ Dialog {
"text": t.name,
"tag": t,
})
if (t == parentTag) { currentIndex = i + 1 }
if (t == __parentTag) { currentIndex = i + 1 }
}
}
......
import Imaginario 1.0
import QtQml 2.2
import QtQml.Models 2.2
import QtQuick 2.0
import QtQuick.Controls 1.4
import "popupUtils.js" as PopupUtils
import "."
TagSelector {
id: root
property var photoModel: null
contextMenu: menu
Menu {
id: menu
MenuItem {
text: qsTr("Find")
enabled: root.activeTag.valid
onTriggered: {
var tag = root.activeTag
var tags = (photoModel.requiredTags && photoModel.requiredTags.length > 0) ? photoModel.requiredTags : []
tags.push([tag])
photoModel.requiredTags = tags
}
}
Menu {
id: tagsMenu
title: qsTr("Find with")
enabled: root.activeTag.valid && instantiator.count > 0
Instantiator {
id: instantiator
model: photoModel.requiredTags
MenuItem {
text: modelData.map(function(el, index, array) {
return el.name
}).join(', ')
onTriggered: {
var tag = root.activeTag
var tags = modelData
tags.push(tag)
var requiredTags = photoModel.requiredTags
requiredTags[index] = tags
photoModel.requiredTags = requiredTags
}
}
onObjectAdded: tagsMenu.insertItem(index, object)
onObjectRemoved: tagsMenu.removeItem(object)
}
}
MenuSeparator {}
MenuItem { action: actions.createTag }
MenuSeparator {}
MenuItem { action: actions.editTag }
MenuItem { action: actions.deleteTag }
}
}
import Imaginario 1.0
import QtQml 2.2
import QtQml.Models 2.2
import QtQuick 2.0
import QtQuick.Controls 1.4
import "popupUtils.js" as PopupUtils
import "."
TabView {
id: root
readonly property var activeTag: __currentView ? __currentView.activeTag : Utils.invalidTag
property var contextMenu: null
signal itemsDropped(var tag)
frameVisible: false
property bool __ready: false
property var __currentTab: __ready ? getTab(currentIndex) : null
property var __currentView: __currentTab ? __currentTab.item : null
Tab {
title: qsTr("Tag tree")
onLoaded: root.__ready = true
TagTreeView {
anchors.fill: parent
contextMenu: root.contextMenu
onItemsDropped: root.itemsDropped(tag)
}
}
Tab {
title: qsTr("Tag cloud")
TagCloudPanel {
anchors.fill: parent
contextMenu: root.contextMenu
onItemsDropped: root.itemsDropped(tag)
}
}
}
......@@ -10,9 +10,9 @@ MouseArea {
id: root
property alias tagView: treeView
property var photoModel: null
property var activeTag: treeView.currentIndex.valid ?
treeView.model.data(treeView.currentIndex, TagModel.TagRole) : TagModel.uncategorizedTag
treeView.model.data(treeView.currentIndex, TagModel.TagRole) : Utils.invalidTag
property var contextMenu: null
signal itemsDropped(var tag)
......@@ -59,7 +59,7 @@ MouseArea {
view.currentIndex = row
}
}
menu.popup()
contextMenu.popup()
}
}
......@@ -68,53 +68,6 @@ MouseArea {
title: "Tag"
}
Menu {
id: menu
MenuItem {
text: qsTr("Find")
enabled: treeView.currentIndex.valid
onTriggered: {
var tag = tagModel.data(treeView.currentIndex, TagModel.TagRole)
var tags = (photoModel.requiredTags && photoModel.requiredTags.length > 0) ? photoModel.requiredTags : []
tags.push([tag])
photoModel.requiredTags = tags
}
}
Menu {
id: tagsMenu
title: qsTr("Find with")
enabled: treeView.currentIndex.valid && instantiator.count > 0
Instantiator {
id: instantiator
model: photoModel.requiredTags
MenuItem {
text: modelData.map(function(el, index, array) {
return el.name
}).join(', ')
onTriggered: {
var tag = tagModel.data(treeView.currentIndex, TagModel.TagRole)
var tags = modelData
tags.push(tag)
var requiredTags = photoModel.requiredTags
requiredTags[index] = tags
photoModel.requiredTags = requiredTags
}
}
onObjectAdded: tagsMenu.insertItem(index, object)
onObjectRemoved: tagsMenu.removeItem(object)
}
}
MenuSeparator {}
MenuItem { action: actions.createTag }
MenuSeparator {}
MenuItem { action: actions.editTag }
MenuItem { action: actions.deleteTag }
}
DropArea {
id: dropArea
anchors.fill: parent
......@@ -131,7 +84,7 @@ MouseArea {
if (selectParent) {
index = index.parent
}
var dropTag = index.valid ? tagModel.data(index, TagModel.TagRole) : tagModel.tag("invalid tag")
var dropTag = index.valid ? tagModel.data(index, TagModel.TagRole) : Utils.invalidTag
if (drop.keys.indexOf("x-imaginario/tag") >= 0) {
var tag = drop.source.tag
if (tag.parent != dropTag) {
......
......@@ -152,7 +152,7 @@ ApplicationWindow {
id: actions
photoModel: photoModel
imageView: imagePanel
tagView: tagView.tagView
tagView: tagView
}
toolBar: ToolBar {
......@@ -187,7 +187,7 @@ ApplicationWindow {
Layout.maximumWidth: 800
spacing: 4
TagTreeView {
TagPanel {
id: tagView
Layout.fillWidth: true
Layout.fillHeight: true
......
......@@ -40,13 +40,17 @@
<file>ScrolledImageView.qml</file>
<file>TabWithMargin.qml</file>
<file>TagAreaView.qml</file>
<file>TagCloud.qml</file>
<file>TagCloudPanel.qml</file>
<file>TagDeleteConfirmation.qml</file>
<file>TagEditDialog.qml</file>
<file>TagFilterRow.qml</file>
<file>TagIconPicker.qml</file>
<file>TagIcon.qml</file>
<file>TagInput.qml</file>
<file>TagPanel.qml</file>
<file>TagRow.qml</file>
<file>TagSelector.qml</file>
<file>TagTreeView.qml</file>
<file>TimeArrow.qml</file>
<file>TimeBar.qml</file>
......
......@@ -43,6 +43,11 @@ Utils::~Utils()
{
}
Tag Utils::invalidTag() const
{
return Tag();
}
QVariantMap Utils::geoFields(const GeoPoint &p) const
{
QVariantMap map;
......
......@@ -36,11 +36,14 @@ namespace Imaginario {
class Utils: public QObject
{
Q_OBJECT
Q_PROPERTY(Tag invalidTag READ invalidTag CONSTANT)
public:
Utils(QObject *parent = 0);
~Utils();
Tag invalidTag() const;
Q_INVOKABLE QVariantMap geoFields(const GeoPoint &p) const;
Q_INVOKABLE GeoPoint geo(const QJSValue &value) const;
......