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 ¤tRange = 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 ¤tRange = 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