File indexing completed on 2024-05-19 05:49:14

0001 /*
0002     SPDX-FileCopyrightText: 2007 Nicolas Ternisien <nicolas.ternisien@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "multipleFileList.h"
0008 
0009 #include <QAction>
0010 #include <QHeaderView>
0011 #include <QPushButton>
0012 
0013 #include <KLocalizedString>
0014 
0015 #include <QFileInfo>
0016 #include <QIcon>
0017 
0018 #include "defaults.h"
0019 
0020 #include "ksystemlog_debug.h"
0021 
0022 MultipleFileList::MultipleFileList(QWidget *parent, const QString &descriptionText)
0023     : QWidget(parent)
0024     , mFileListHelper(this)
0025 {
0026     qCDebug(KSYSTEMLOG) << "Initializing multiple file list...";
0027 
0028     setupUi(this);
0029 
0030     mWarningBox = new KMessageWidget(this);
0031     mWarningBox->setVisible(false);
0032     mWarningBox->setMessageType(KMessageWidget::Warning);
0033     mWarningBox->setText(
0034         i18n("Some log files do not exist.\n"
0035              "Modes with missing log files will be unavailable."));
0036     mWarningBox->setCloseButtonVisible(false);
0037     mWarningBox->setIcon(QIcon::fromTheme(QStringLiteral("dialog-warning")));
0038     vboxLayout->insertWidget(1, mWarningBox);
0039 
0040     description->setText(descriptionText);
0041 
0042     mFileListHelper.prepareButton(modify, QIcon::fromTheme(QStringLiteral("document-open")), this, SLOT(modifyItem()), fileList);
0043 
0044     fileList->header()->setVisible(false);
0045 
0046     // Add a separator in the MultipleFileList
0047     auto separator = new QAction(this);
0048     separator->setSeparator(true);
0049     fileList->addAction(separator);
0050 
0051     mFileListHelper.prepareButton(remove, QIcon::fromTheme(QStringLiteral("list-remove")), this, SLOT(removeSelectedItem()), fileList);
0052 
0053     mFileListHelper.prepareButton(up, QIcon::fromTheme(QStringLiteral("go-up")), this, SLOT(moveUpItem()), fileList);
0054 
0055     mFileListHelper.prepareButton(down, QIcon::fromTheme(QStringLiteral("go-down")), this, SLOT(moveDownItem()), fileList);
0056 
0057     mFileListHelper.prepareButton(removeAll, QIcon::fromTheme(QStringLiteral("trash-empty")), this, SLOT(removeAllItems()), fileList);
0058 
0059     connect(fileList, &QTreeWidget::itemSelectionChanged, this, &MultipleFileList::updateButtons);
0060     connect(fileList, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(modifyItem(QTreeWidgetItem *)));
0061     connect(this, &MultipleFileList::fileListChanged, this, &MultipleFileList::updateButtons);
0062 
0063     connect(&mAddButtons, &QButtonGroup::buttonClicked, this, &MultipleFileList::addItem);
0064 
0065     updateButtons();
0066 
0067     qCDebug(KSYSTEMLOG) << "Multiple File list initialized";
0068 }
0069 
0070 MultipleFileList::~MultipleFileList()
0071 {
0072 }
0073 
0074 void MultipleFileList::updateButtons()
0075 {
0076     qCDebug(KSYSTEMLOG) << "Updating buttons...";
0077 
0078     if (isFileListsEmpty()) {
0079         mFileListHelper.setEnabledAction(removeAll, false);
0080     } else {
0081         mFileListHelper.setEnabledAction(removeAll, true);
0082     }
0083 
0084     const QList<QTreeWidgetItem *> selectedItems = fileList->selectedItems();
0085 
0086     // If the selection is not empty and a empty item is not selected
0087     QTreeWidgetItem *categoryItem = nullptr;
0088     if (!selectedItems.isEmpty() && !isEmptyItem(selectedItems.at(0))) {
0089         categoryItem = findCategoryOfChild(selectedItems.at(0));
0090     }
0091 
0092     if (categoryItem) {
0093         const int categoryIndex = fileList->indexOfTopLevelItem(categoryItem);
0094 
0095         mFileListHelper.setEnabledAction(remove, true);
0096         mFileListHelper.setEnabledAction(modify, true);
0097 
0098         QTreeWidgetItem *selectedItem = selectedItems.at(0);
0099 
0100         // If the item is at the top of the list, it could not be upped anymore
0101         if (categoryItem->indexOfChild(selectedItem) == 0) {
0102             mFileListHelper.setEnabledAction(up, false);
0103         } else {
0104             mFileListHelper.setEnabledAction(up, true);
0105         }
0106 
0107         // If the item is at bottom of the list, it could not be downed anymore
0108         if (categoryItem->indexOfChild(selectedItem) == categoryCount(categoryIndex) - 1) {
0109             mFileListHelper.setEnabledAction(down, false);
0110         } else {
0111             mFileListHelper.setEnabledAction(down, true);
0112         }
0113     }
0114     // If nothing is selected, disabled special buttons
0115     else {
0116         mFileListHelper.setEnabledAction(remove, false);
0117         mFileListHelper.setEnabledAction(modify, false);
0118         mFileListHelper.setEnabledAction(up, false);
0119         mFileListHelper.setEnabledAction(down, false);
0120     }
0121 
0122     qCDebug(KSYSTEMLOG) << "Buttons updated";
0123 }
0124 
0125 bool MultipleFileList::isFileListsEmpty() const
0126 {
0127     for (int i = 0, total = fileList->topLevelItemCount(); i < total; ++i) {
0128         if (categoryCount(i) != 0) {
0129             qCDebug(KSYSTEMLOG) << "Is not empty";
0130             return false;
0131         }
0132     }
0133 
0134     qCDebug(KSYSTEMLOG) << "Is empty";
0135     return true;
0136 }
0137 
0138 bool MultipleFileList::isOneOfCategoryEmpty() const
0139 {
0140     for (int i = 0, total = fileList->topLevelItemCount(); i < total; ++i) {
0141         if (categoryCount(i) == 0) {
0142             qCDebug(KSYSTEMLOG) << "A category is empty";
0143             return true;
0144         }
0145     }
0146 
0147     qCDebug(KSYSTEMLOG) << "No category is empty";
0148     return false;
0149 }
0150 
0151 int MultipleFileList::categoryCount(int index) const
0152 {
0153     QTreeWidgetItem *item = fileList->topLevelItem(index);
0154     if (!item) {
0155         qCCritical(KSYSTEMLOG) << "Index out of range" << index;
0156         return 0;
0157     }
0158 
0159     int count = 0;
0160     for (int i = 0, total = item->childCount(); i < total; ++i) {
0161         QTreeWidgetItem *childItem = item->child(i);
0162         if (!isEmptyItem(childItem)) {
0163             count++;
0164         }
0165     }
0166 
0167     return count;
0168 }
0169 
0170 int MultipleFileList::addCategory(const QString &itemName, const QString &buttonName)
0171 {
0172     auto item = new QTreeWidgetItem(fileList, QStringList(itemName));
0173     item->setExpanded(true);
0174     QFont font = item->font(0);
0175     font.setBold(true);
0176     item->setFont(0, font);
0177 
0178     int const index = fileList->indexOfTopLevelItem(item);
0179 
0180     auto addButton = new QPushButton(buttonName, this);
0181     QAction *action = mFileListHelper.prepareButtonAndAction(addButton, QIcon::fromTheme(QStringLiteral("document-new")));
0182 
0183     // Insert the action in first position
0184     fileList->insertAction(fileList->actions().at(mAddButtons.buttons().size()), action);
0185 
0186     mAddButtons.addButton(addButton, index);
0187 
0188     vboxLayout1->insertWidget(index, addButton);
0189 
0190     updateEmptyItems();
0191 
0192     updateButtons();
0193 
0194     return index;
0195 }
0196 
0197 void MultipleFileList::addItem(QAbstractButton *button)
0198 {
0199     const int category = mAddButtons.id(button);
0200     qCDebug(KSYSTEMLOG) << "Adding item" << category;
0201 
0202     // Open a standard Filedialog
0203     const QList<QUrl> urls = mFileListHelper.openUrls();
0204 
0205     QTreeWidgetItem *categoryItem = fileList->topLevelItem(category);
0206 
0207     const QStringList paths = mFileListHelper.findPaths(urls);
0208     for (const QString &path : paths) {
0209         addItemInternal(categoryItem, path);
0210     }
0211 
0212     updateEmptyItems();
0213 
0214     Q_EMIT fileListChanged();
0215 }
0216 
0217 void MultipleFileList::addItemInternal(QTreeWidgetItem *categoryItem, const QString &path)
0218 {
0219     qCDebug(KSYSTEMLOG) << "Adding" << path << "to" << categoryItem->text(0);
0220     auto item = new QTreeWidgetItem(QStringList(path));
0221 
0222     QFileInfo const checkFile(path);
0223     if (!checkFile.exists()) {
0224         mMissingFiles = true;
0225         item->setForeground(0, Qt::red);
0226     }
0227 
0228     categoryItem->addChild(item);
0229     categoryItem->setExpanded(true);
0230 }
0231 
0232 QTreeWidgetItem *MultipleFileList::findCategoryOfChild(QTreeWidgetItem *childItem)
0233 {
0234     qCDebug(KSYSTEMLOG) << "Finding Category of" << childItem->text(0);
0235 
0236     for (int i = 0, total = fileList->topLevelItemCount(); i < total; ++i) {
0237         QTreeWidgetItem *item = fileList->topLevelItem(i);
0238 
0239         if (item->indexOfChild(childItem) != -1) {
0240             qCDebug(KSYSTEMLOG) << "Category of" << childItem->text(0) << "is" << item->text(0);
0241             return item;
0242         }
0243     }
0244 
0245     qCDebug(KSYSTEMLOG) << "No Category of" << childItem->text(0);
0246     return nullptr;
0247 }
0248 
0249 void MultipleFileList::modifyItem()
0250 {
0251     const QList<QTreeWidgetItem *> selectedItems = fileList->selectedItems();
0252     modifyItem(selectedItems.at(0));
0253 }
0254 
0255 void MultipleFileList::modifyItem(QTreeWidgetItem *item)
0256 {
0257     // If the user tries to modify a category item, we do nothing
0258     if (!findCategoryOfChild(item) || isEmptyItem(item)) {
0259         return;
0260     }
0261 
0262     const QString previousPath = item->text(0);
0263 
0264     // Open a standard Filedialog
0265     const QUrl url = mFileListHelper.openUrl(previousPath);
0266     if (url.isEmpty()) {
0267         return;
0268     }
0269 
0270     QList<QUrl> urls;
0271     urls.append(url);
0272     const QStringList paths = mFileListHelper.findPaths(urls);
0273 
0274     // We only take the first path
0275     if (paths.count() >= 1) {
0276         item->setText(0, paths.at(0));
0277     }
0278 
0279     Q_EMIT fileListChanged();
0280 }
0281 
0282 void MultipleFileList::removeSelectedItem()
0283 {
0284     const QList<QTreeWidgetItem *> selectedItems = fileList->selectedItems();
0285 
0286     for (QTreeWidgetItem *item : selectedItems) {
0287         QTreeWidgetItem *categoryItem = findCategoryOfChild(item);
0288         delete categoryItem->takeChild(categoryItem->indexOfChild(item));
0289     }
0290 
0291     updateEmptyItems();
0292 
0293     Q_EMIT fileListChanged();
0294 }
0295 
0296 void MultipleFileList::moveItem(int direction)
0297 {
0298     QList<QTreeWidgetItem *> const selectedItems = fileList->selectedItems();
0299 
0300     QTreeWidgetItem *item = selectedItems.at(0);
0301 
0302     QTreeWidgetItem *categoryItem = findCategoryOfChild(item);
0303     int const itemIndex = categoryItem->indexOfChild(item);
0304 
0305     categoryItem->takeChild(itemIndex);
0306 
0307     unselectAllItems();
0308 
0309     categoryItem->insertChild(itemIndex + direction, item);
0310 
0311     fileList->setCurrentItem(item);
0312     // item->setSelected(true);
0313 
0314     Q_EMIT fileListChanged();
0315 }
0316 
0317 void MultipleFileList::moveUpItem()
0318 {
0319     moveItem(-1);
0320 }
0321 
0322 void MultipleFileList::moveDownItem()
0323 {
0324     moveItem(1);
0325 }
0326 
0327 void MultipleFileList::removeAllItems()
0328 {
0329     QTreeWidgetItemIterator it(fileList, QTreeWidgetItemIterator::All);
0330     while (*it) {
0331         QTreeWidgetItem *item = *it;
0332 
0333         const QList<QTreeWidgetItem *> children = item->takeChildren();
0334         for (QTreeWidgetItem *childItem : children) {
0335             delete childItem;
0336         }
0337 
0338         ++it;
0339     }
0340 
0341     updateEmptyItems();
0342 
0343     Q_EMIT fileListChanged();
0344 }
0345 
0346 void MultipleFileList::unselectAllItems()
0347 {
0348     const QList<QTreeWidgetItem *> selectedItems = fileList->selectedItems();
0349     for (QTreeWidgetItem *item : selectedItems) {
0350         item->setSelected(false);
0351     }
0352 }
0353 
0354 void MultipleFileList::updateEmptyItems()
0355 {
0356     qCDebug(KSYSTEMLOG) << "Updating empty items...";
0357 
0358     qCDebug(KSYSTEMLOG) << "Adding empty items...";
0359 
0360     for (int i = 0, total = fileList->topLevelItemCount(); i < total; ++i) {
0361         QTreeWidgetItem *categoryItem = fileList->topLevelItem(i);
0362 
0363         // If it's a category item and it's empty
0364         if (categoryItem->childCount() == 0) {
0365             addEmptyItem(categoryItem);
0366         }
0367     }
0368 
0369     removeEmptyItems();
0370 
0371     qCDebug(KSYSTEMLOG) << "Empty items updated";
0372 }
0373 
0374 void MultipleFileList::removeEmptyItems()
0375 {
0376     qCDebug(KSYSTEMLOG) << "Removing empty items...";
0377 
0378     // Remove empty items of lists
0379     for (int categoryIndex = 0; categoryIndex < fileList->topLevelItemCount(); ++categoryIndex) {
0380         QTreeWidgetItem *categoryItem = fileList->topLevelItem(categoryIndex);
0381 
0382         qCDebug(KSYSTEMLOG) << "Removing empty items of " << categoryItem->text(0);
0383 
0384         for (int i = 0; i < categoryItem->childCount(); ++i) {
0385             QTreeWidgetItem *childItem = categoryItem->child(i);
0386 
0387             if (isEmptyItem(childItem) && categoryItem->childCount() > 1) {
0388                 qCDebug(KSYSTEMLOG) << "Remove a child item";
0389                 delete categoryItem->takeChild(i);
0390                 break;
0391             }
0392         }
0393 
0394         qCDebug(KSYSTEMLOG) << "Empty items of " << categoryItem->text(0) << "removed";
0395     }
0396 }
0397 
0398 bool MultipleFileList::isEmptyItem(QTreeWidgetItem *item) const
0399 {
0400     if (item->font(0).italic()) {
0401         return true;
0402     } else {
0403         return false;
0404     }
0405 }
0406 
0407 void MultipleFileList::addEmptyItem(QTreeWidgetItem *item)
0408 {
0409     qCDebug(KSYSTEMLOG) << "Adding an empty item...";
0410 
0411     auto emptyItem = new QTreeWidgetItem(item, QStringList(i18n("No log file...")));
0412     item->setExpanded(true);
0413     QFont font = emptyItem->font(0);
0414     font.setItalic(true);
0415     emptyItem->setFont(0, font);
0416 }
0417 
0418 void MultipleFileList::addPaths(int category, const QStringList &paths)
0419 {
0420     mMissingFiles = false;
0421     QTreeWidgetItem *categoryItem = fileList->topLevelItem(category);
0422 
0423     for (const QString &path : paths) {
0424         addItemInternal(categoryItem, path);
0425     }
0426 
0427     updateEmptyItems();
0428 
0429     updateButtons();
0430 
0431     mWarningBox->setVisible(mMissingFiles);
0432 }
0433 
0434 QStringList MultipleFileList::paths(int category)
0435 {
0436     QTreeWidgetItem *categoryItem = fileList->topLevelItem(category);
0437     QTreeWidgetItemIterator it(fileList, QTreeWidgetItemIterator::All);
0438 
0439     QStringList paths;
0440     while (*it) {
0441         QTreeWidgetItem *item = *it;
0442 
0443         if (categoryItem->indexOfChild(item) != -1) {
0444             paths.append(item->text(0));
0445         }
0446 
0447         ++it;
0448     }
0449 
0450     return paths;
0451 }
0452 
0453 #include "moc_multipleFileList.cpp"