#include "categorytreewidget.h" #include "db/dao/categorydao.h" #include "db/dao/mailitemdao.h" #include #include #include #include #include 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 categories = CategoryDao::findByAccount(m_accountId); // Build tree: first root categories, then children QMap> childrenMap; QVector 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 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 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 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()); }