File indexing completed on 2024-04-28 04:21:20
0001 // SPDX-FileCopyrightText: 2009-2022 Jesper K. Pedersen <jesper.pedersen@kdab.com> 0002 // SPDX-FileCopyrightText: 2010 Jan Kundrát <jkt@flaska.net> 0003 // SPDX-FileCopyrightText: 2012 Miika Turkia <miika.turkia@gmail.com> 0004 // SPDX-FileCopyrightText: 2013-2024 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0005 // SPDX-FileCopyrightText: 2014-2020 Tobias Leupold <tl@stonemx.de> 0006 // SPDX-FileCopyrightText: 2022 Friedrich W. H. Kossebau <kossebau@kde.org> 0007 // 0008 // SPDX-License-Identifier: GPL-2.0-or-later 0009 0010 #include "TagGroupsPage.h" 0011 #include "CategoriesGroupsWidget.h" 0012 0013 #include <DB/CategoryCollection.h> 0014 #include <MainWindow/DirtyIndicator.h> 0015 #include <kpabase/SettingsData.h> 0016 0017 #include <KLocalizedString> 0018 #include <KMessageBox> 0019 #include <QAction> 0020 #include <QFont> 0021 #include <QGridLayout> 0022 #include <QHeaderView> 0023 #include <QInputDialog> 0024 #include <QLabel> 0025 #include <QListWidget> 0026 #include <QLocale> 0027 #include <QMenu> 0028 #include <QTreeWidget> 0029 #include <QTreeWidgetItem> 0030 0031 Settings::TagGroupsPage::TagGroupsPage(QWidget *parent) 0032 : QWidget(parent) 0033 { 0034 QGridLayout *layout = new QGridLayout(this); 0035 0036 // The category and group tree 0037 layout->addWidget(new QLabel(i18nc("@label", "Categories and groups:")), 0, 0); 0038 m_categoryTreeWidget = new CategoriesGroupsWidget(this); 0039 m_categoryTreeWidget->header()->hide(); 0040 m_categoryTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); 0041 layout->addWidget(m_categoryTreeWidget, 1, 0); 0042 connect(m_categoryTreeWidget, &CategoriesGroupsWidget::customContextMenuRequested, this, &TagGroupsPage::showTreeContextMenu); 0043 connect(m_categoryTreeWidget, &CategoriesGroupsWidget::itemClicked, this, &TagGroupsPage::slotGroupSelected); 0044 0045 // The member list 0046 m_selectGroupToAddTags = i18nc("@label/rich", "<strong>Select a group on the left side to add tags to it</strong>"); 0047 m_tagsInGroupLabel = new QLabel(m_selectGroupToAddTags); 0048 layout->addWidget(m_tagsInGroupLabel, 0, 1); 0049 m_membersListWidget = new QListWidget; 0050 m_membersListWidget->setEnabled(false); 0051 m_membersListWidget->setContextMenuPolicy(Qt::CustomContextMenu); 0052 layout->addWidget(m_membersListWidget, 1, 1); 0053 connect(m_membersListWidget, &QListWidget::itemChanged, this, &TagGroupsPage::checkItemSelection); 0054 connect(m_membersListWidget, &QListWidget::customContextMenuRequested, this, &TagGroupsPage::showMembersContextMenu); 0055 0056 // The "pending rename actions" label 0057 m_pendingChangesLabel = new QLabel(i18nc("@label/rich", "<strong>There are pending changes on the categories page.<nl> " 0058 "Please save the changes before working on tag groups.</strong>")); 0059 m_pendingChangesLabel->hide(); 0060 layout->addWidget(m_pendingChangesLabel, 2, 0, 1, 2); 0061 0062 QDialog *parentDialog = qobject_cast<QDialog *>(parent); 0063 connect(parentDialog, &QDialog::rejected, this, &TagGroupsPage::discardChanges); 0064 0065 // Context menu actions 0066 m_newGroupAction = new QAction(i18nc("@action:inmenu", "Add group ..."), this); 0067 connect(m_newGroupAction, &QAction::triggered, this, &TagGroupsPage::slotAddGroup); 0068 m_renameAction = new QAction(this); 0069 connect(m_renameAction, &QAction::triggered, this, &TagGroupsPage::slotRenameGroup); 0070 m_deleteAction = new QAction(this); 0071 connect(m_deleteAction, &QAction::triggered, this, &TagGroupsPage::slotDeleteGroup); 0072 m_deleteMemberAction = new QAction(this); 0073 connect(m_deleteMemberAction, &QAction::triggered, this, &TagGroupsPage::slotDeleteMember); 0074 m_renameMemberAction = new QAction(this); 0075 connect(m_renameMemberAction, &QAction::triggered, this, &TagGroupsPage::slotRenameMember); 0076 0077 m_memberMap = DB::ImageDB::instance()->memberMap(); 0078 0079 connect(DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::itemRemoved, 0080 &m_memberMap, &DB::MemberMap::deleteItem); 0081 0082 connect(DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::itemRenamed, 0083 &m_memberMap, &DB::MemberMap::renameItem); 0084 0085 connect(DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::categoryRemoved, 0086 &m_memberMap, &DB::MemberMap::deleteCategory); 0087 0088 m_dataChanged = false; 0089 } 0090 0091 void Settings::TagGroupsPage::updateCategoryTree() 0092 { 0093 // Store all expanded items so that they can be expanded after reload 0094 QList<QPair<QString, QString>> expandedItems = QList<QPair<QString, QString>>(); 0095 for (QTreeWidgetItemIterator it { m_categoryTreeWidget }; *it; ++it) { 0096 if ((*it)->isExpanded()) { 0097 QString parentName; 0098 if ((*it)->parent() != nullptr) { 0099 parentName = (*it)->parent()->text(0); 0100 } 0101 expandedItems.append(QPair<QString, QString>((*it)->text(0), parentName)); 0102 } 0103 } 0104 0105 m_categoryTreeWidget->clear(); 0106 0107 // Create a tree view of all groups and their sub-groups 0108 0109 const QList<DB::CategoryPtr> categories = DB::ImageDB::instance()->categoryCollection()->categories(); 0110 for (const DB::CategoryPtr &category : categories) { 0111 if (category->isSpecialCategory()) { 0112 continue; 0113 } 0114 0115 // Add the real categories as top-level items 0116 QTreeWidgetItem *topLevelItem = new QTreeWidgetItem; 0117 topLevelItem->setText(0, category->name()); 0118 topLevelItem->setFlags(topLevelItem->flags() & Qt::ItemIsEnabled); 0119 QFont font = topLevelItem->font(0); 0120 font.setWeight(QFont::Bold); 0121 topLevelItem->setFont(0, font); 0122 m_categoryTreeWidget->addTopLevelItem(topLevelItem); 0123 0124 // Build a map with all members for each group 0125 QMap<QString, QStringList> membersForGroup; 0126 const QStringList allGroups = m_memberMap.groups(category->name()); 0127 for (const QString &group : allGroups) { 0128 // FIXME: Why does the member map return an empty category?! 0129 if (group.isEmpty()) { 0130 continue; 0131 } 0132 0133 QStringList allMembers = m_memberMap.members(category->name(), group, true); 0134 membersForGroup[group] = allMembers; 0135 } 0136 0137 // Add all groups (their sub-groups will be added recursively) 0138 addSubCategories(topLevelItem, membersForGroup, allGroups); 0139 } 0140 0141 // Order the items alphabetically 0142 m_categoryTreeWidget->sortItems(0, Qt::AscendingOrder); 0143 0144 // Re-expand all previously expanded items 0145 for (QTreeWidgetItemIterator it { m_categoryTreeWidget }; *it; ++it) { 0146 QString parentName; 0147 if ((*it)->parent() != nullptr) { 0148 parentName = (*it)->parent()->text(0); 0149 } 0150 if (expandedItems.contains(QPair<QString, QString>((*it)->text(0), parentName))) { 0151 (*it)->setExpanded(true); 0152 } 0153 } 0154 } 0155 0156 void Settings::TagGroupsPage::addSubCategories(QTreeWidgetItem *superCategory, 0157 const QMap<QString, QStringList> &membersForGroup, 0158 const QStringList &allGroups) 0159 { 0160 // Process all group members 0161 for (auto memIt = membersForGroup.constBegin(); memIt != membersForGroup.constEnd(); ++memIt) { 0162 const QString &group = memIt.key(); 0163 bool isSubGroup = false; 0164 0165 // Search for a membership in another group for the current group 0166 for (const QStringList &members : membersForGroup) { 0167 if (members.contains(group)) { 0168 isSubGroup = true; 0169 break; 0170 } 0171 } 0172 0173 // Add the group if it's not member of another group 0174 if (!isSubGroup) { 0175 QTreeWidgetItem *groupItem = new QTreeWidgetItem; 0176 groupItem->setText(0, group); 0177 superCategory->addChild(groupItem); 0178 0179 // Search the member list for other groups 0180 QMap<QString, QStringList> subGroups; 0181 for (const QString &groupName : allGroups) { 0182 if (membersForGroup[group].contains(groupName)) { 0183 subGroups[groupName] = membersForGroup[groupName]; 0184 } 0185 } 0186 0187 // If the list contains other groups, add them recursively 0188 if (subGroups.count() > 0) { 0189 addSubCategories(groupItem, subGroups, allGroups); 0190 } 0191 } 0192 } 0193 } 0194 0195 QString Settings::TagGroupsPage::getCategory(QTreeWidgetItem *currentItem) 0196 { 0197 while (currentItem->parent() != nullptr) { 0198 currentItem = currentItem->parent(); 0199 } 0200 0201 return currentItem->text(0); 0202 } 0203 0204 void Settings::TagGroupsPage::showTreeContextMenu(QPoint point) 0205 { 0206 QTreeWidgetItem *currentItem = m_categoryTreeWidget->currentItem(); 0207 if (currentItem == nullptr) { 0208 return; 0209 } 0210 0211 m_currentSubCategory = currentItem->text(0); 0212 0213 if (currentItem->parent() == nullptr) { 0214 // It's a top-level, "real" category 0215 m_currentSuperCategory.clear(); 0216 } else { 0217 // It's a normal sub-category that belongs to another one 0218 m_currentSuperCategory = currentItem->parent()->text(0); 0219 } 0220 0221 m_currentCategory = getCategory(currentItem); 0222 0223 QMenu *menu = new QMenu; 0224 menu->addAction(m_newGroupAction); 0225 0226 // "Real" top-level categories have to processed on the category page. 0227 if (!m_currentSuperCategory.isEmpty()) { 0228 menu->addSeparator(); 0229 m_renameAction->setText(i18nc("@action:inmenu", "Rename group \"%1\"", m_currentSubCategory)); 0230 menu->addAction(m_renameAction); 0231 m_deleteAction->setText(i18nc("@action:inmenu", "Delete group \"%1\"", m_currentSubCategory)); 0232 menu->addAction(m_deleteAction); 0233 } 0234 0235 menu->exec(m_categoryTreeWidget->mapToGlobal(point)); 0236 delete menu; 0237 } 0238 0239 void Settings::TagGroupsPage::categoryChanged(const QString &name) 0240 { 0241 if (name.isEmpty()) { 0242 return; 0243 } 0244 0245 m_membersListWidget->blockSignals(true); 0246 m_membersListWidget->clear(); 0247 0248 const QStringList list = getCategoryObject(name)->items() + m_memberMap.groups(name); 0249 QStringList alreadyAdded; 0250 0251 for (const QString &member : list) { 0252 if (member.isEmpty()) { 0253 // This can happen if we add group that currently has no members. 0254 continue; 0255 } 0256 0257 if (!alreadyAdded.contains(member)) { 0258 alreadyAdded << member; 0259 0260 if (DB::ImageDB::instance()->untaggedCategoryFeatureConfigured() 0261 && !Settings::SettingsData::instance()->untaggedImagesTagVisible()) { 0262 0263 if (name == Settings::SettingsData::instance()->untaggedCategory()) { 0264 if (member == Settings::SettingsData::instance()->untaggedTag()) { 0265 continue; 0266 } 0267 } 0268 } 0269 0270 QListWidgetItem *newItem = new QListWidgetItem(member, m_membersListWidget); 0271 newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); 0272 newItem->setCheckState(Qt::Unchecked); 0273 } 0274 } 0275 0276 m_currentGroup.clear(); 0277 m_membersListWidget->clearSelection(); 0278 m_membersListWidget->sortItems(); 0279 m_membersListWidget->setEnabled(false); 0280 m_membersListWidget->blockSignals(false); 0281 } 0282 0283 void Settings::TagGroupsPage::slotGroupSelected(QTreeWidgetItem *item) 0284 { 0285 // When something else than a "real" category has been selected before, 0286 // we have to save it's members. 0287 if (!m_currentGroup.isEmpty()) { 0288 saveOldGroup(); 0289 } 0290 0291 if (item->parent() == nullptr) { 0292 // A "real" category has been selected, not a group 0293 m_currentCategory.clear(); 0294 m_currentGroup.clear(); 0295 m_membersListWidget->setEnabled(false); 0296 categoryChanged(item->text(0)); 0297 m_tagsInGroupLabel->setText(m_selectGroupToAddTags); 0298 return; 0299 } 0300 0301 // Let's see if the category changed 0302 QString itemCategory = getCategory(item); 0303 if (m_currentCategory != itemCategory) { 0304 m_currentCategory = itemCategory; 0305 categoryChanged(m_currentCategory); 0306 } 0307 0308 m_currentGroup = item->text(0); 0309 selectMembers(m_currentGroup); 0310 m_tagsInGroupLabel->setText(i18nc("@label", "Tags in group \"%1\" of category \"%2\"", 0311 m_currentGroup, m_currentCategory)); 0312 } 0313 0314 void Settings::TagGroupsPage::slotAddGroup() 0315 { 0316 bool ok; 0317 DB::CategoryPtr category = getCategoryObject(m_currentCategory); 0318 QStringList groups = m_memberMap.groups(m_currentCategory); 0319 QStringList tags; 0320 for (QString tag : category->items()) { 0321 if (!groups.contains(tag)) { 0322 tags << tag; 0323 } 0324 } 0325 0326 //// reject existing group names: 0327 // KStringListValidator validator(groups); 0328 // QString newSubCategory = KInputDialog::getText(i18nc("@title:window","New Group"), 0329 // i18nc("@label:textbox","Group name:"), 0330 // QString() /*value*/, 0331 // &ok, 0332 // this /*parent*/, 0333 // &validator, 0334 // QString() /*mask*/, 0335 // QString() /*WhatsThis*/, 0336 // tags /*completion*/ 0337 // ); 0338 // FIXME: KF5-port: QInputDialog does not accept a validator, 0339 // and KInputDialog was removed in KF5. -> Reimplement input validation using other stuff 0340 QString newSubCategory = QInputDialog::getText(this, 0341 i18nc("@title:window", "New Group"), 0342 i18nc("@label:textbox", "Group name:"), 0343 QLineEdit::Normal, 0344 QString(), 0345 &ok) 0346 .trimmed(); 0347 if (groups.contains(newSubCategory)) 0348 return; // only a workaround until GUI-support for validation is restored 0349 if (!ok) { 0350 return; 0351 } 0352 0353 // Let's see if we already have this group 0354 if (groups.contains(newSubCategory)) { 0355 // (with the validator working correctly, we should not get to this point) 0356 KMessageBox::error(this, 0357 i18nc("@info", "<p>The group \"%1\" already exists.</p>", newSubCategory), 0358 i18nc("@title:window", "Cannot add group")); 0359 return; 0360 } 0361 0362 // Add the group as a new tag to the respective category 0363 MainWindow::DirtyIndicator::suppressMarkDirty(true); 0364 category->addItem(newSubCategory); 0365 MainWindow::DirtyIndicator::suppressMarkDirty(false); 0366 QMap<CategoryEdit, QString> categoryChange; 0367 categoryChange[CategoryEdit::Category] = m_currentCategory; 0368 categoryChange[CategoryEdit::Add] = newSubCategory; 0369 m_categoryChanges.append(categoryChange); 0370 0371 // Add the group 0372 m_memberMap.addGroup(m_currentCategory, newSubCategory); 0373 0374 // Display the new group 0375 categoryChanged(m_currentCategory); 0376 0377 // Display the new item 0378 QTreeWidgetItem *parentItem = m_categoryTreeWidget->currentItem(); 0379 addNewSubItem(newSubCategory, parentItem); 0380 0381 // Check if we also have to update some other group (in case this is not a top-level group) 0382 if (!m_currentSuperCategory.isEmpty()) { 0383 m_memberMap.addMemberToGroup(m_currentCategory, parentItem->text(0), newSubCategory); 0384 slotGroupSelected(parentItem); 0385 } 0386 0387 m_dataChanged = true; 0388 } 0389 0390 void Settings::TagGroupsPage::addNewSubItem(QString &name, QTreeWidgetItem *parentItem) 0391 { 0392 QTreeWidgetItem *newItem = new QTreeWidgetItem; 0393 newItem->setText(0, name); 0394 parentItem->addChild(newItem); 0395 0396 if (!parentItem->isExpanded()) { 0397 parentItem->setExpanded(true); 0398 } 0399 } 0400 0401 QTreeWidgetItem *Settings::TagGroupsPage::findCategoryItem(QString category) 0402 { 0403 QTreeWidgetItem *categoryItem = nullptr; 0404 for (int i = 0; i < m_categoryTreeWidget->topLevelItemCount(); ++i) { 0405 categoryItem = m_categoryTreeWidget->topLevelItem(i); 0406 if (categoryItem->text(0) == category) { 0407 break; 0408 } 0409 } 0410 0411 return categoryItem; 0412 } 0413 0414 void Settings::TagGroupsPage::checkItemSelection(QListWidgetItem *) 0415 { 0416 m_dataChanged = true; 0417 saveOldGroup(); 0418 updateCategoryTree(); 0419 } 0420 0421 void Settings::TagGroupsPage::slotRenameGroup() 0422 { 0423 bool ok; 0424 DB::CategoryPtr category = getCategoryObject(m_currentCategory); 0425 QStringList groups = m_memberMap.groups(m_currentCategory); 0426 QStringList tags; 0427 for (QString tag : category->items()) { 0428 if (!groups.contains(tag)) { 0429 tags << tag; 0430 } 0431 } 0432 0433 // FIXME: reject existing group names 0434 QString newSubCategoryName = QInputDialog::getText(this, 0435 i18nc("@title:window", "Rename Group"), 0436 i18nc("@label:textbox", "New group name:"), 0437 QLineEdit::Normal, 0438 m_currentSubCategory, 0439 &ok) 0440 .trimmed(); 0441 0442 if (!ok || m_currentSubCategory == newSubCategoryName) { 0443 return; 0444 } 0445 0446 if (groups.contains(newSubCategoryName)) { 0447 // (with the validator working correctly, we should not get to this point) 0448 KMessageBox::error(this, 0449 xi18nc("@info", "<para>Cannot rename group \"%1\" to \"%2\": " 0450 "\"%2\" already exists in category \"%3\"</para>", 0451 m_currentSubCategory, 0452 newSubCategoryName, 0453 m_currentCategory), 0454 i18nc("@title:window", "Rename Group")); 0455 return; 0456 } 0457 0458 QTreeWidgetItem *selectedGroup = m_categoryTreeWidget->currentItem(); 0459 0460 saveOldGroup(); 0461 0462 // Update the group 0463 m_memberMap.renameGroup(m_currentCategory, m_currentSubCategory, newSubCategoryName); 0464 0465 // Update the tag in the respective category 0466 MainWindow::DirtyIndicator::suppressMarkDirty(true); 0467 category->renameItem(m_currentSubCategory, newSubCategoryName); 0468 MainWindow::DirtyIndicator::suppressMarkDirty(false); 0469 QMap<CategoryEdit, QString> categoryChange; 0470 categoryChange[CategoryEdit::Category] = m_currentCategory; 0471 categoryChange[CategoryEdit::Rename] = m_currentSubCategory; 0472 categoryChange[CategoryEdit::NewName] = newSubCategoryName; 0473 m_categoryChanges.append(categoryChange); 0474 m_dataChanged = true; 0475 0476 // Search for all possible sub-category items in this category that have to be renamed 0477 QTreeWidgetItem *categoryItem = findCategoryItem(m_currentCategory); 0478 for (int i = 0; i < categoryItem->childCount(); ++i) { 0479 renameAllSubCategories(categoryItem->child(i), m_currentSubCategory, newSubCategoryName); 0480 } 0481 0482 // Update the displayed items 0483 categoryChanged(m_currentCategory); 0484 slotGroupSelected(selectedGroup); 0485 0486 m_dataChanged = true; 0487 } 0488 0489 void Settings::TagGroupsPage::renameAllSubCategories(QTreeWidgetItem *categoryItem, 0490 QString oldName, 0491 QString newName) 0492 { 0493 // Probably, it item itself has to be renamed 0494 if (categoryItem->text(0) == oldName) { 0495 categoryItem->setText(0, newName); 0496 } 0497 0498 // Also check all sub-categories recursively 0499 for (int i = 0; i < categoryItem->childCount(); ++i) { 0500 renameAllSubCategories(categoryItem->child(i), oldName, newName); 0501 } 0502 } 0503 0504 void Settings::TagGroupsPage::slotDeleteGroup() 0505 { 0506 QTreeWidgetItem *currentItem = m_categoryTreeWidget->currentItem(); 0507 QString message; 0508 0509 if (currentItem->childCount() > 0) { 0510 message = xi18nc("@info", "<para>Really delete group \"%1\"?</para>" 0511 "<para>Sub-categories of this group will be moved to the super category of \"%1\" (\"%2\").<nl/> " 0512 "All other memberships of the sub-categories will stay intact.</para>", 0513 m_currentSubCategory, 0514 m_currentSuperCategory); 0515 } else { 0516 message = xi18nc("@info", "<para>Really delete group \"%1\"?</para>", m_currentSubCategory); 0517 } 0518 0519 int res = KMessageBox::warningContinueCancel(this, 0520 message, 0521 i18nc("@title:window", "Delete Group"), 0522 KGuiItem(i18n("&Delete"), 0523 QString::fromUtf8("editdelete"))); 0524 if (res == KMessageBox::Cancel) { 0525 return; 0526 } 0527 0528 // Delete the group 0529 m_memberMap.deleteGroup(m_currentCategory, m_currentSubCategory); 0530 0531 // Delete the tag 0532 MainWindow::DirtyIndicator::suppressMarkDirty(true); 0533 getCategoryObject(m_currentCategory)->removeItem(m_currentSubCategory); 0534 MainWindow::DirtyIndicator::suppressMarkDirty(false); 0535 QMap<CategoryEdit, QString> categoryChange; 0536 categoryChange[CategoryEdit::Category] = m_currentCategory; 0537 categoryChange[CategoryEdit::Remove] = m_currentSubCategory; 0538 m_categoryChanges.append(categoryChange); 0539 m_dataChanged = true; 0540 0541 slotPageChange(); 0542 0543 m_dataChanged = true; 0544 } 0545 0546 void Settings::TagGroupsPage::saveOldGroup() 0547 { 0548 QStringList list; 0549 for (int i = 0; i < m_membersListWidget->count(); ++i) { 0550 QListWidgetItem *item = m_membersListWidget->item(i); 0551 if (item->checkState() == Qt::Checked) { 0552 list << item->text(); 0553 } 0554 } 0555 0556 if (!m_currentCategory.isEmpty() && !m_currentGroup.isEmpty()) 0557 m_memberMap.setMembers(m_currentCategory, m_currentGroup, list); 0558 } 0559 0560 void Settings::TagGroupsPage::selectMembers(const QString &group) 0561 { 0562 m_membersListWidget->blockSignals(true); 0563 m_membersListWidget->setEnabled(false); 0564 0565 m_currentGroup = group; 0566 QStringList memberList = m_memberMap.members(m_currentCategory, group, false); 0567 0568 for (int i = 0; i < m_membersListWidget->count(); ++i) { 0569 QListWidgetItem *item = m_membersListWidget->item(i); 0570 item->setCheckState(Qt::Unchecked); 0571 0572 if (!m_memberMap.canAddMemberToGroup(m_currentCategory, group, item->text())) { 0573 item->setFlags(item->flags() & ~Qt::ItemIsSelectable & ~Qt::ItemIsEnabled); 0574 } else { 0575 item->setFlags(item->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled); 0576 if (memberList.contains(item->text())) { 0577 item->setCheckState(Qt::Checked); 0578 } 0579 } 0580 } 0581 0582 m_membersListWidget->setEnabled(true); 0583 m_membersListWidget->blockSignals(false); 0584 } 0585 0586 void Settings::TagGroupsPage::slotPageChange() 0587 { 0588 m_tagsInGroupLabel->setText(m_selectGroupToAddTags); 0589 m_membersListWidget->setEnabled(false); 0590 m_membersListWidget->clear(); 0591 m_currentCategory.clear(); 0592 updateCategoryTree(); 0593 } 0594 0595 void Settings::TagGroupsPage::saveSettings() 0596 { 0597 saveOldGroup(); 0598 slotPageChange(); 0599 DB::ImageDB::instance()->memberMap() = m_memberMap; 0600 m_categoryChanges.clear(); 0601 0602 if (m_dataChanged) { 0603 m_dataChanged = false; 0604 MainWindow::DirtyIndicator::markDirty(); 0605 } 0606 0607 m_categoryTreeWidget->setEnabled(true); 0608 m_membersListWidget->setEnabled(true); 0609 m_pendingChangesLabel->hide(); 0610 } 0611 0612 void Settings::TagGroupsPage::discardChanges() 0613 { 0614 m_memberMap = DB::ImageDB::instance()->memberMap(); 0615 slotPageChange(); 0616 m_dataChanged = false; 0617 0618 // Revert all changes to the "real" category objects 0619 MainWindow::DirtyIndicator::suppressMarkDirty(true); 0620 for (int i = m_categoryChanges.size() - 1; i >= 0; i--) { 0621 DB::CategoryPtr category = getCategoryObject(m_categoryChanges.at(i)[CategoryEdit::Category]); 0622 0623 if (m_categoryChanges.at(i).contains(CategoryEdit::Add)) { 0624 // Remove added tags 0625 category->removeItem(m_categoryChanges.at(i)[CategoryEdit::Add]); 0626 } else if (m_categoryChanges.at(i).contains(CategoryEdit::Remove)) { 0627 // Add removed tags 0628 category->addItem(m_categoryChanges.at(i)[CategoryEdit::Add]); 0629 } else if (m_categoryChanges.at(i).contains(CategoryEdit::Rename)) { 0630 // Re-rename tags to their old name 0631 category->renameItem(m_categoryChanges.at(i)[CategoryEdit::NewName], 0632 m_categoryChanges.at(i)[Rename]); 0633 } 0634 } 0635 MainWindow::DirtyIndicator::suppressMarkDirty(false); 0636 0637 m_categoryChanges.clear(); 0638 0639 m_categoryTreeWidget->setEnabled(true); 0640 m_membersListWidget->setEnabled(true); 0641 m_pendingChangesLabel->hide(); 0642 } 0643 0644 void Settings::TagGroupsPage::loadSettings() 0645 { 0646 categoryChanged(m_currentCategory); 0647 updateCategoryTree(); 0648 } 0649 0650 void Settings::TagGroupsPage::categoryChangesPending() 0651 { 0652 m_categoryTreeWidget->setEnabled(false); 0653 m_membersListWidget->setEnabled(false); 0654 m_pendingChangesLabel->show(); 0655 } 0656 0657 DB::MemberMap *Settings::TagGroupsPage::memberMap() 0658 { 0659 return &m_memberMap; 0660 } 0661 0662 void Settings::TagGroupsPage::processDrop(QTreeWidgetItem *draggedItem, QTreeWidgetItem *targetItem) 0663 { 0664 if (targetItem->parent() != nullptr) { 0665 // Dropped on a group 0666 0667 // Select the group 0668 m_categoryTreeWidget->setCurrentItem(targetItem); 0669 slotGroupSelected(targetItem); 0670 0671 // Check the dragged group on the member side to make it a sub-group of the target group 0672 m_membersListWidget->findItems(draggedItem->text(0), Qt::MatchExactly)[0]->setCheckState(Qt::Checked); 0673 } else { 0674 // Dropped on a top-level category 0675 0676 // Check if it's already a direct child of the category. 0677 // If so, we don't need to do anything. 0678 QTreeWidgetItem *parent = draggedItem->parent(); 0679 if (parent->parent() == nullptr) { 0680 return; 0681 } 0682 0683 // Select the former super group 0684 m_categoryTreeWidget->setCurrentItem(parent); 0685 slotGroupSelected(parent); 0686 0687 // Deselect the dragged group (this will bring it to the top level) 0688 m_membersListWidget->findItems(draggedItem->text(0), Qt::MatchExactly)[0]->setCheckState(Qt::Unchecked); 0689 } 0690 } 0691 0692 void Settings::TagGroupsPage::showMembersContextMenu(QPoint point) 0693 { 0694 if (m_membersListWidget->currentItem() == nullptr) { 0695 return; 0696 } 0697 0698 QMenu *menu = new QMenu; 0699 0700 m_renameMemberAction->setText(i18nc("@action:inmenu", "Rename \"%1\"", m_membersListWidget->currentItem()->text())); 0701 menu->addAction(m_renameMemberAction); 0702 m_deleteMemberAction->setText(i18nc("@action:inmenu", "Delete \"%1\"", m_membersListWidget->currentItem()->text())); 0703 menu->addAction(m_deleteMemberAction); 0704 0705 menu->exec(m_membersListWidget->mapToGlobal(point)); 0706 delete menu; 0707 } 0708 0709 void Settings::TagGroupsPage::slotRenameMember() 0710 { 0711 bool ok; 0712 QString newTagName = QInputDialog::getText(this, 0713 i18nc("@title:window", "New Tag Name"), 0714 i18nc("@label:textbox", "Tag name:"), 0715 QLineEdit::Normal, 0716 m_membersListWidget->currentItem()->text(), 0717 &ok) 0718 .trimmed(); 0719 if (!ok || newTagName == m_membersListWidget->currentItem()->text()) { 0720 return; 0721 } 0722 0723 // Update the tag name in the database 0724 MainWindow::DirtyIndicator::suppressMarkDirty(true); 0725 getCategoryObject(m_currentCategory)->renameItem(m_membersListWidget->currentItem()->text(), newTagName); 0726 MainWindow::DirtyIndicator::suppressMarkDirty(false); 0727 QMap<CategoryEdit, QString> categoryChange; 0728 categoryChange[CategoryEdit::Category] = m_currentCategory; 0729 categoryChange[CategoryEdit::Rename] = m_membersListWidget->currentItem()->text(); 0730 categoryChange[CategoryEdit::NewName] = newTagName; 0731 m_categoryChanges.append(categoryChange); 0732 0733 // Update the displayed tag name 0734 m_membersListWidget->currentItem()->setText(newTagName); 0735 0736 // Re-order the tags, as their alphabetial order may have changed 0737 m_membersListWidget->sortItems(); 0738 } 0739 0740 void Settings::TagGroupsPage::slotDeleteMember() 0741 { 0742 QString memberToDelete = m_membersListWidget->currentItem()->text(); 0743 0744 if (m_memberMap.groups(m_currentCategory).contains(memberToDelete)) { 0745 // The item to delete is a group 0746 0747 // Find the tag in the tree view and select it ... 0748 QTreeWidgetItemIterator it(m_categoryTreeWidget); 0749 while (*it) { 0750 if ((*it)->text(0) == memberToDelete && getCategory((*it)) == m_currentCategory) { 0751 m_categoryTreeWidget->setCurrentItem((*it)); 0752 m_currentSubCategory = (*it)->text(0); 0753 m_currentSuperCategory = (*it)->parent()->text(0); 0754 break; 0755 } 0756 ++it; 0757 } 0758 // ... then delete it like it had been requested by the TreeWidget's context menu 0759 slotDeleteGroup(); 0760 0761 } else { 0762 // The item to delete is a normal tag 0763 int res = KMessageBox::warningContinueCancel(this, 0764 xi18nc("@info", "<para>Do you really want to delete \"%1\"?</para>" 0765 "<para>Deleting the item will remove any information " 0766 "about it from any image containing the item.</para>", 0767 memberToDelete), 0768 i18nc("@title:window", "Really delete %1?", memberToDelete), 0769 KGuiItem(i18n("&Delete"), QString::fromUtf8("editdelete"))); 0770 if (res != KMessageBox::Continue) { 0771 return; 0772 } 0773 0774 // Delete the tag as if it had been deleted from the annotation dialog. 0775 getCategoryObject(m_currentCategory)->removeItem(memberToDelete); 0776 slotPageChange(); 0777 } 0778 } 0779 0780 DB::CategoryPtr Settings::TagGroupsPage::getCategoryObject(QString category) const 0781 { 0782 return DB::ImageDB::instance()->categoryCollection()->categoryForName(category); 0783 } 0784 0785 // vi:expandtab:tabstop=4 shiftwidth=4: 0786 0787 #include "moc_TagGroupsPage.cpp"