File indexing completed on 2024-05-12 05:13:14

0001 /*
0002   SPDX-FileCopyrightText: 2005 Rafal Rzepecki <divide@users.sourceforge.net>
0003 
0004   SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "categoryhierarchyreader.h"
0008 
0009 #include <QComboBox>
0010 
0011 using namespace CalendarSupport;
0012 
0013 namespace CategoryConfig
0014 {
0015 static const QLatin1StringView categorySeparator(":");
0016 }
0017 
0018 inline QString &quote(QString &string)
0019 {
0020     Q_ASSERT(CategoryConfig::categorySeparator != QLatin1StringView("@"));
0021     return string.replace(QLatin1Char('@'), QStringLiteral("@0")).replace(QLatin1Char('\\') + CategoryConfig::categorySeparator, QStringLiteral("@1"));
0022 }
0023 
0024 inline QStringList &unquote(QStringList &strings)
0025 {
0026     return strings.replaceInStrings(QStringLiteral("@1"), CategoryConfig::categorySeparator).replaceInStrings(QStringLiteral("@0"), QStringLiteral("@"));
0027 }
0028 
0029 QStringList CategoryHierarchyReader::path(QString string)
0030 {
0031     QStringList _path = quote(string).split(CategoryConfig::categorySeparator, Qt::SkipEmptyParts);
0032     return unquote(_path);
0033 }
0034 
0035 void CategoryHierarchyReader::read(const QStringList &categories)
0036 {
0037     clear();
0038 
0039     // case insensitive sort
0040     QMap<QString, QString> sortedCategories;
0041     for (const QString &str : categories) {
0042         sortedCategories.insert(str.toLower(), str);
0043     }
0044 
0045     QStringList last_path;
0046     for (const QString &category : std::as_const(sortedCategories)) {
0047         QStringList _path = path(category);
0048 
0049         // we need to figure out where last item and the new one differ
0050         QStringList::Iterator jt;
0051         QStringList::Iterator kt;
0052         int split_level = 0;
0053         QStringList new_path = _path; // save it for later
0054         for (jt = _path.begin(), kt = last_path.begin(); jt != _path.end() && kt != last_path.end(); ++jt, ++kt) {
0055             if (*jt == *kt) {
0056                 split_level++;
0057             } else {
0058                 break; // now we have first non_equal component in the iterators
0059             }
0060         }
0061 
0062         // make a path relative to the shared ancestor
0063         if (jt != _path.begin()) {
0064             _path.erase(_path.begin(), jt);
0065         }
0066         last_path = new_path;
0067 
0068         if (_path.isEmpty()) {
0069             // something is wrong, we already have this node
0070             continue;
0071         }
0072 
0073         // find that ancestor
0074         while (split_level < depth()) {
0075             goUp();
0076         }
0077         Q_ASSERT(split_level == depth());
0078 
0079         // make the node and any non-existent ancestors
0080         while (!_path.isEmpty()) {
0081             addChild(_path.first(), QVariant(category));
0082             _path.pop_front();
0083         }
0084     }
0085 }
0086 
0087 void CategoryHierarchyReaderQComboBox::clear()
0088 {
0089     mBox->clear();
0090 }
0091 
0092 void CategoryHierarchyReaderQComboBox::goUp()
0093 {
0094     mCurrentDepth--;
0095 }
0096 
0097 void CategoryHierarchyReaderQComboBox::addChild(const QString &label, const QVariant &userData)
0098 {
0099     QString spaces;
0100     spaces.fill(QLatin1Char(' '), 2 * mCurrentDepth);
0101     mBox->addItem(spaces + label, userData);
0102     mCurrentDepth++;
0103 }
0104 
0105 int CategoryHierarchyReaderQComboBox::depth() const
0106 {
0107     return mCurrentDepth;
0108 }