void Subtitle::splitLines(const RangeList &ranges) { beginCompositeAction(i18n("Split Lines")); for(SubtitleIterator it(*this, ranges, true); it.current(); --it) { SubtitleLine *line = it.current(); if(line->primaryLines() > 1) { line->simplifyTextWhiteSpace(SubtitleLine::Primary); long autoDurationsSum = 0; QList<int> autoDurations; SStringList primaryLines = line->primaryText().split('\n'); for(SStringList::ConstIterator ptIt = primaryLines.begin(), ptEnd = primaryLines.end(); ptIt != ptEnd; ++ptIt) { const Time &autoDuration = SubtitleLine::autoDuration((*ptIt).string(), 60, 50, 50); autoDurations.append(autoDuration.toMillis()); autoDurationsSum += autoDuration.toMillis(); } double factor = line->durationTime().toMillis() / autoDurationsSum; SStringList secondaryLines = line->secondaryText().split('\n'); while(secondaryLines.count() < primaryLines.count()) secondaryLines.append(SString()); while(secondaryLines.count() > primaryLines.count()) { SString lastLine = secondaryLines.last(); secondaryLines.pop_back(); secondaryLines.last() += '\n'; secondaryLines.last() += lastLine; } int subLineIndex = it.index(), splitLineIndex = 0; for(SStringList::ConstIterator ptIt = primaryLines.begin(), ptEnd = secondaryLines.end(), stIt = secondaryLines.begin(), stEnd = secondaryLines.end(); ptIt != ptEnd && stIt != stEnd; ++ptIt, ++stIt, ++subLineIndex, ++splitLineIndex) { if(splitLineIndex) { SubtitleLine *newLine = new SubtitleLine(); newLine->setShowTime(line->hideTime() + 1); insertLine(newLine, subLineIndex); line = newLine; } line->setTexts(*ptIt, *stIt); line->setDurationTime(Time((factor * autoDurations[splitLineIndex]) - 1.0)); } } } endCompositeAction(); }
void Subtitle::setPrimaryData(const Subtitle &from, bool usePrimaryData) { beginCompositeAction(i18n("Set Primary Data")); setFormatData(from.m_formatData); setFramesPerSecond(from.framesPerSecond()); SubtitleIterator fromIt(from, Range::full()); SubtitleIterator thisIt(*this, Range::full()); // the errors that we are going to take from 'from': const int fromErrors = (usePrimaryData ? SubtitleLine::PrimaryOnlyErrors : SubtitleLine::SecondaryOnlyErrors) | SubtitleLine::SharedErrors; // the errors that we are going to keep: const int thisErrors = SubtitleLine::SecondaryOnlyErrors; for(SubtitleLine *fromLine = fromIt.current(), *thisLine = thisIt.current(); fromLine && thisLine; ++fromIt, ++thisIt, fromLine = fromIt.current(), thisLine = thisIt.current()) { thisLine->setPrimaryText(usePrimaryData ? fromLine->primaryText() : fromLine->secondaryText()); thisLine->setTimes(fromLine->showTime(), fromLine->hideTime()); thisLine->setErrorFlags((fromLine->errorFlags() & fromErrors) | (thisLine->errorFlags() & thisErrors)); thisLine->setFormatData(fromLine->formatData()); } if(fromIt.current()) { // 'from' had more lines than '*this' QList<SubtitleLine *> lines; for(; fromIt.current(); ++fromIt) { SubtitleLine *thisLine = new SubtitleLine(*fromIt.current()); if(!usePrimaryData) thisLine->setPrimaryText(thisLine->secondaryText()); thisLine->setSecondaryText(SString()); thisLine->setErrorFlags(SubtitleLine::SecondaryOnlyErrors, false); thisLine->setFormatData(fromIt.current()->formatData()); lines.append(thisLine); } InsertLinesAction a(*this, lines); processAction(&a); } else if(thisIt.current()) { // '*this' had more lines than 'from' for(SubtitleLine *thisLine = thisIt.current(); thisLine; ++thisIt, thisLine = thisIt.current()) { thisLine->setPrimaryText(SString()); thisLine->setErrorFlags(SubtitleLine::PrimaryOnlyErrors, false); thisLine->setFormatData(0); } } endCompositeAction(); }
void Subtitle::joinLines(const RangeList &ranges) { beginCompositeAction(i18n("Join Lines")); RangeList obsoletedRanges; for(RangeList::ConstIterator rangesIt = ranges.begin(), end = ranges.end(); rangesIt != end; ++rangesIt) { int rangeStart = (*rangesIt).start(); int rangeEnd = normalizeRangeIndex((*rangesIt).end()); if(rangeStart >= rangeEnd) continue; SubtitleLine *firstLine = m_lines.at(rangeStart); SubtitleLine *lastLine = m_lines.at(rangeEnd); SString primaryText, secondaryText; for(SubtitleIterator it(*this, Range(rangeStart, rangeEnd - 1)); it.current(); ++it) { if(!it.current()->primaryText().isEmpty()) { primaryText.append(it.current()->primaryText()); primaryText.append("\n"); } if(!it.current()->secondaryText().isEmpty()) { secondaryText.append(it.current()->secondaryText()); secondaryText.append("\n"); } } primaryText.append(lastLine->primaryText()); secondaryText.append(lastLine->secondaryText()); firstLine->setTexts(primaryText, secondaryText); firstLine->setHideTime(lastLine->hideTime()); obsoletedRanges << Range(rangeStart + 1, rangeEnd); } removeLines(obsoletedRanges, Both); endCompositeAction(); }
void Subtitle::removeLines(const RangeList &r, TextTarget target) { if(m_lines.isEmpty()) return; RangeList ranges = r; ranges.trimToIndex(m_lines.count() - 1); if(ranges.isEmpty()) return; if(target == Both) { beginCompositeAction(i18n("Remove Lines")); RangeList::ConstIterator rangesIt = ranges.end(), begin = ranges.begin(); do { rangesIt--; processAction(new RemoveLinesAction(*this, (*rangesIt).start(), (*rangesIt).end())); } while(rangesIt != begin); endCompositeAction(); } else if(target == Secondary) { beginCompositeAction(i18n("Remove Lines")); RangeList rangesComplement = ranges.complement(); rangesComplement.trimToRange(Range(ranges.firstIndex(), m_lines.count() - 1)); // we have to move the secondary texts up (we do it in chunks) SubtitleIterator srcIt(*this, rangesComplement); SubtitleIterator dstIt(*this, Range::upper(ranges.firstIndex())); for(; srcIt.current() && dstIt.current(); ++srcIt, ++dstIt) dstIt.current()->setSecondaryText(srcIt.current()->secondaryText()); // the remaining lines secondary text must be cleared for(; dstIt.current(); ++dstIt) dstIt.current()->setSecondaryText(SString()); endCompositeAction(); } else { // if target == Primary beginCompositeAction(i18n("Remove Lines"), true, false); RangeList mutableRanges(ranges); mutableRanges.trimToIndex(m_lines.count() - 1); // first, we need to append as many empty lines as we're to remove // we insert them with a greater time than the one of the last (non deleted) line int linesCount = m_lines.count(); Range lastRange = mutableRanges.last(); int lastIndex = lastRange.end() == linesCount - 1 ? lastRange.start() - 1 : linesCount - 1; SubtitleLine *lastLine = lastIndex < linesCount ? m_lines.at(lastIndex) : 0; Time showTime(lastLine ? lastLine->hideTime() + 100 : 0); Time hideTime(showTime + 1000); QList<SubtitleLine *> lines; for(int index = 0, size = ranges.indexesCount(); index < size; ++index) { lines.append(new SubtitleLine(SString(), SString(), showTime, hideTime)); showTime.shift(1100); hideTime.shift(1100); } processAction(new InsertLinesAction(*this, lines)); // then, we move the secondary texts down (we need to iterate from bottom to top for that) RangeList rangesComplement = mutableRanges.complement(); SubtitleIterator srcIt(*this, Range(ranges.firstIndex(), m_lines.count() - lines.count() - 1), true); SubtitleIterator dstIt(*this, rangesComplement, true); for(; srcIt.current() && dstIt.current(); --srcIt, --dstIt) dstIt.current()->setSecondaryText(srcIt.current()->secondaryText()); // finally, we can remove the specified lines RangeList::ConstIterator rangesIt = ranges.end(), begin = ranges.begin(); do { rangesIt--; processAction(new RemoveLinesAction(*this, (*rangesIt).start(), (*rangesIt).end())); } while(rangesIt != begin); endCompositeAction(); } }
SubtitleLine * Subtitle::insertNewLine(int index, bool timeAfter, TextTarget target) { Q_ASSERT(index <= m_lines.count()); if(index < 0) index = m_lines.count(); SubtitleLine *newLine = new SubtitleLine(); int newLineIndex = (target == Secondary) ? m_lines.count() : index; if(timeAfter) { if(newLineIndex) { // there is a previous line SubtitleLine *prevLine = m_lines.value(newLineIndex - 1); newLine->setTimes(prevLine->hideTime() + 100, prevLine->hideTime() + 1000); } else if(newLineIndex < m_lines.count()) { // there is a next line SubtitleLine *nextLine = m_lines.value(newLineIndex); newLine->setTimes(nextLine->showTime() - 1100, nextLine->showTime() - 100); } else newLine->setHideTime(1000); } else { // ! timeAfter if(newLineIndex < m_lines.count()) { // there is a next line SubtitleLine *nextLine = m_lines.at(newLineIndex); newLine->setTimes(nextLine->showTime() - 1100, nextLine->showTime() - 100); } else if(newLineIndex) { // there is a previous line SubtitleLine *prevLine = m_lines.at(newLineIndex - 1); newLine->setTimes(prevLine->hideTime() + 100, prevLine->hideTime() + 1000); } else newLine->setHideTime(1000); } if(target == Both || index == m_lines.count()) { insertLine(newLine, newLineIndex); } else if(target == Primary) { beginCompositeAction(i18n("Insert Line")); insertLine(newLine, newLineIndex); SubtitleLine *line = newLine; SubtitleIterator it(*this, Range::full(), false); for(it.toIndex(newLineIndex + 1); it.current(); ++it) { line->setSecondaryText(it.current()->secondaryText()); line = it.current(); } line->setSecondaryText(SString()); endCompositeAction(); } else if(target == Secondary) { beginCompositeAction(i18n("Insert Line")); insertLine(newLine, newLineIndex); SubtitleIterator it(*this, Range::full(), true); SubtitleLine *line = it.current(); for(--it; it.index() >= index; --it) { line->setSecondaryText(it.current()->secondaryText()); line = it.current(); } line->setSecondaryText(SString()); newLine = line; endCompositeAction(); } return newLine; }
void Subtitle::splitLines(const RangeList &ranges) { beginCompositeAction(i18n("Split Lines")); for(SubtitleIterator it(*this, ranges, true); it.current(); --it) { SubtitleLine *line = it.current(); line->simplifyTextWhiteSpace(SubtitleLine::Primary); SString primaryText = line->primaryText(); if(primaryText.isEmpty()) continue; if(line->primaryLines() < 2) { if(primaryText.count(QChar::Space) == 0) continue; int len = primaryText.length(); int i = len / 2; int j = i + len % 2; for(; ; i--, j++) { if(primaryText[i] == QChar::Space) { primaryText[i] = QChar::LineFeed; break; } Q_ASSERT(j <= len); if(primaryText[j] == QChar::Space) { primaryText[j] = QChar::LineFeed; break; } if(i == 0) { primaryText.append(QChar::LineFeed); break; } } } double autoDurationsSum = 0; QList<double> autoDurations; SStringList primaryLines = primaryText.split('\n'); for(SStringList::ConstIterator ptIt = primaryLines.begin(), ptEnd = primaryLines.end(); ptIt != ptEnd; ++ptIt) { const Time &autoDuration = SubtitleLine::autoDuration(ptIt->string(), 60, 50, 50); autoDurations.append(autoDuration.toMillis()); autoDurationsSum += autoDuration.toMillis(); } double factor = (line->durationTime().toMillis() + 1.) / autoDurationsSum; SStringList secondaryLines = line->secondaryText().split('\n'); while(secondaryLines.count() < primaryLines.count()) secondaryLines.append(SString()); while(secondaryLines.count() > primaryLines.count()) { SString lastLine = secondaryLines.last(); secondaryLines.pop_back(); secondaryLines.last() += '\n'; secondaryLines.last() += lastLine; } int subLineIndex = it.index(), splitLineIndex = 0; for(SStringList::ConstIterator ptIt = primaryLines.begin(), ptEnd = secondaryLines.end(), stIt = secondaryLines.begin(), stEnd = secondaryLines.end(); ptIt != ptEnd && stIt != stEnd; ++ptIt, ++stIt, ++subLineIndex, ++splitLineIndex) { if(splitLineIndex) { SubtitleLine *newLine = new SubtitleLine(); newLine->setShowTime(line->hideTime() + 1); insertLine(newLine, subLineIndex); line = newLine; } line->setTexts(*ptIt, *stIt); line->setDurationTime(Time((factor * autoDurations[splitLineIndex]) - 1.0)); } } endCompositeAction(); }