File indexing completed on 2024-12-22 04:40:03

0001 /*
0002     SPDX-FileCopyrightText: 2007-2009 Sergio Pistone <sergio_pistone@yahoo.com.ar>
0003     SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #ifndef RANGELIST_H
0009 #define RANGELIST_H
0010 
0011 #include "core/range.h"
0012 
0013 #include <QList>
0014 #include <QStringList>
0015 
0016 #include <QDebug>
0017 
0018 namespace SubtitleComposer {
0019 class RangeList
0020 {
0021 public:
0022     typedef QList<Range>::Iterator Iterator;
0023     typedef QList<Range>::ConstIterator ConstIterator;
0024 
0025     RangeList() {}
0026 
0027     RangeList(const Range &range)
0028     {
0029         m_ranges.append(range);
0030     }
0031 
0032     RangeList(const RangeList &ranges) : m_ranges(ranges.m_ranges) {}
0033 
0034     RangeList & operator=(const RangeList &ranges)
0035     {
0036         if(this == &ranges)
0037             return *this;
0038 
0039         m_ranges = ranges.m_ranges;
0040 
0041         return *this;
0042     }
0043 
0044     bool operator==(const RangeList &ranges) const
0045     {
0046         return m_ranges == ranges.m_ranges;
0047     }
0048 
0049     bool operator!=(const RangeList &ranges) const
0050     {
0051         return m_ranges != ranges.m_ranges;
0052     }
0053 
0054     bool isFullRange(int maxIndex = Range::MaxIndex) const
0055     {
0056         return m_ranges.count() == 1 && m_ranges.first().isFullRange(maxIndex);
0057     }
0058 
0059     RangeList complement() const
0060     {
0061         qDebug() << this->inspect();
0062 
0063         if(m_ranges.empty())
0064             return Range(0, Range::MaxIndex);
0065 
0066         RangeList ret;
0067 
0068         QList<Range>::ConstIterator it = m_ranges.begin();
0069 
0070         if(it->m_start > 0)
0071             ret << Range(0, it->m_start - 1);
0072 
0073         int lastEnd = it->m_end;
0074         ++it;
0075         for(QList<Range>::ConstIterator end = m_ranges.end(); it != end; ++it) {
0076             ret << Range(lastEnd + 1, it->m_start - 1);
0077             lastEnd = it->m_end;
0078         }
0079         if(lastEnd < Range::MaxIndex)
0080             ret << Range(lastEnd + 1, Range::MaxIndex);
0081 
0082         qDebug() << ret.inspect();
0083 
0084         return ret;
0085     }
0086 
0087     bool contains(int index) const
0088     {
0089         for(QList<Range>::ConstIterator it = m_ranges.begin(), end2 = m_ranges.end(); it != end2; ++it)
0090             if(it->contains(index))
0091                 return true;
0092 
0093         return false;
0094     }
0095 
0096     Range range(int rangeIndex) const
0097     {
0098         Q_ASSERT(rangeIndex >= 0);
0099         Q_ASSERT(rangeIndex < m_ranges.count());
0100 
0101         return m_ranges.at(rangeIndex);
0102     }
0103 
0104     bool isEmpty() const
0105     {
0106         return m_ranges.isEmpty();
0107     }
0108 
0109     int rangesCount() const
0110     {
0111         return m_ranges.count();
0112     }
0113 
0114     int indexesCount() const
0115     {
0116         int count = m_ranges.count();
0117 
0118         for(QList<Range>::ConstIterator it = m_ranges.begin(), end2 = m_ranges.end(); it != end2; ++it)
0119             count += it->m_end - it->m_start;
0120 
0121         return count;
0122     }
0123 
0124     Range first() const
0125     {
0126         Q_ASSERT(!m_ranges.empty());
0127 
0128         return m_ranges.first();
0129     }
0130 
0131     Range last() const
0132     {
0133         Q_ASSERT(!m_ranges.empty());
0134 
0135         return m_ranges.last();
0136     }
0137 
0138     int firstIndex() const
0139     {
0140         Q_ASSERT(!m_ranges.empty());
0141 
0142         return m_ranges.first().start();
0143     }
0144 
0145     int lastIndex() const
0146     {
0147         Q_ASSERT(!m_ranges.empty());
0148 
0149         return m_ranges.last().end();
0150     }
0151 
0152     void clear()
0153     {
0154         m_ranges.clear();
0155     }
0156 
0157     void trimToIndex(int index)
0158     {
0159         trimToRange(Range::lower(index));
0160     }
0161 
0162     void trimToRange(const Range &range)
0163     {
0164         if(m_ranges.empty())
0165             return;
0166 
0167         // GENERAL ALGORITHM FOLLOWS
0168         // - we search the lower and the upper items affected.
0169         //   - update lower item start and end values
0170         //   - remove all items below lower up and above upper (lower and upper excluded)
0171 
0172         //       0         1         2
0173         //     XXXXX     XXXXX     XXXXX           [L, U]
0174         //  [    ]                                 [0, 0]
0175         //  []                                     [0, 4]
0176         //  [         ]                            [0, 0]
0177         //  [            ]                         [0, 1]
0178         //      [               ]                  [0, 1]
0179         //           []                            [1, 3]
0180         //           [     ]                       [1, 1]
0181         //                [     ]                  [1, 1]
0182         //  [                              ]       [0, 2]
0183         //                            [    ]       [2, 2]
0184         //                                 [  ]    [3, 2]
0185         //  [                         ]            [0, 2]
0186 
0187         const int SIZE = m_ranges.count();
0188         const int LAST_INDEX = SIZE - 1;
0189 
0190         int lowerIndex = SIZE;
0191         int upperIndex = LAST_INDEX;
0192 
0193         for(int index = 0; index <= LAST_INDEX; ++index) {
0194             Range &currentRange = m_ranges[index];
0195 
0196             if(lowerIndex == SIZE) {
0197                 if(range.m_start <= currentRange.m_end) {
0198                     lowerIndex = index;
0199                     if(range.m_end < currentRange.m_start) {
0200                         // special case, we must remove all items
0201                         upperIndex = LAST_INDEX + 1;
0202                         break;
0203                     }
0204                 }
0205             } else {
0206                 if(range.m_end < currentRange.m_start) {
0207                     upperIndex = index - 1;
0208                     break;
0209                 }
0210             }
0211         }
0212 
0213         if(lowerIndex > LAST_INDEX || upperIndex > LAST_INDEX) {
0214             m_ranges.clear();
0215         } else {
0216             Range &lowerRange = m_ranges[lowerIndex];
0217             Range &upperRange = m_ranges[upperIndex];
0218 
0219             if(range.m_start > lowerRange.m_start)
0220                 lowerRange.m_start = range.m_start;
0221             if(range.m_end < upperRange.m_end)
0222                 upperRange.m_end = range.m_end;
0223 
0224             // remove all items after upperRange
0225             for(int index = upperIndex + 1; index < SIZE; ++index)
0226                 m_ranges.removeAt(upperIndex + 1);
0227 
0228             // remove all items before lowerRange
0229             for(int index = 0; index < lowerIndex; ++index)
0230                 m_ranges.removeAt(0);
0231         }
0232     }
0233 
0234     void operator<<(const Range &range)
0235     {
0236         // first resolve the most common (and easy) cases
0237         if(m_ranges.empty()) {
0238             m_ranges.append(range);
0239             return;
0240         } else {
0241             Range &lastRange = m_ranges.last();
0242             if(lastRange.m_end < range.m_start) {           // range is higher than lastRange
0243                 if(lastRange.m_end + 1 == range.m_start) // range is absorbed by lastRange
0244                     lastRange.m_end = range.m_end;
0245                 else
0246                     m_ranges.append(range);
0247                 return;
0248             } else {
0249                 Range &firstRange = m_ranges.first();
0250                 if(firstRange.m_start > range.m_end) {          // range is lower than firstRange
0251                     if(range.m_end + 1 == firstRange.m_start) // range is absorbed by firstRange
0252                         firstRange.m_start = range.m_start;
0253                     else
0254                         m_ranges.append(range);
0255                     return;
0256                 }
0257             }
0258         }
0259 
0260         // GENERAL ALGORITHM FOLLOWS
0261         // - we search the lower and the upper items affected.
0262         //   - update lower item start and end values
0263         //   - remove all items following lower up to upper (lower excluded)
0264 
0265         //       0         1         2
0266         //     XXXXX     XXXXX     XXXXX           [L, U]
0267         //  [    ]                                 [0, 0]
0268         //  [         ]                            [0, 0]
0269         //  [            ]                         [0, 1]
0270         //  [                   ]                  [0, 1]
0271         //          [ ]                            [0, 0]
0272         //          [      ]                       [0, 1]
0273         //  [                              ]       [0, 2]
0274         //                            [    ]       [2, 2]
0275         //                                  [ ]    [2, 2] this case isn't correctly handled below but was caught before
0276         //           []                            [1, 3]
0277 
0278         const int LAST_INDEX = m_ranges.count() - 1;
0279 
0280         int lowerIndex = LAST_INDEX;
0281         int upperIndex = LAST_INDEX;
0282 
0283         for(int index = 0; index < LAST_INDEX; ++index) {
0284             Range &currentRange = m_ranges[index];
0285 
0286             if(lowerIndex == LAST_INDEX) {
0287                 if(range.m_start <= currentRange.m_end + 1) {
0288                     lowerIndex = index;
0289                     if(range.m_end < currentRange.m_start - 1) {
0290                         // special case, we must insert a new Range at lowerIndex
0291                         upperIndex = LAST_INDEX + 1;
0292                         break;
0293                     }
0294                 }
0295             } else {
0296                 if(range.m_end < currentRange.m_start - 1) {
0297                     upperIndex = index - 1;
0298                     break;
0299                 }
0300             }
0301         }
0302 
0303         if(upperIndex > LAST_INDEX) {
0304             // insert new range at lowerIndex
0305             m_ranges.insert(m_ranges.begin() + lowerIndex, range);
0306         } else {
0307             Range &lowerRange = m_ranges[lowerIndex];
0308             Range &upperRange = m_ranges[upperIndex];
0309 
0310             if(range.m_start < lowerRange.m_start)
0311                 lowerRange.m_start = range.m_start;
0312             if(range.m_end > upperRange.m_end)
0313                 lowerRange.m_end = range.m_end;
0314             else
0315                 lowerRange.m_end = upperRange.m_end;
0316 
0317             for(int index = lowerIndex + 1; index <= upperIndex; ++index)
0318                 m_ranges.removeAt(lowerIndex + 1);
0319         }
0320     }
0321 
0322     void shiftIndexesForwards(int fromIndex, int delta, bool fillSplitGap)
0323     {
0324         Q_ASSERT(fromIndex >= 0);
0325         Q_ASSERT(delta >= 0);
0326 
0327         if(!delta || m_ranges.isEmpty())
0328             return;
0329 
0330         for(int index = 0, count = m_ranges.count(); index < count; ++index) {
0331             Range &range = m_ranges[index];
0332             if(range.m_start < fromIndex && fromIndex <= range.m_end) {             // range must be filled or split to insert gap
0333                 if(fillSplitGap)
0334                     shiftRangeForwards(range, fromIndex, delta);
0335                 else {
0336                     Range range0(range.m_start, fromIndex - 1);
0337                     range.m_start = fromIndex;
0338                     shiftRangeForwards(range, fromIndex, delta);
0339                     m_ranges.insert(m_ranges.begin() + index, range0);
0340                 }
0341             } else
0342                 shiftRangeForwards(range, fromIndex, delta);
0343         }
0344     }
0345 
0346     void shiftIndexesBackwards(int fromIndex, int delta)
0347     {
0348         Q_ASSERT(fromIndex >= 0);
0349         Q_ASSERT(delta >= 0);
0350 
0351         if(!delta || m_ranges.isEmpty())
0352             return;
0353 
0354         for(int index = 0, count = m_ranges.count(); index < count; ++index) {
0355             if(!shiftRangeBackwards(m_ranges[index], fromIndex, delta)) {           // range invalidated by shift
0356                 m_ranges.removeAt(index);
0357                 index--;
0358                 count--;
0359             }
0360         }
0361     }
0362 
0363     QString inspect() const
0364     {
0365         QStringList ranges;
0366         for(int index = 0, count = m_ranges.count(); index < count; ++index)
0367             ranges += QString::asprintf("[%d,%d]", m_ranges.at(index).m_start, m_ranges.at(index).m_end);
0368         return ranges.join(", ");
0369     }
0370 
0371     inline ConstIterator begin() const
0372     {
0373         return m_ranges.begin();
0374     }
0375 
0376     inline ConstIterator end() const
0377     {
0378         return m_ranges.end();
0379     }
0380 
0381 private:
0382     inline void shiftRangeForwards(Range &range, int fromIndex, int delta)
0383     {
0384         // Q_ASSERT( delta > 0 );
0385 
0386         if(fromIndex <= range.m_start) {
0387             range.m_start += delta;
0388             range.m_end += delta;
0389         } else if(fromIndex <= range.m_end) {
0390             range.m_end += delta;
0391         }
0392         // else // if ( fromIndex > range.m_end )
0393         // {
0394         //  // nothing to do in this case
0395         // }
0396     }
0397 
0398     inline bool shiftRangeBackwards(Range &range, int fromIndex, int delta)
0399     {
0400         // Q_ASSERT( delta > 0 );
0401 
0402         if(fromIndex <= range.m_start) {
0403             range.m_start -= delta;
0404             range.m_end -= delta;
0405             if(range.m_start < fromIndex) {
0406                 if(range.m_end < fromIndex)
0407                     return false; // range invalidated
0408                 else
0409                     range.m_start = fromIndex;
0410             }
0411         } else if(fromIndex <= range.m_end) {
0412             if(fromIndex + delta > range.m_end)
0413                 range.m_end = fromIndex - 1;
0414             else
0415                 range.m_end -= delta;
0416         }
0417         // else // if ( fromIndex > range.m_end )
0418         // {
0419         //  // nothing to do in this case
0420         // }
0421 
0422         return true;
0423     }
0424 
0425     QList<Range> m_ranges;
0426 };
0427 }
0428 
0429 #endif