// SPDX-FileCopyrightText: 2009-2022 Jesper K. Pedersen <jesper.pedersen@kdab.com>
// SPDX-FileCopyrightText: 2010 Jan Kundrát <jkt@flaska.net>
// SPDX-FileCopyrightText: 2012 Miika Turkia <miika.turkia@gmail.com>
// SPDX-FileCopyrightText: 2013-2024 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
// SPDX-FileCopyrightText: 2014-2020 Tobias Leupold <tl@stonemx.de>
// SPDX-FileCopyrightText: 2022 Friedrich W. H. Kossebau <kossebau@kde.org>
//
// SPDX-License-Identifier: GPL-2.0-or-later

#include "TagGroupsPage.h"
#include "CategoriesGroupsWidget.h"

#include <DB/CategoryCollection.h>
#include <MainWindow/DirtyIndicator.h>
#include <kpabase/SettingsData.h>

#include <KLocalizedString>
#include <KMessageBox>
#include <QAction>
#include <QFont>
#include <QGridLayout>
#include <QHeaderView>
#include <QInputDialog>
#include <QLabel>
#include <QListWidget>
#include <QLocale>
#include <QMenu>
#include <QTreeWidget>
#include <QTreeWidgetItem>

Settings::TagGroupsPage::TagGroupsPage(QWidget *parent)
    : QWidget(parent)
{
    QGridLayout *layout = new QGridLayout(this);

    // The category and group tree
    layout->addWidget(new QLabel(i18nc("@label", "Categories and groups:")), 0, 0);
    m_categoryTreeWidget = new CategoriesGroupsWidget(this);
    m_categoryTreeWidget->header()->hide();
    m_categoryTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
    layout->addWidget(m_categoryTreeWidget, 1, 0);
    connect(m_categoryTreeWidget, &CategoriesGroupsWidget::customContextMenuRequested, this, &TagGroupsPage::showTreeContextMenu);
    connect(m_categoryTreeWidget, &CategoriesGroupsWidget::itemClicked, this, &TagGroupsPage::slotGroupSelected);

    // The member list
    m_selectGroupToAddTags = i18nc("@label/rich", "<strong>Select a group on the left side to add tags to it</strong>");
    m_tagsInGroupLabel = new QLabel(m_selectGroupToAddTags);
    layout->addWidget(m_tagsInGroupLabel, 0, 1);
    m_membersListWidget = new QListWidget;
    m_membersListWidget->setEnabled(false);
    m_membersListWidget->setContextMenuPolicy(Qt::CustomContextMenu);
    layout->addWidget(m_membersListWidget, 1, 1);
    connect(m_membersListWidget, &QListWidget::itemChanged, this, &TagGroupsPage::checkItemSelection);
    connect(m_membersListWidget, &QListWidget::customContextMenuRequested, this, &TagGroupsPage::showMembersContextMenu);

    // The "pending rename actions" label
    m_pendingChangesLabel = new QLabel(i18nc("@label/rich", "<strong>There are pending changes on the categories page.<nl> "
                                                            "Please save the changes before working on tag groups.</strong>"));
    m_pendingChangesLabel->hide();
    layout->addWidget(m_pendingChangesLabel, 2, 0, 1, 2);

    QDialog *parentDialog = qobject_cast<QDialog *>(parent);
    connect(parentDialog, &QDialog::rejected, this, &TagGroupsPage::discardChanges);

    // Context menu actions
    m_newGroupAction = new QAction(i18nc("@action:inmenu", "Add group ..."), this);
    connect(m_newGroupAction, &QAction::triggered, this, &TagGroupsPage::slotAddGroup);
    m_renameAction = new QAction(this);
    connect(m_renameAction, &QAction::triggered, this, &TagGroupsPage::slotRenameGroup);
    m_deleteAction = new QAction(this);
    connect(m_deleteAction, &QAction::triggered, this, &TagGroupsPage::slotDeleteGroup);
    m_deleteMemberAction = new QAction(this);
    connect(m_deleteMemberAction, &QAction::triggered, this, &TagGroupsPage::slotDeleteMember);
    m_renameMemberAction = new QAction(this);
    connect(m_renameMemberAction, &QAction::triggered, this, &TagGroupsPage::slotRenameMember);

    m_memberMap = DB::ImageDB::instance()->memberMap();

    connect(DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::itemRemoved,
            &m_memberMap, &DB::MemberMap::deleteItem);

    connect(DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::itemRenamed,
            &m_memberMap, &DB::MemberMap::renameItem);

    connect(DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::categoryRemoved,
            &m_memberMap, &DB::MemberMap::deleteCategory);

    m_dataChanged = false;
}

void Settings::TagGroupsPage::updateCategoryTree()
{
    // Store all expanded items so that they can be expanded after reload
    QList<QPair<QString, QString>> expandedItems = QList<QPair<QString, QString>>();
    for (QTreeWidgetItemIterator it { m_categoryTreeWidget }; *it; ++it) {
        if ((*it)->isExpanded()) {
            QString parentName;
            if ((*it)->parent() != nullptr) {
                parentName = (*it)->parent()->text(0);
            }
            expandedItems.append(QPair<QString, QString>((*it)->text(0), parentName));
        }
    }

    m_categoryTreeWidget->clear();

    // Create a tree view of all groups and their sub-groups

    const QList<DB::CategoryPtr> categories = DB::ImageDB::instance()->categoryCollection()->categories();
    for (const DB::CategoryPtr &category : categories) {
        if (category->isSpecialCategory()) {
            continue;
        }

        // Add the real categories as top-level items
        QTreeWidgetItem *topLevelItem = new QTreeWidgetItem;
        topLevelItem->setText(0, category->name());
        topLevelItem->setFlags(topLevelItem->flags() & Qt::ItemIsEnabled);
        QFont font = topLevelItem->font(0);
        font.setWeight(QFont::Bold);
        topLevelItem->setFont(0, font);
        m_categoryTreeWidget->addTopLevelItem(topLevelItem);

        // Build a map with all members for each group
        QMap<QString, QStringList> membersForGroup;
        const QStringList allGroups = m_memberMap.groups(category->name());
        for (const QString &group : allGroups) {
            // FIXME: Why does the member map return an empty category?!
            if (group.isEmpty()) {
                continue;
            }

            QStringList allMembers = m_memberMap.members(category->name(), group, true);
            membersForGroup[group] = allMembers;
        }

        // Add all groups (their sub-groups will be added recursively)
        addSubCategories(topLevelItem, membersForGroup, allGroups);
    }

    // Order the items alphabetically
    m_categoryTreeWidget->sortItems(0, Qt::AscendingOrder);

    // Re-expand all previously expanded items
    for (QTreeWidgetItemIterator it { m_categoryTreeWidget }; *it; ++it) {
        QString parentName;
        if ((*it)->parent() != nullptr) {
            parentName = (*it)->parent()->text(0);
        }
        if (expandedItems.contains(QPair<QString, QString>((*it)->text(0), parentName))) {
            (*it)->setExpanded(true);
        }
    }
}

void Settings::TagGroupsPage::addSubCategories(QTreeWidgetItem *superCategory,
                                               const QMap<QString, QStringList> &membersForGroup,
                                               const QStringList &allGroups)
{
    // Process all group members
    for (auto memIt = membersForGroup.constBegin(); memIt != membersForGroup.constEnd(); ++memIt) {
        const QString &group = memIt.key();
        bool isSubGroup = false;

        // Search for a membership in another group for the current group
        for (const QStringList &members : membersForGroup) {
            if (members.contains(group)) {
                isSubGroup = true;
                break;
            }
        }

        // Add the group if it's not member of another group
        if (!isSubGroup) {
            QTreeWidgetItem *groupItem = new QTreeWidgetItem;
            groupItem->setText(0, group);
            superCategory->addChild(groupItem);

            // Search the member list for other groups
            QMap<QString, QStringList> subGroups;
            for (const QString &groupName : allGroups) {
                if (membersForGroup[group].contains(groupName)) {
                    subGroups[groupName] = membersForGroup[groupName];
                }
            }

            // If the list contains other groups, add them recursively
            if (subGroups.count() > 0) {
                addSubCategories(groupItem, subGroups, allGroups);
            }
        }
    }
}

QString Settings::TagGroupsPage::getCategory(QTreeWidgetItem *currentItem)
{
    while (currentItem->parent() != nullptr) {
        currentItem = currentItem->parent();
    }

    return currentItem->text(0);
}

void Settings::TagGroupsPage::showTreeContextMenu(QPoint point)
{
    QTreeWidgetItem *currentItem = m_categoryTreeWidget->currentItem();
    if (currentItem == nullptr) {
        return;
    }

    m_currentSubCategory = currentItem->text(0);

    if (currentItem->parent() == nullptr) {
        // It's a top-level, "real" category
        m_currentSuperCategory.clear();
    } else {
        // It's a normal sub-category that belongs to another one
        m_currentSuperCategory = currentItem->parent()->text(0);
    }

    m_currentCategory = getCategory(currentItem);

    QMenu *menu = new QMenu;
    menu->addAction(m_newGroupAction);

    // "Real" top-level categories have to processed on the category page.
    if (!m_currentSuperCategory.isEmpty()) {
        menu->addSeparator();
        m_renameAction->setText(i18nc("@action:inmenu", "Rename group \"%1\"", m_currentSubCategory));
        menu->addAction(m_renameAction);
        m_deleteAction->setText(i18nc("@action:inmenu", "Delete group \"%1\"", m_currentSubCategory));
        menu->addAction(m_deleteAction);
    }

    menu->exec(m_categoryTreeWidget->mapToGlobal(point));
    delete menu;
}

void Settings::TagGroupsPage::categoryChanged(const QString &name)
{
    if (name.isEmpty()) {
        return;
    }

    m_membersListWidget->blockSignals(true);
    m_membersListWidget->clear();

    const QStringList list = getCategoryObject(name)->items() + m_memberMap.groups(name);
    QStringList alreadyAdded;

    for (const QString &member : list) {
        if (member.isEmpty()) {
            // This can happen if we add group that currently has no members.
            continue;
        }

        if (!alreadyAdded.contains(member)) {
            alreadyAdded << member;

            if (DB::ImageDB::instance()->untaggedCategoryFeatureConfigured()
                && !Settings::SettingsData::instance()->untaggedImagesTagVisible()) {

                if (name == Settings::SettingsData::instance()->untaggedCategory()) {
                    if (member == Settings::SettingsData::instance()->untaggedTag()) {
                        continue;
                    }
                }
            }

            QListWidgetItem *newItem = new QListWidgetItem(member, m_membersListWidget);
            newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
            newItem->setCheckState(Qt::Unchecked);
        }
    }

    m_currentGroup.clear();
    m_membersListWidget->clearSelection();
    m_membersListWidget->sortItems();
    m_membersListWidget->setEnabled(false);
    m_membersListWidget->blockSignals(false);
}

void Settings::TagGroupsPage::slotGroupSelected(QTreeWidgetItem *item)
{
    // When something else than a "real" category has been selected before,
    // we have to save it's members.
    if (!m_currentGroup.isEmpty()) {
        saveOldGroup();
    }

    if (item->parent() == nullptr) {
        // A "real" category has been selected, not a group
        m_currentCategory.clear();
        m_currentGroup.clear();
        m_membersListWidget->setEnabled(false);
        categoryChanged(item->text(0));
        m_tagsInGroupLabel->setText(m_selectGroupToAddTags);
        return;
    }

    // Let's see if the category changed
    QString itemCategory = getCategory(item);
    if (m_currentCategory != itemCategory) {
        m_currentCategory = itemCategory;
        categoryChanged(m_currentCategory);
    }

    m_currentGroup = item->text(0);
    selectMembers(m_currentGroup);
    m_tagsInGroupLabel->setText(i18nc("@label", "Tags in group \"%1\" of category \"%2\"",
                                      m_currentGroup, m_currentCategory));
}

void Settings::TagGroupsPage::slotAddGroup()
{
    bool ok;
    DB::CategoryPtr category = getCategoryObject(m_currentCategory);
    QStringList groups = m_memberMap.groups(m_currentCategory);
    QStringList tags;
    for (QString tag : category->items()) {
        if (!groups.contains(tag)) {
            tags << tag;
        }
    }

    //// reject existing group names:
    // KStringListValidator validator(groups);
    // QString newSubCategory = KInputDialog::getText(i18nc("@title:window","New Group"),
    //                                                i18nc("@label:textbox","Group name:"),
    //                                                QString() /*value*/,
    //                                                &ok,
    //                                                this /*parent*/,
    //                                                &validator,
    //                                                QString() /*mask*/,
    //                                                QString() /*WhatsThis*/,
    //                                                tags /*completion*/
    //                                                );
    //  FIXME: KF5-port: QInputDialog does not accept a validator,
    //  and KInputDialog was removed in KF5. -> Reimplement input validation using other stuff
    QString newSubCategory = QInputDialog::getText(this,
                                                   i18nc("@title:window", "New Group"),
                                                   i18nc("@label:textbox", "Group name:"),
                                                   QLineEdit::Normal,
                                                   QString(),
                                                   &ok)
                                 .trimmed();
    if (groups.contains(newSubCategory))
        return; // only a workaround until GUI-support for validation is restored
    if (!ok) {
        return;
    }

    // Let's see if we already have this group
    if (groups.contains(newSubCategory)) {
        // (with the validator working correctly, we should not get to this point)
        KMessageBox::error(this,
                           i18nc("@info", "<p>The group \"%1\" already exists.</p>", newSubCategory),
                           i18nc("@title:window", "Cannot add group"));
        return;
    }

    // Add the group as a new tag to the respective category
    MainWindow::DirtyIndicator::suppressMarkDirty(true);
    category->addItem(newSubCategory);
    MainWindow::DirtyIndicator::suppressMarkDirty(false);
    QMap<CategoryEdit, QString> categoryChange;
    categoryChange[CategoryEdit::Category] = m_currentCategory;
    categoryChange[CategoryEdit::Add] = newSubCategory;
    m_categoryChanges.append(categoryChange);

    // Add the group
    m_memberMap.addGroup(m_currentCategory, newSubCategory);

    // Display the new group
    categoryChanged(m_currentCategory);

    // Display the new item
    QTreeWidgetItem *parentItem = m_categoryTreeWidget->currentItem();
    addNewSubItem(newSubCategory, parentItem);

    // Check if we also have to update some other group (in case this is not a top-level group)
    if (!m_currentSuperCategory.isEmpty()) {
        m_memberMap.addMemberToGroup(m_currentCategory, parentItem->text(0), newSubCategory);
        slotGroupSelected(parentItem);
    }

    m_dataChanged = true;
}

void Settings::TagGroupsPage::addNewSubItem(QString &name, QTreeWidgetItem *parentItem)
{
    QTreeWidgetItem *newItem = new QTreeWidgetItem;
    newItem->setText(0, name);
    parentItem->addChild(newItem);

    if (!parentItem->isExpanded()) {
        parentItem->setExpanded(true);
    }
}

QTreeWidgetItem *Settings::TagGroupsPage::findCategoryItem(QString category)
{
    QTreeWidgetItem *categoryItem = nullptr;
    for (int i = 0; i < m_categoryTreeWidget->topLevelItemCount(); ++i) {
        categoryItem = m_categoryTreeWidget->topLevelItem(i);
        if (categoryItem->text(0) == category) {
            break;
        }
    }

    return categoryItem;
}

void Settings::TagGroupsPage::checkItemSelection(QListWidgetItem *)
{
    m_dataChanged = true;
    saveOldGroup();
    updateCategoryTree();
}

void Settings::TagGroupsPage::slotRenameGroup()
{
    bool ok;
    DB::CategoryPtr category = getCategoryObject(m_currentCategory);
    QStringList groups = m_memberMap.groups(m_currentCategory);
    QStringList tags;
    for (QString tag : category->items()) {
        if (!groups.contains(tag)) {
            tags << tag;
        }
    }

    // FIXME: reject existing group names
    QString newSubCategoryName = QInputDialog::getText(this,
                                                       i18nc("@title:window", "Rename Group"),
                                                       i18nc("@label:textbox", "New group name:"),
                                                       QLineEdit::Normal,
                                                       m_currentSubCategory,
                                                       &ok)
                                     .trimmed();

    if (!ok || m_currentSubCategory == newSubCategoryName) {
        return;
    }

    if (groups.contains(newSubCategoryName)) {
        // (with the validator working correctly, we should not get to this point)
        KMessageBox::error(this,
                           xi18nc("@info", "<para>Cannot rename group \"%1\" to \"%2\": "
                                           "\"%2\" already exists in category \"%3\"</para>",
                                  m_currentSubCategory,
                                  newSubCategoryName,
                                  m_currentCategory),
                           i18nc("@title:window", "Rename Group"));
        return;
    }

    QTreeWidgetItem *selectedGroup = m_categoryTreeWidget->currentItem();

    saveOldGroup();

    // Update the group
    m_memberMap.renameGroup(m_currentCategory, m_currentSubCategory, newSubCategoryName);

    // Update the tag in the respective category
    MainWindow::DirtyIndicator::suppressMarkDirty(true);
    category->renameItem(m_currentSubCategory, newSubCategoryName);
    MainWindow::DirtyIndicator::suppressMarkDirty(false);
    QMap<CategoryEdit, QString> categoryChange;
    categoryChange[CategoryEdit::Category] = m_currentCategory;
    categoryChange[CategoryEdit::Rename] = m_currentSubCategory;
    categoryChange[CategoryEdit::NewName] = newSubCategoryName;
    m_categoryChanges.append(categoryChange);
    m_dataChanged = true;

    // Search for all possible sub-category items in this category that have to be renamed
    QTreeWidgetItem *categoryItem = findCategoryItem(m_currentCategory);
    for (int i = 0; i < categoryItem->childCount(); ++i) {
        renameAllSubCategories(categoryItem->child(i), m_currentSubCategory, newSubCategoryName);
    }

    // Update the displayed items
    categoryChanged(m_currentCategory);
    slotGroupSelected(selectedGroup);

    m_dataChanged = true;
}

void Settings::TagGroupsPage::renameAllSubCategories(QTreeWidgetItem *categoryItem,
                                                     QString oldName,
                                                     QString newName)
{
    // Probably, it item itself has to be renamed
    if (categoryItem->text(0) == oldName) {
        categoryItem->setText(0, newName);
    }

    // Also check all sub-categories recursively
    for (int i = 0; i < categoryItem->childCount(); ++i) {
        renameAllSubCategories(categoryItem->child(i), oldName, newName);
    }
}

void Settings::TagGroupsPage::slotDeleteGroup()
{
    QTreeWidgetItem *currentItem = m_categoryTreeWidget->currentItem();
    QString message;

    if (currentItem->childCount() > 0) {
        message = xi18nc("@info", "<para>Really delete group \"%1\"?</para>"
                                  "<para>Sub-categories of this group will be moved to the super category of \"%1\" (\"%2\").<nl/> "
                                  "All other memberships of the sub-categories will stay intact.</para>",
                         m_currentSubCategory,
                         m_currentSuperCategory);
    } else {
        message = xi18nc("@info", "<para>Really delete group \"%1\"?</para>", m_currentSubCategory);
    }

    int res = KMessageBox::warningContinueCancel(this,
                                                 message,
                                                 i18nc("@title:window", "Delete Group"),
                                                 KGuiItem(i18n("&Delete"),
                                                          QString::fromUtf8("editdelete")));
    if (res == KMessageBox::Cancel) {
        return;
    }

    // Delete the group
    m_memberMap.deleteGroup(m_currentCategory, m_currentSubCategory);

    // Delete the tag
    MainWindow::DirtyIndicator::suppressMarkDirty(true);
    getCategoryObject(m_currentCategory)->removeItem(m_currentSubCategory);
    MainWindow::DirtyIndicator::suppressMarkDirty(false);
    QMap<CategoryEdit, QString> categoryChange;
    categoryChange[CategoryEdit::Category] = m_currentCategory;
    categoryChange[CategoryEdit::Remove] = m_currentSubCategory;
    m_categoryChanges.append(categoryChange);
    m_dataChanged = true;

    slotPageChange();

    m_dataChanged = true;
}

void Settings::TagGroupsPage::saveOldGroup()
{
    QStringList list;
    for (int i = 0; i < m_membersListWidget->count(); ++i) {
        QListWidgetItem *item = m_membersListWidget->item(i);
        if (item->checkState() == Qt::Checked) {
            list << item->text();
        }
    }

    if (!m_currentCategory.isEmpty() && !m_currentGroup.isEmpty())
        m_memberMap.setMembers(m_currentCategory, m_currentGroup, list);
}

void Settings::TagGroupsPage::selectMembers(const QString &group)
{
    m_membersListWidget->blockSignals(true);
    m_membersListWidget->setEnabled(false);

    m_currentGroup = group;
    QStringList memberList = m_memberMap.members(m_currentCategory, group, false);

    for (int i = 0; i < m_membersListWidget->count(); ++i) {
        QListWidgetItem *item = m_membersListWidget->item(i);
        item->setCheckState(Qt::Unchecked);

        if (!m_memberMap.canAddMemberToGroup(m_currentCategory, group, item->text())) {
            item->setFlags(item->flags() & ~Qt::ItemIsSelectable & ~Qt::ItemIsEnabled);
        } else {
            item->setFlags(item->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
            if (memberList.contains(item->text())) {
                item->setCheckState(Qt::Checked);
            }
        }
    }

    m_membersListWidget->setEnabled(true);
    m_membersListWidget->blockSignals(false);
}

void Settings::TagGroupsPage::slotPageChange()
{
    m_tagsInGroupLabel->setText(m_selectGroupToAddTags);
    m_membersListWidget->setEnabled(false);
    m_membersListWidget->clear();
    m_currentCategory.clear();
    updateCategoryTree();
}

void Settings::TagGroupsPage::saveSettings()
{
    saveOldGroup();
    slotPageChange();
    DB::ImageDB::instance()->memberMap() = m_memberMap;
    m_categoryChanges.clear();

    if (m_dataChanged) {
        m_dataChanged = false;
        MainWindow::DirtyIndicator::markDirty();
    }

    m_categoryTreeWidget->setEnabled(true);
    m_membersListWidget->setEnabled(true);
    m_pendingChangesLabel->hide();
}

void Settings::TagGroupsPage::discardChanges()
{
    m_memberMap = DB::ImageDB::instance()->memberMap();
    slotPageChange();
    m_dataChanged = false;

    // Revert all changes to the "real" category objects
    MainWindow::DirtyIndicator::suppressMarkDirty(true);
    for (int i = m_categoryChanges.size() - 1; i >= 0; i--) {
        DB::CategoryPtr category = getCategoryObject(m_categoryChanges.at(i)[CategoryEdit::Category]);

        if (m_categoryChanges.at(i).contains(CategoryEdit::Add)) {
            // Remove added tags
            category->removeItem(m_categoryChanges.at(i)[CategoryEdit::Add]);
        } else if (m_categoryChanges.at(i).contains(CategoryEdit::Remove)) {
            // Add removed tags
            category->addItem(m_categoryChanges.at(i)[CategoryEdit::Add]);
        } else if (m_categoryChanges.at(i).contains(CategoryEdit::Rename)) {
            // Re-rename tags to their old name
            category->renameItem(m_categoryChanges.at(i)[CategoryEdit::NewName],
                                 m_categoryChanges.at(i)[Rename]);
        }
    }
    MainWindow::DirtyIndicator::suppressMarkDirty(false);

    m_categoryChanges.clear();

    m_categoryTreeWidget->setEnabled(true);
    m_membersListWidget->setEnabled(true);
    m_pendingChangesLabel->hide();
}

void Settings::TagGroupsPage::loadSettings()
{
    categoryChanged(m_currentCategory);
    updateCategoryTree();
}

void Settings::TagGroupsPage::categoryChangesPending()
{
    m_categoryTreeWidget->setEnabled(false);
    m_membersListWidget->setEnabled(false);
    m_pendingChangesLabel->show();
}

DB::MemberMap *Settings::TagGroupsPage::memberMap()
{
    return &m_memberMap;
}

void Settings::TagGroupsPage::processDrop(QTreeWidgetItem *draggedItem, QTreeWidgetItem *targetItem)
{
    if (targetItem->parent() != nullptr) {
        // Dropped on a group

        // Select the group
        m_categoryTreeWidget->setCurrentItem(targetItem);
        slotGroupSelected(targetItem);

        // Check the dragged group on the member side to make it a sub-group of the target group
        m_membersListWidget->findItems(draggedItem->text(0), Qt::MatchExactly)[0]->setCheckState(Qt::Checked);
    } else {
        // Dropped on a top-level category

        // Check if it's already a direct child of the category.
        // If so, we don't need to do anything.
        QTreeWidgetItem *parent = draggedItem->parent();
        if (parent->parent() == nullptr) {
            return;
        }

        // Select the former super group
        m_categoryTreeWidget->setCurrentItem(parent);
        slotGroupSelected(parent);

        // Deselect the dragged group (this will bring it to the top level)
        m_membersListWidget->findItems(draggedItem->text(0), Qt::MatchExactly)[0]->setCheckState(Qt::Unchecked);
    }
}

void Settings::TagGroupsPage::showMembersContextMenu(QPoint point)
{
    if (m_membersListWidget->currentItem() == nullptr) {
        return;
    }

    QMenu *menu = new QMenu;

    m_renameMemberAction->setText(i18nc("@action:inmenu", "Rename \"%1\"", m_membersListWidget->currentItem()->text()));
    menu->addAction(m_renameMemberAction);
    m_deleteMemberAction->setText(i18nc("@action:inmenu", "Delete \"%1\"", m_membersListWidget->currentItem()->text()));
    menu->addAction(m_deleteMemberAction);

    menu->exec(m_membersListWidget->mapToGlobal(point));
    delete menu;
}

void Settings::TagGroupsPage::slotRenameMember()
{
    bool ok;
    QString newTagName = QInputDialog::getText(this,
                                               i18nc("@title:window", "New Tag Name"),
                                               i18nc("@label:textbox", "Tag name:"),
                                               QLineEdit::Normal,
                                               m_membersListWidget->currentItem()->text(),
                                               &ok)
                             .trimmed();
    if (!ok || newTagName == m_membersListWidget->currentItem()->text()) {
        return;
    }

    // Update the tag name in the database
    MainWindow::DirtyIndicator::suppressMarkDirty(true);
    getCategoryObject(m_currentCategory)->renameItem(m_membersListWidget->currentItem()->text(), newTagName);
    MainWindow::DirtyIndicator::suppressMarkDirty(false);
    QMap<CategoryEdit, QString> categoryChange;
    categoryChange[CategoryEdit::Category] = m_currentCategory;
    categoryChange[CategoryEdit::Rename] = m_membersListWidget->currentItem()->text();
    categoryChange[CategoryEdit::NewName] = newTagName;
    m_categoryChanges.append(categoryChange);

    // Update the displayed tag name
    m_membersListWidget->currentItem()->setText(newTagName);

    // Re-order the tags, as their alphabetial order may have changed
    m_membersListWidget->sortItems();
}

void Settings::TagGroupsPage::slotDeleteMember()
{
    QString memberToDelete = m_membersListWidget->currentItem()->text();

    if (m_memberMap.groups(m_currentCategory).contains(memberToDelete)) {
        // The item to delete is a group

        // Find the tag in the tree view and select it ...
        QTreeWidgetItemIterator it(m_categoryTreeWidget);
        while (*it) {
            if ((*it)->text(0) == memberToDelete && getCategory((*it)) == m_currentCategory) {
                m_categoryTreeWidget->setCurrentItem((*it));
                m_currentSubCategory = (*it)->text(0);
                m_currentSuperCategory = (*it)->parent()->text(0);
                break;
            }
            ++it;
        }
        // ... then delete it like it had been requested by the TreeWidget's context menu
        slotDeleteGroup();

    } else {
        // The item to delete is a normal tag
        int res = KMessageBox::warningContinueCancel(this,
                                                     xi18nc("@info", "<para>Do you really want to delete \"%1\"?</para>"
                                                                     "<para>Deleting the item will remove any information "
                                                                     "about it from any image containing the item.</para>",
                                                            memberToDelete),
                                                     i18nc("@title:window", "Really delete %1?", memberToDelete),
                                                     KGuiItem(i18n("&Delete"), QString::fromUtf8("editdelete")));
        if (res != KMessageBox::Continue) {
            return;
        }

        // Delete the tag as if it had been deleted from the annotation dialog.
        getCategoryObject(m_currentCategory)->removeItem(memberToDelete);
        slotPageChange();
    }
}

DB::CategoryPtr Settings::TagGroupsPage::getCategoryObject(QString category) const
{
    return DB::ImageDB::instance()->categoryCollection()->categoryForName(category);
}

// vi:expandtab:tabstop=4 shiftwidth=4:

#include "moc_TagGroupsPage.cpp"
