Baloo Widgets

kedittagsdialog.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Peter Penz <[email protected]>
3  SPDX-FileCopyrightText: 2014 Vishesh Handa <[email protected]>
4  SPDX-FileCopyrightText: 2017 James D. Smith <[email protected]
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kedittagsdialog_p.h"
10 
11 #include <KLocalizedString>
12 
13 #include <QDebug>
14 #include <QDialogButtonBox>
15 #include <QHBoxLayout>
16 #include <QLabel>
17 #include <QLineEdit>
18 #include <QPushButton>
19 #include <QTimer>
20 #include <QTreeWidget>
21 #include <QUrl>
22 #include <QVBoxLayout>
23 #include <QWidget>
24 
25 #include <Baloo/TagListJob>
26 
27 KEditTagsDialog::KEditTagsDialog(const QStringList &tags, QWidget *parent)
28  : QDialog(parent)
29  , m_tags(tags)
30 {
31  const QString captionText = (tags.count() > 0) ? i18nc("@title:window", "Edit Tags") : i18nc("@title:window", "Add Tags");
32  setWindowTitle(captionText);
33  auto buttonBox = new QDialogButtonBox(this);
34 
35  buttonBox->addButton(i18n("Save"), QDialogButtonBox::AcceptRole);
36  buttonBox->addButton(QDialogButtonBox::Cancel);
37 
38  connect(buttonBox, &QDialogButtonBox::accepted, this, &KEditTagsDialog::slotAcceptedButtonClicked);
40 
41  auto topLayout = new QVBoxLayout;
42  setLayout(topLayout);
43 
44  auto label = new QLabel(i18nc("@label:textbox",
45  "Configure which tags should "
46  "be applied."),
47  this);
48 
49  m_tagTree = new QTreeWidget();
50  m_tagTree->setSortingEnabled(true);
51  m_tagTree->setSelectionMode(QAbstractItemView::NoSelection);
52  m_tagTree->setHeaderHidden(true);
53 
54  auto newTagLabel = new QLabel(i18nc("@label", "Create new tag:"));
55  m_newTagEdit = new QLineEdit(this);
56  m_newTagEdit->setClearButtonEnabled(true);
57  m_newTagEdit->setFocus();
58  connect(m_newTagEdit, &QLineEdit::textEdited, this, &KEditTagsDialog::slotTextEdited);
59  connect(m_tagTree, &QTreeWidget::itemActivated, this, &KEditTagsDialog::slotItemActivated);
60 
61  auto newTagLayout = new QHBoxLayout();
62  newTagLayout->addWidget(newTagLabel);
63  newTagLayout->addWidget(m_newTagEdit, 1);
64 
65  topLayout->addWidget(label);
66  topLayout->addWidget(m_tagTree);
67  topLayout->addLayout(newTagLayout);
68  topLayout->addWidget(buttonBox);
69 
70  resize(sizeHint());
71 
72  auto job = new Baloo::TagListJob();
73  connect(job, &Baloo::TagListJob::finished, [this](KJob *job) {
74  auto tjob = static_cast<Baloo::TagListJob *>(job);
75  m_allTags = tjob->tags();
76  loadTagWidget();
77  });
78 
79  job->start();
80 }
81 
82 KEditTagsDialog::~KEditTagsDialog() = default;
83 
84 QStringList KEditTagsDialog::tags() const
85 {
86  return m_tags;
87 }
88 
89 void KEditTagsDialog::slotAcceptedButtonClicked()
90 {
91  m_tags.clear();
92 
93  for (const QTreeWidgetItem *item : std::as_const(m_allTagTreeItems)) {
94  if (item->checkState(0) == Qt::Checked) {
95  m_tags << qvariant_cast<QString>(item->data(0, Qt::UserRole));
96  }
97  }
98 
99  accept();
100 }
101 
102 void KEditTagsDialog::slotItemActivated(const QTreeWidgetItem *item, int column)
103 {
104  Q_UNUSED(column)
105 
106  const auto tag = qvariant_cast<QString>(item->data(0, Qt::UserRole));
107  m_newTagEdit->setText(tag + QLatin1Char('/'));
108  m_newTagEdit->setFocus();
109 }
110 
111 void KEditTagsDialog::slotTextEdited(const QString &text)
112 {
113  // Remove unnecessary spaces from a new tag is
114  // mandatory, as the user cannot see the difference
115  // between a tag "Test" and "Test ".
116  QString tagText = text.simplified();
117  while (tagText.endsWith(QLatin1String("//"))) {
118  tagText.chop(1);
119  m_newTagEdit->setText(tagText);
120  return;
121  }
122 
123  // Remove all tree items related to the previous new tag
124  const QStringList splitTag = m_newTag.split(QLatin1Char('/'), Qt::SkipEmptyParts);
125  for (int i = splitTag.size() - 1; i >= 0 && i < splitTag.size(); --i) {
126  QString itemTag = m_newTag.section(QLatin1Char('/'), 0, i, QString::SectionSkipEmpty);
127  QTreeWidgetItem *item = m_allTagTreeItems.value(itemTag);
128 
129  if (!m_allTags.contains(m_newTag) && (item->childCount() == 0)) {
130  if (i != 0) {
131  QTreeWidgetItem *parentItem = item->parent();
132  parentItem->removeChild(item);
133  } else {
134  const int row = m_tagTree->indexOfTopLevelItem(item);
135  m_tagTree->takeTopLevelItem(row);
136  }
137 
138  m_allTagTreeItems.remove(itemTag);
139  }
140 
141  if (!m_tags.contains(itemTag)) {
142  item->setCheckState(0, Qt::Unchecked);
143  }
144 
145  item->setExpanded(false);
146  }
147 
148  if (!tagText.isEmpty()) {
149  m_newTag = tagText;
150  modifyTagWidget(tagText);
151  m_tagTree->sortItems(0, Qt::SortOrder::AscendingOrder);
152  } else {
153  m_newTag.clear();
154  m_allTagTreeItems.clear();
155  m_tagTree->clear();
156  loadTagWidget();
157  }
158 }
159 
160 void KEditTagsDialog::loadTagWidget()
161 {
162  for (const QString &tag : std::as_const(m_tags)) {
163  modifyTagWidget(tag);
164  }
165 
166  for (const QString &tag : std::as_const(m_allTags)) {
167  modifyTagWidget(tag);
168  }
169 
170  m_tagTree->sortItems(0, Qt::SortOrder::AscendingOrder);
171 }
172 
173 void KEditTagsDialog::modifyTagWidget(const QString &tag)
174 {
175  const QStringList splitTag = tag.split(QLatin1Char('/'), Qt::SkipEmptyParts);
176  for (int i = 0; i < splitTag.size(); ++i) {
177  QTreeWidgetItem *item;
178  QString itemTag = tag.section(QLatin1Char('/'), 0, i, QString::SectionSkipEmpty);
179 
180  if (!m_allTagTreeItems.contains(itemTag)) {
181  item = new QTreeWidgetItem();
182  item->setText(0, splitTag.at(i));
183  item->setIcon(0, QIcon::fromTheme(QLatin1String("tag")));
184  item->setData(0, Qt::UserRole, itemTag);
185  m_allTagTreeItems.insert(itemTag, item);
186  QString parentTag = tag.section(QLatin1Char('/'), 0, (i - 1), QString::SectionSkipEmpty);
187  QTreeWidgetItem *parentItem = m_allTagTreeItems.value(parentTag);
188 
189  if (i != 0) {
190  parentItem->addChild(item);
191  } else {
192  m_tagTree->addTopLevelItem(item);
193  }
194  } else {
195  item = m_allTagTreeItems.value(itemTag);
196  }
197 
198  if (!m_allTags.contains(tag)) {
199  m_tagTree->scrollToItem(item, QAbstractItemView::PositionAtCenter);
200  }
201 
202  if (((item->childCount() != 0) && m_tags.contains(tag)) || (m_newTag == tag)) {
203  item->setExpanded(true);
204  } else if (item->parent() && m_tags.contains(tag)) {
205  item->parent()->setExpanded(true);
206  }
207 
208  const bool check = (m_tags.contains(itemTag) || (m_newTag == itemTag));
209  item->setCheckState(0, check ? Qt::Checked : Qt::Unchecked);
210  }
211 }
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString section(QChar sep, int start, int end, QString::SectionFlags flags) const const
UserRole
QTreeWidgetItem * parent() const const
virtual void reject()
virtual Q_SCRIPTABLE void start()=0
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int count(const T &value) const const
QIcon fromTheme(const QString &name)
void chop(int n)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString simplified() const const
void setCheckState(int column, Qt::CheckState state)
int size() const const
QString i18n(const char *text, const TYPE &arg...)
void addChild(QTreeWidgetItem *child)
virtual QVariant data(int column, int role) const const
SkipEmptyParts
bool isEmpty() const const
const T & at(int i) const const
void setExpanded(bool expand)
void setText(int column, const QString &text)
QString label(StandardShortcut id)
void textEdited(const QString &text)
void itemActivated(QTreeWidgetItem *item, int column)
void clear()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void removeChild(QTreeWidgetItem *child)
int childCount() const const
void setIcon(int column, const QIcon &icon)
virtual void setData(int column, int role, const QVariant &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 19 2022 04:12:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.