257 lines
8.1 KiB
C++
257 lines
8.1 KiB
C++
#include "categorytreewidget.h"
|
|||
|
|
#include "db/dao/categorydao.h"
|
||
|
|
#include "db/dao/mailitemdao.h"
|
||
|
|
#include <QInputDialog>
|
||
|
|
#include <QColorDialog>
|
||
|
|
#include <QMessageBox>
|
||
|
|
#include <QDebug>
|
||
|
|
#include <QApplication>
|
||
|
|
|
||
|
|
CategoryTreeWidget::CategoryTreeWidget(QWidget *parent) : QTreeWidget(parent)
|
||
|
|
{
|
||
|
|
setHeaderLabel(tr("Categorías"));
|
||
|
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||
|
|
setExpandsOnDoubleClick(false);
|
||
|
|
setAnimated(true);
|
||
|
|
setIndentation(16);
|
||
|
|
|
||
|
|
connect(this, &QTreeWidget::customContextMenuRequested, this, &CategoryTreeWidget::onCategoryContextMenu);
|
||
|
|
connect(this, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem* item, int) {
|
||
|
|
Category cat = itemToCategory(item);
|
||
|
|
if (cat.isValid()) emit categoryDoubleClicked(cat);
|
||
|
|
});
|
||
|
|
connect(this, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem* item, int) {
|
||
|
|
Category cat = itemToCategory(item);
|
||
|
|
if (cat.isValid()) emit categorySelected(cat);
|
||
|
|
});
|
||
|
|
|
||
|
|
setupContextMenu();
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::setupContextMenu()
|
||
|
|
{
|
||
|
|
m_contextMenu = new QMenu(this);
|
||
|
|
m_contextMenu->addAction(tr("Nueva categoría"), this, &CategoryTreeWidget::onNewCategory);
|
||
|
|
m_contextMenu->addAction(tr("Nueva subcategoría"), this, [this]() {
|
||
|
|
QTreeWidgetItem* item = currentItem();
|
||
|
|
if (item) onEditCategory(item); // Reuse for subcategory creation
|
||
|
|
});
|
||
|
|
m_contextMenu->addSeparator();
|
||
|
|
m_contextMenu->addAction(tr("Editar"), this, [this]() {
|
||
|
|
QTreeWidgetItem* item = currentItem();
|
||
|
|
if (item) onEditCategory(item);
|
||
|
|
});
|
||
|
|
m_contextMenu->addAction(tr("Eliminar"), this, [this]() {
|
||
|
|
QTreeWidgetItem* item = currentItem();
|
||
|
|
if (item) onDeleteCategory(item);
|
||
|
|
});
|
||
|
|
m_contextMenu->addSeparator();
|
||
|
|
m_contextMenu->addAction(tr("Asignar a correos seleccionados"), this, [this]() {
|
||
|
|
QTreeWidgetItem* item = currentItem();
|
||
|
|
if (item) onAssignToSelectedMails(item);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::setAccountId(qint64 accountId)
|
||
|
|
{
|
||
|
|
m_accountId = accountId;
|
||
|
|
refresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::refresh()
|
||
|
|
{
|
||
|
|
clear();
|
||
|
|
m_categoryItems.clear();
|
||
|
|
loadCategories();
|
||
|
|
expandAll();
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::loadCategories()
|
||
|
|
{
|
||
|
|
QVector<Category> categories = CategoryDao::findByAccount(m_accountId);
|
||
|
|
|
||
|
|
// Build tree: first root categories, then children
|
||
|
|
QMap<qint64, QVector<Category>> childrenMap;
|
||
|
|
QVector<Category> roots;
|
||
|
|
|
||
|
|
for (const Category& cat : categories) {
|
||
|
|
if (cat.parentCategoryId >= 0) {
|
||
|
|
childrenMap[cat.parentCategoryId].append(cat);
|
||
|
|
} else {
|
||
|
|
roots.append(cat);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Sort by sortOrder
|
||
|
|
auto sortByOrder = [](const Category& a, const Category& b) {
|
||
|
|
return a.sortOrder < b.sortOrder;
|
||
|
|
};
|
||
|
|
std::sort(roots.begin(), roots.end(), sortByOrder);
|
||
|
|
for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) {
|
||
|
|
std::sort(it.value().begin(), it.value().end(), sortByOrder);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const Category& root : roots) {
|
||
|
|
addCategory(root);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::addCategory(const Category& cat, QTreeWidgetItem* parent)
|
||
|
|
{
|
||
|
|
QTreeWidgetItem* item = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem(this);
|
||
|
|
item->setText(0, cat.name);
|
||
|
|
item->setData(0, Qt::UserRole, cat.id);
|
||
|
|
item->setData(0, Qt::UserRole + 1, cat.accountId);
|
||
|
|
item->setData(0, Qt::UserRole + 2, cat.parentCategoryId);
|
||
|
|
item->setData(0, Qt::UserRole + 3, cat.sortOrder);
|
||
|
|
|
||
|
|
// Color indicator
|
||
|
|
QPixmap pix(16, 16);
|
||
|
|
pix.fill(cat.color);
|
||
|
|
item->setIcon(0, QIcon(pix));
|
||
|
|
|
||
|
|
// Tooltip with details
|
||
|
|
QString tooltip = QString("%1\nColor: %2\nAccount: %3")
|
||
|
|
.arg(cat.name)
|
||
|
|
.arg(cat.color.name())
|
||
|
|
.arg(cat.isGlobal() ? "Global" : QString::number(cat.accountId));
|
||
|
|
item->setToolTip(0, tooltip);
|
||
|
|
|
||
|
|
m_categoryItems[cat.id] = item;
|
||
|
|
|
||
|
|
// Add children
|
||
|
|
QVector<Category> children = CategoryDao::findChildren(cat.id);
|
||
|
|
for (const Category& child : children) {
|
||
|
|
addCategory(child, item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
QTreeWidgetItem* CategoryTreeWidget::findItemByCategoryId(qint64 categoryId)
|
||
|
|
{
|
||
|
|
return m_categoryItems.value(categoryId, nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::onMailSelected(qint64 mailId)
|
||
|
|
{
|
||
|
|
// Update checkboxes/selection state for categories this mail belongs to
|
||
|
|
QVector<qint64> catIds = CategoryDao::categoriesForMail(mailId);
|
||
|
|
|
||
|
|
for (auto it = m_categoryItems.begin(); it != m_categoryItems.end(); ++it) {
|
||
|
|
QTreeWidgetItem* item = it.value();
|
||
|
|
bool assigned = catIds.contains(it.key());
|
||
|
|
item->setCheckState(0, assigned ? Qt::Checked : Qt::Unchecked);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::onCategoryContextMenu(const QPoint& pos)
|
||
|
|
{
|
||
|
|
QTreeWidgetItem* item = itemAt(pos);
|
||
|
|
if (item) setCurrentItem(item);
|
||
|
|
|
||
|
|
// Update action states
|
||
|
|
bool hasItem = item != nullptr;
|
||
|
|
QList<QAction*> actions = m_contextMenu->actions();
|
||
|
|
for (QAction* act : actions) {
|
||
|
|
QString text = act->text();
|
||
|
|
if (text.contains("Editar") || text.contains("Eliminar") || text.contains("Asignar")) {
|
||
|
|
act->setEnabled(hasItem);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
m_contextMenu->exec(viewport()->mapToGlobal(pos));
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::onNewCategory()
|
||
|
|
{
|
||
|
|
QTreeWidgetItem* parentItem = currentItem();
|
||
|
|
qint64 parentId = parentItem ? parentItem->data(0, Qt::UserRole).toLongLong() : -1;
|
||
|
|
|
||
|
|
bool ok;
|
||
|
|
QString name = QInputDialog::getText(this, tr("Nueva categoría"), tr("Nombre:"), QLineEdit::Normal, QString(), &ok);
|
||
|
|
if (!ok || name.trimmed().isEmpty()) return;
|
||
|
|
|
||
|
|
QColor color = QColorDialog::getColor(QColor("#1976D2"), this, tr("Color de la categoría"));
|
||
|
|
if (!color.isValid()) color = QColor("#1976D2");
|
||
|
|
|
||
|
|
Category cat;
|
||
|
|
cat.name = name.trimmed();
|
||
|
|
cat.color = color;
|
||
|
|
cat.accountId = m_accountId;
|
||
|
|
cat.parentCategoryId = parentId;
|
||
|
|
cat.sortOrder = 0;
|
||
|
|
|
||
|
|
if (CategoryDao::insert(cat)) {
|
||
|
|
refresh();
|
||
|
|
emit categoryCreated(cat);
|
||
|
|
} else {
|
||
|
|
QMessageBox::warning(this, tr("Error"), tr("No se pudo crear la categoría."));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::onEditCategory(QTreeWidgetItem* item)
|
||
|
|
{
|
||
|
|
if (!item) return;
|
||
|
|
|
||
|
|
qint64 catId = item->data(0, Qt::UserRole).toLongLong();
|
||
|
|
auto catOpt = CategoryDao::findById(catId);
|
||
|
|
if (!catOpt.has_value()) return;
|
||
|
|
|
||
|
|
Category cat = *catOpt;
|
||
|
|
|
||
|
|
bool ok;
|
||
|
|
QString name = QInputDialog::getText(this, tr("Editar categoría"), tr("Nombre:"), QLineEdit::Normal, cat.name, &ok);
|
||
|
|
if (!ok || name.trimmed().isEmpty()) return;
|
||
|
|
|
||
|
|
QColor color = QColorDialog::getColor(cat.color, this, tr("Color de la categoría"));
|
||
|
|
if (!color.isValid()) color = cat.color;
|
||
|
|
|
||
|
|
cat.name = name.trimmed();
|
||
|
|
cat.color = color;
|
||
|
|
|
||
|
|
if (CategoryDao::update(cat)) {
|
||
|
|
refresh();
|
||
|
|
emit categoryEdited(cat);
|
||
|
|
} else {
|
||
|
|
QMessageBox::warning(this, tr("Error"), tr("No se pudo actualizar la categoría."));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::onDeleteCategory(QTreeWidgetItem* item)
|
||
|
|
{
|
||
|
|
if (!item) return;
|
||
|
|
|
||
|
|
qint64 catId = item->data(0, Qt::UserRole).toLongLong();
|
||
|
|
|
||
|
|
if (QMessageBox::question(this, tr("Eliminar categoría"),
|
||
|
|
tr("¿Eliminar esta categoría y quitarla de todos los correos?"),
|
||
|
|
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (CategoryDao::remove(catId)) {
|
||
|
|
refresh();
|
||
|
|
emit categoryDeleted(catId);
|
||
|
|
} else {
|
||
|
|
QMessageBox::warning(this, tr("Error"), tr("No se pudo eliminar la categoría."));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CategoryTreeWidget::onAssignToSelectedMails(QTreeWidgetItem* item)
|
||
|
|
{
|
||
|
|
if (!item) return;
|
||
|
|
|
||
|
|
qint64 catId = item->data(0, Qt::UserRole).toLongLong();
|
||
|
|
|
||
|
|
// This would need integration with the mail list view to get selected mail IDs
|
||
|
|
// For now, emit signal for the main window to handle
|
||
|
|
emit assignCategoryRequested(-1, catId); // -1 = use current selection
|
||
|
|
}
|
||
|
|
|
||
|
|
Category CategoryTreeWidget::itemToCategory(QTreeWidgetItem* item) const
|
||
|
|
{
|
||
|
|
if (!item) return Category();
|
||
|
|
|
||
|
|
qint64 id = item->data(0, Qt::UserRole).toLongLong();
|
||
|
|
auto catOpt = CategoryDao::findById(id);
|
||
|
|
return catOpt.value_or(Category());
|
||
|
|
}
|