[baloo-widgets] src: Implement nested tags.
James D. Smith
null at kde.org
Sun Feb 19 20:10:57 UTC 2017
Git commit 0a2c8177439770f5a69826abebda1a53d26078a5 by James D. Smith.
Committed on 19/02/2017 at 20:09.
Pushed by smithjd into branch 'master'.
Implement nested tags.
FEATURE:334615
GUI:
REVIEW:128665
M +94 -71 src/kedittagsdialog.cpp
M +8 -8 src/kedittagsdialog_p.h
M +2 -1 src/tagcheckbox.cpp
https://commits.kde.org/baloo-widgets/0a2c8177439770f5a69826abebda1a53d26078a5
diff --git a/src/kedittagsdialog.cpp b/src/kedittagsdialog.cpp
index 1778d53..b17632a 100644
--- a/src/kedittagsdialog.cpp
+++ b/src/kedittagsdialog.cpp
@@ -1,6 +1,7 @@
/*****************************************************************************
* Copyright (C) 2009 by Peter Penz <peter.penz at gmx.at> *
* Copyright (C) 2014 by Vishesh Handa <me at vhanda.in> *
+ * Copyright (C) 2017 by James Smith <smithjd15 at gmail.com *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
@@ -24,7 +25,7 @@
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
-#include <QtWidgets/QListWidget>
+#include <QtWidgets/QTreeWidget>
#include <QtWidgets/QPushButton>
#include <QtCore/QTimer>
#include <QtWidgets/QVBoxLayout>
@@ -39,9 +40,7 @@ KEditTagsDialog::KEditTagsDialog(const QStringList& tags,
QWidget *parent) :
QDialog(parent),
m_tags(tags),
- m_tagsList(0),
- m_newTagItem(0),
- m_autoCheckedItem(0),
+ m_tagTree(0),
m_newTagEdit(0)
{
const QString captionText = (tags.count() > 0) ?
@@ -63,28 +62,37 @@ KEditTagsDialog::KEditTagsDialog(const QStringList& tags,
"Configure which tags should "
"be applied."), this);
- m_tagsList = new QListWidget();
- m_tagsList->setSortingEnabled(true);
- m_tagsList->setSelectionMode(QAbstractItemView::NoSelection);
+ m_tagTree = new QTreeWidget();
+ m_tagTree->setSortingEnabled(true);
+ m_tagTree->setSelectionMode(QAbstractItemView::NoSelection);
+ m_tagTree->setHeaderHidden(true);
QLabel* newTagLabel = new QLabel(i18nc("@label", "Create new tag:"));
m_newTagEdit = new QLineEdit(this);
m_newTagEdit->setClearButtonEnabled(true);
m_newTagEdit->setFocus();
connect(m_newTagEdit, &QLineEdit::textEdited, this, &KEditTagsDialog::slotTextEdited);
+ connect(m_tagTree, &QTreeWidget::itemActivated, this, &KEditTagsDialog::slotItemActivated);
QHBoxLayout* newTagLayout = new QHBoxLayout();
newTagLayout->addWidget(newTagLabel);
newTagLayout->addWidget(m_newTagEdit, 1);
topLayout->addWidget(label);
- topLayout->addWidget(m_tagsList);
+ topLayout->addWidget(m_tagTree);
topLayout->addLayout(newTagLayout);
topLayout->addWidget(buttonBox);
resize(sizeHint());
- loadTags();
+ Baloo::TagListJob* job = new Baloo::TagListJob();
+ connect(job, &Baloo::TagListJob::finished, [this] (KJob* job) {
+ Baloo::TagListJob* tjob = static_cast<Baloo::TagListJob*>(job);
+ m_allTags = tjob->tags();
+ loadTagWidget();
+ });
+
+ job->start();
}
KEditTagsDialog::~KEditTagsDialog()
@@ -100,94 +108,109 @@ void KEditTagsDialog::slotAcceptedButtonClicked()
{
m_tags.clear();
- const int count = m_tagsList->count();
- for (int i = 0; i < count; ++i) {
- QListWidgetItem* item = m_tagsList->item(i);
- if (item->checkState() == Qt::Checked) {
- m_tags << item->text();
+ for (const QTreeWidgetItem* item : m_allTagTreeItems.values()) {
+ if (item->checkState(0) == Qt::Checked) {
+ m_tags << qvariant_cast<QString>(item->data(0, Qt::UserRole));
}
}
accept();
}
+void KEditTagsDialog::slotItemActivated(const QTreeWidgetItem* item, int column)
+{
+ Q_UNUSED(column)
+
+ const QString tag = qvariant_cast<QString>(item->data(0, Qt::UserRole));
+ m_newTagEdit->setText(tag + QLatin1Char('/'));
+ m_newTagEdit->setFocus();
+}
+
void KEditTagsDialog::slotTextEdited(const QString& text)
{
// Remove unnecessary spaces from a new tag is
// mandatory, as the user cannot see the difference
// between a tag "Test" and "Test ".
- const QString tagText = text.simplified();
- if (tagText.isEmpty()) {
- removeNewTagItem();
+ QString tagText = text.simplified();
+ while (tagText.endsWith("//")) {
+ tagText.chop(1);
+ m_newTagEdit->setText(tagText);
return;
}
- // Check whether the new tag already exists. If this
- // is the case, remove the new tag item.
- const int count = m_tagsList->count();
- for (int i = 0; i < count; ++i) {
- QListWidgetItem* item = m_tagsList->item(i);
- const bool remove = (item->text() == tagText) &&
- ((m_newTagItem == 0) || (m_newTagItem != item));
- if (remove) {
- m_tagsList->scrollToItem(item);
- if (item->checkState() == Qt::Unchecked) {
- item->setCheckState(Qt::Checked);
- // Remember the checked item, so that it can be unchecked
- // again if the user changes the tag-text.
- m_autoCheckedItem = item;
+ // Remove all tree items related to the previous new tag
+ const QStringList splitTag = m_newTag.split(QLatin1Char('/'), QString::SkipEmptyParts);
+ for (int i = splitTag.size() - 1; i >= 0 && i < splitTag.size(); --i) {
+ QString itemTag = m_newTag.section(QLatin1Char('/'), 0, i, QString::SectionSkipEmpty);
+ QTreeWidgetItem* item = m_allTagTreeItems.value(itemTag);
+ if (!m_allTags.contains(m_newTag) && (item->childCount() == 0)) {
+ if (i != 0) {
+ QTreeWidgetItem* parentItem = item->parent();
+ parentItem->removeChild(item);
+ } else {
+ const int row = m_tagTree->indexOfTopLevelItem(item);
+ m_tagTree->takeTopLevelItem(row);
}
- removeNewTagItem();
- return;
+ m_allTagTreeItems.remove(itemTag);
+ }
+ item->setExpanded(false);
+ if (!m_tags.contains(itemTag)) {
+ item->setCheckState(0, Qt::Unchecked);
}
}
- // There is no tag in the list with the the passed text.
- if (m_newTagItem == 0) {
- m_newTagItem = new QListWidgetItem(tagText, m_tagsList);
+ if (!tagText.isEmpty()) {
+ m_newTag = tagText;
+ modifyTagWidget(tagText);
+ m_tagTree->sortItems(0, Qt::SortOrder::AscendingOrder);
} else {
- m_newTagItem->setText(tagText);
- }
-
- if (m_autoCheckedItem != 0) {
- m_autoCheckedItem->setCheckState(Qt::Unchecked);
- m_autoCheckedItem = 0;
+ m_newTag.clear();
+ m_allTagTreeItems.clear();
+ m_tagTree->clear();
+ loadTagWidget();
}
-
- m_newTagItem->setData(Qt::UserRole, QUrl());
- m_newTagItem->setCheckState(Qt::Checked);
- m_tagsList->scrollToItem(m_newTagItem);
-}
-
-void KEditTagsDialog::loadTags()
-{
- Baloo::TagListJob* job = new Baloo::TagListJob();
- connect(job, &Baloo::TagListJob::finished, this, &KEditTagsDialog::slotTagsLoaded);
-
- job->start();
}
-void KEditTagsDialog::slotTagsLoaded(KJob* job)
+void KEditTagsDialog::loadTagWidget()
{
- Baloo::TagListJob* tjob = static_cast<Baloo::TagListJob*>(job);
-
- m_allTags = tjob->tags();
- qSort(m_allTags.begin(), m_allTags.end());
-
- foreach (const QString &tag, m_allTags) {
- QListWidgetItem* item = new QListWidgetItem(tag, m_tagsList);
-
- const bool check = m_tags.contains(tag);
- item->setCheckState(check ? Qt::Checked : Qt::Unchecked);
+ for (const QString &tag : m_allTags) {
+ modifyTagWidget(tag);
}
+
+ m_tagTree->sortItems(0, Qt::SortOrder::AscendingOrder);
}
-void KEditTagsDialog::removeNewTagItem()
+void KEditTagsDialog::modifyTagWidget(const QString &tag)
{
- if (m_newTagItem != 0) {
- const int row = m_tagsList->row(m_newTagItem);
- m_tagsList->takeItem(row);
- delete m_newTagItem;
- m_newTagItem = 0;
+ const QStringList splitTag = tag.split(QLatin1Char('/'), QString::SkipEmptyParts);
+
+ for (int i = 0; i < splitTag.size(); ++i) {
+ QTreeWidgetItem* item = new QTreeWidgetItem();
+ QString itemTag = tag.section(QLatin1Char('/'), 0, i, QString::SectionSkipEmpty);
+ if (!m_allTagTreeItems.contains(itemTag)) {
+ item->setText(0, splitTag.at(i));
+ item->setIcon(0, QIcon::fromTheme(QLatin1String("tag")));
+ item->setData(0, Qt::UserRole, itemTag);
+ m_allTagTreeItems.insert(itemTag, item);
+ QString parentTag = tag.section(QLatin1Char('/'), 0, (i - 1), QString::SectionSkipEmpty);
+ QTreeWidgetItem* parentItem = m_allTagTreeItems.value(parentTag);
+ if (i != 0) {
+ parentItem->addChild(item);
+ } else {
+ m_tagTree->addTopLevelItem(item);
+ }
+ } else {
+ item = m_allTagTreeItems.value(itemTag);
+ }
+ if (!m_allTags.contains(tag)) {
+ m_tagTree->scrollToItem(item, QAbstractItemView::PositionAtCenter);
+ }
+ if (((item->childCount() != 0) && m_tags.contains(tag)) || (m_newTag == tag)) {
+ item->setExpanded(true);
+ } else if (item->parent() && m_tags.contains(tag)) {
+ item->parent()->setExpanded(true);
+ }
+ const bool check = (m_tags.contains(itemTag) || (m_newTag == itemTag));
+ item->setCheckState(0, check ? Qt::Checked : Qt::Unchecked);
}
}
diff --git a/src/kedittagsdialog_p.h b/src/kedittagsdialog_p.h
index 3c4de19..8d168ea 100644
--- a/src/kedittagsdialog_p.h
+++ b/src/kedittagsdialog_p.h
@@ -22,8 +22,8 @@
class QLineEdit;
class KJob;
-class QListWidget;
-class QListWidgetItem;
+class QTreeWidget;
+class QTreeWidgetItem;
class QPushButton;
class QTimer;
@@ -52,19 +52,19 @@ private Q_SLOTS:
void slotAcceptedButtonClicked();
- void slotTagsLoaded(KJob* job);
+ void slotItemActivated(const QTreeWidgetItem* item, int column);
private:
- void loadTags();
- void removeNewTagItem();
+ void loadTagWidget();
+ void modifyTagWidget(const QString& tag);
private:
+ QHash<QString, QTreeWidgetItem*> m_allTagTreeItems;
QStringList m_tags;
QStringList m_allTags;
+ QString m_newTag;
- QListWidget* m_tagsList;
- QListWidgetItem* m_newTagItem;
- QListWidgetItem* m_autoCheckedItem;
+ QTreeWidget* m_tagTree;
QLineEdit* m_newTagEdit;
};
diff --git a/src/tagcheckbox.cpp b/src/tagcheckbox.cpp
index 8273a7f..70db43e 100644
--- a/src/tagcheckbox.cpp
+++ b/src/tagcheckbox.cpp
@@ -39,7 +39,8 @@ TagCheckBox::TagCheckBox(const QString& tag, QWidget* parent)
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setMargin(0);
- m_label = new QLabel(tag, this);
+ m_label = new QLabel(tag.split("/", QString::SkipEmptyParts).last(), this);
+ m_label->setToolTip(tag);
m_label->setMouseTracking(true);
m_label->setTextFormat(Qt::PlainText);
m_label->setForegroundRole(parent->foregroundRole());
More information about the kde-doc-english
mailing list