[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