File indexing completed on 2024-05-12 04:57:16
0001 /* 0002 SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "application.h" 0008 0009 #include "appglobal.h" 0010 #include "actions/kcodecactionext.h" 0011 #include "actions/krecentfilesactionext.h" 0012 #include "actions/useractionnames.h" 0013 #include "core/undo/undostack.h" 0014 #include "dialogs/joinsubtitlesdialog.h" 0015 #include "dialogs/splitsubtitledialog.h" 0016 #include "dialogs/syncsubtitlesdialog.h" 0017 #include "formats/inputformat.h" 0018 #include "formats/formatmanager.h" 0019 #include "formats/textdemux/textdemux.h" 0020 #include "formats/outputformat.h" 0021 #include "helpers/commondefs.h" 0022 #include "helpers/common.h" 0023 #include "gui/treeview/lineswidget.h" 0024 #include "speechprocessor/speechprocessor.h" 0025 #include "videoplayer/videoplayer.h" 0026 0027 #include <QFileDialog> 0028 #include <QLabel> 0029 #include <QProcess> 0030 #include <QStringBuilder> 0031 #include <QTextCodec> 0032 0033 #include <KCodecAction> 0034 #include <KLocalizedString> 0035 #include <KMessageBox> 0036 #include <kwidgetsaddons_version.h> 0037 0038 0039 #undef ERROR 0040 0041 using namespace SubtitleComposer; 0042 0043 static const QStringList videoExtensionList = { 0044 $("avi"), $("divx"), $("flv"), $("m2ts"), $("mkv"), $("mov"), $("mp4"), $("mpeg"), 0045 $("mpg"), $("ogm"), $("ogv"), $("rmvb"), $("ts"), $("vob"), $("webm"), $("wmv"), 0046 }; 0047 0048 static const QStringList audioExtensionList = { 0049 $("aac"), $("ac3"), $("ape"), $("flac"), $("la"), $("m4a"), $("mac"), $("mp+"), 0050 $("mp2"), $("mp3"), $("mp4"), $("mpc"), $("mpp"), $("ofr"), $("oga"), $("ogg"), 0051 $("pac"), $("ra"), $("spx"), $("tta"), $("wav"), $("wma"), $("wv"), 0052 }; 0053 0054 const QString & 0055 Application::buildSubtitleFilesFilter(bool openFileFilter) 0056 { 0057 static QString filterSave; 0058 static QString filterOpen; 0059 0060 if(filterSave.isEmpty()) { 0061 QString textExtensions; 0062 QString imageExtensions; 0063 const QStringList formats = FormatManager::instance().inputNames(); 0064 for(const QString &fmt : formats) { 0065 const InputFormat *format = FormatManager::instance().input(fmt); 0066 QString extensions; 0067 for(const QString &ext : format->extensions()) 0068 extensions += $(" *.") % ext; 0069 const QString formatLine = format->dialogFilter() % QChar::LineFeed; 0070 filterOpen += formatLine; 0071 if(format->isBinary()) { 0072 imageExtensions += extensions; 0073 } else { 0074 textExtensions += extensions; 0075 filterSave += formatLine; 0076 } 0077 } 0078 filterOpen = i18n("All Text Subtitles") % $(" (") % QStringView(textExtensions).mid(1) % $(")\n") 0079 % i18n("All Image Subtitles") % $(" (") % QStringView(imageExtensions).mid(1) % $(")\n") 0080 % filterSave 0081 % i18n("All Files") % $(" (*)"); 0082 filterSave.truncate(filterSave.size() - 1); 0083 } 0084 0085 return openFileFilter ? filterOpen : filterSave; 0086 } 0087 0088 const QString & 0089 Application::buildMediaFilesFilter() 0090 { 0091 static QString filter; 0092 0093 if(filter.isEmpty()) { 0094 QString videoExtensions; 0095 foreach(const QString ext, videoExtensionList) 0096 videoExtensions += $(" *.") % ext; 0097 0098 QString audioExtensions; 0099 foreach(const QString ext, audioExtensionList) 0100 audioExtensions += $(" *.") % ext; 0101 0102 filter = i18n("Media Files") % $(" (") % QStringView(videoExtensions).mid(1) % audioExtensions % $(")\n") 0103 % i18n("Video Files") % $(" (") % QStringView(videoExtensions).mid(1) % $(")\n") 0104 % i18n("Audio Files") % $(" (") % QStringView(audioExtensions).mid(1) % $(")\n") 0105 % i18n("All Files") % $(" (*)"); 0106 } 0107 0108 return filter; 0109 } 0110 0111 QTextCodec * 0112 Application::codecForEncoding(const QString &encoding) 0113 { 0114 if(encoding.isEmpty()) 0115 return nullptr; 0116 return QTextCodec::codecForName(encoding.toUtf8()); 0117 } 0118 0119 bool 0120 Application::acceptClashingUrls(const QUrl &subtitleUrl, const QUrl &subtitleTrUrl) 0121 { 0122 if(subtitleUrl != subtitleTrUrl || subtitleUrl.isEmpty() || subtitleTrUrl.isEmpty()) 0123 return true; 0124 0125 return KMessageBox::Continue == KMessageBox::warningContinueCancel( 0126 m_mainWindow, 0127 i18n("The requested action would make the subtitle and its translation share the same file, " 0128 "possibly resulting in loss of information when saving.\n" 0129 "Are you sure you want to continue?"), 0130 i18n("Conflicting Subtitle Files")); 0131 } 0132 0133 void 0134 Application::newSubtitle() 0135 { 0136 if(!closeSubtitle()) 0137 return; 0138 0139 AppGlobal::subtitle = new Subtitle(); 0140 m_subtitleUrl.clear(); 0141 0142 processSubtitleOpened(nullptr, QString()); 0143 } 0144 0145 void 0146 Application::openSubtitle() 0147 { 0148 QFileDialog openDlg(m_mainWindow, i18n("Open Subtitle"), QString(), buildSubtitleFilesFilter()); 0149 0150 openDlg.setModal(true); 0151 openDlg.selectUrl(m_lastSubtitleUrl); 0152 0153 if(openDlg.exec() == QDialog::Accepted) 0154 openSubtitle(openDlg.selectedUrls().constFirst()); 0155 } 0156 0157 void 0158 Application::openSubtitle(const QUrl &url, bool warnClashingUrls) 0159 { 0160 m_lastSubtitleUrl = url; 0161 0162 if(warnClashingUrls && !acceptClashingUrls(url, m_subtitleTrUrl)) 0163 return; 0164 0165 if(!closeSubtitle()) 0166 return; 0167 0168 QTextCodec *codec = codecForEncoding(KRecentFilesActionExt::encodingForUrl(url)); 0169 0170 AppGlobal::subtitle = new Subtitle(); 0171 FormatManager::Status res = FormatManager::instance().readSubtitle(*appSubtitle(), true, url, &codec, &m_subtitleFormat); 0172 if(res == FormatManager::SUCCESS) { 0173 m_subtitleUrl = url; 0174 processSubtitleOpened(codec, m_subtitleFormat); 0175 0176 if(m_subtitleUrl.isLocalFile() && SCConfig::automaticVideoLoad()) { 0177 QFileInfo subtitleFileInfo(m_subtitleUrl.toLocalFile()); 0178 0179 QString subtitleFileName = m_subtitleFileName.toLower(); 0180 QString videoFileName = QFileInfo(VideoPlayer::instance()->filePath()).completeBaseName().toLower(); 0181 0182 if(videoFileName.isEmpty() || subtitleFileName.indexOf(videoFileName) != 0) { 0183 QStringList subtitleDirFiles = subtitleFileInfo.dir().entryList(QDir::Files | QDir::Readable); 0184 for(QStringList::ConstIterator it = subtitleDirFiles.constBegin(), end = subtitleDirFiles.constEnd(); it != end; ++it) { 0185 QFileInfo fileInfo(*it); 0186 if(videoExtensionList.contains(fileInfo.suffix().toLower())) { 0187 if(subtitleFileName.indexOf(fileInfo.completeBaseName().toLower()) == 0) { 0188 QUrl auxUrl; 0189 auxUrl.setScheme($("file")); 0190 auxUrl.setPath(subtitleFileInfo.dir().filePath(*it)); 0191 openVideo(auxUrl); 0192 break; 0193 } 0194 } 0195 } 0196 } 0197 } 0198 } else { 0199 AppGlobal::subtitle.reset(); 0200 0201 if(res == FormatManager::ERROR) { 0202 KMessageBox::error( 0203 m_mainWindow, 0204 i18n("<qt>Could not parse the subtitle file.<br/>" 0205 "This may have been caused by usage of the wrong encoding.</qt>")); 0206 } 0207 } 0208 } 0209 0210 void 0211 Application::reopenSubtitleWithCodec(QTextCodec *codec) 0212 { 0213 if(m_subtitleUrl.isEmpty()) 0214 return; 0215 0216 Subtitle *subtitle = new Subtitle(); 0217 QString subtitleFormat; 0218 0219 FormatManager::Status res = FormatManager::instance().readSubtitle(*subtitle, true, m_subtitleUrl, &codec, &subtitleFormat); 0220 if(res != FormatManager::SUCCESS) { 0221 if(res == FormatManager::ERROR) { 0222 KMessageBox::error( 0223 m_mainWindow, 0224 i18n("<qt>Could not parse the subtitle file.<br/>" 0225 "This may have been caused by usage of the wrong encoding.</qt>")); 0226 } 0227 delete subtitle; 0228 return; 0229 } 0230 0231 emit subtitleClosed(); 0232 0233 if(m_translationMode) 0234 subtitle->setSecondaryData(*appSubtitle(), false); 0235 0236 AppGlobal::subtitle = subtitle; 0237 0238 processSubtitleOpened(codec, subtitleFormat); 0239 } 0240 0241 void 0242 Application::processSubtitleOpened(QTextCodec *codec, const QString &subtitleFormat) 0243 { 0244 // The loading of the subtitle shouldn't be an undoable action as there's no state before it 0245 appUndoStack()->clear(); 0246 appSubtitle()->clearPrimaryDirty(); 0247 appSubtitle()->clearSecondaryDirty(); 0248 0249 emit subtitleOpened(appSubtitle()); 0250 0251 m_subtitleFormat = subtitleFormat; 0252 0253 if(m_subtitleUrl.isEmpty()) { 0254 m_subtitleFileName.clear(); 0255 m_subtitleEncoding = SCConfig::defaultSubtitlesEncoding().toUtf8(); 0256 codec = QTextCodec::codecForName(m_subtitleEncoding.toUtf8()); 0257 } else { 0258 m_subtitleFileName = QFileInfo(m_subtitleUrl.path()).fileName(); 0259 Q_ASSERT(codec != nullptr); 0260 m_subtitleEncoding = codec->name(); 0261 m_recentSubtitlesAction->addUrl(m_subtitleUrl, m_subtitleEncoding); 0262 } 0263 m_reopenSubtitleAsAction->setCurrentCodec(codec); 0264 m_saveSubtitleAsAction->setCurrentCodec(codec); 0265 0266 connect(appSubtitle(), &Subtitle::primaryDirtyStateChanged, this, &Application::updateTitle); 0267 connect(appSubtitle(), &Subtitle::secondaryDirtyStateChanged, this, &Application::updateTitle); 0268 updateTitle(); 0269 0270 m_labSubFormat->setText(i18n("Format: %1", m_subtitleFormat)); 0271 m_labSubEncoding->setText(i18n("Encoding: %1", m_subtitleEncoding)); 0272 } 0273 0274 void 0275 Application::demuxTextStream(int textStreamIndex) 0276 { 0277 if(!closeSubtitle()) 0278 return; 0279 0280 newSubtitle(); 0281 0282 m_textDemux->demuxFile(appSubtitle(), VideoPlayer::instance()->filePath(), textStreamIndex); 0283 } 0284 0285 void 0286 Application::speechImportAudioStream(int audioStreamIndex) 0287 { 0288 if(!closeSubtitle()) 0289 return; 0290 0291 newSubtitle(); 0292 0293 m_speechProcessor->setSubtitle(appSubtitle()); 0294 m_speechProcessor->setAudioStream(VideoPlayer::instance()->filePath(), audioStreamIndex); 0295 } 0296 0297 bool 0298 Application::saveSubtitle(QTextCodec *codec) 0299 { 0300 if(m_subtitleUrl.isEmpty() || !FormatManager::instance().hasOutput(m_subtitleFormat)) 0301 return saveSubtitleAs(codec); 0302 0303 if(!codec) 0304 codec = QTextCodec::codecForName(m_subtitleEncoding.toUtf8()); 0305 if(!codec) 0306 codec = QTextCodec::codecForName(SCConfig::defaultSubtitlesEncoding().toUtf8()); 0307 if(!codec) 0308 codec = QTextCodec::codecForLocale(); 0309 0310 if(FormatManager::instance().writeSubtitle(*appSubtitle(), true, m_subtitleUrl, codec, m_subtitleFormat, true)) { 0311 appSubtitle()->clearPrimaryDirty(); 0312 0313 m_reopenSubtitleAsAction->setCurrentCodec(codec); 0314 m_saveSubtitleAsAction->setCurrentCodec(codec); 0315 m_recentSubtitlesAction->addUrl(m_subtitleUrl, codec->name()); 0316 m_subtitleEncoding = codec->name(); 0317 m_labSubFormat->setText(i18n("Format: %1", m_subtitleFormat)); 0318 m_labSubEncoding->setText(i18n("Encoding: %1", m_subtitleEncoding)); 0319 0320 updateTitle(); 0321 0322 return true; 0323 } else { 0324 KMessageBox::error(m_mainWindow, i18n("There was an error saving the subtitle.")); 0325 return false; 0326 } 0327 } 0328 0329 bool 0330 Application::saveSubtitleAs(QTextCodec *codec) 0331 { 0332 QFileDialog saveDlg(m_mainWindow, i18n("Save Subtitle"), QString(), buildSubtitleFilesFilter(false)); 0333 0334 saveDlg.setModal(true); 0335 saveDlg.setAcceptMode(QFileDialog::AcceptSave); 0336 saveDlg.setDirectory(QFileInfo(m_lastSubtitleUrl.toLocalFile()).absoluteDir()); 0337 saveDlg.selectNameFilter(FormatManager::instance().defaultOutput()->dialogFilter()); 0338 if(!m_subtitleUrl.isEmpty()) { 0339 saveDlg.selectUrl(m_subtitleUrl); 0340 const OutputFormat *fmt = FormatManager::instance().output(m_subtitleFormat); 0341 if(fmt) 0342 saveDlg.selectNameFilter(fmt->dialogFilter()); 0343 } 0344 0345 if(saveDlg.exec() == QDialog::Accepted) { 0346 const QUrl url = saveDlg.selectedUrls().constFirst(); 0347 if(!acceptClashingUrls(url, m_subtitleTrUrl)) 0348 return false; 0349 0350 m_subtitleUrl = url; 0351 m_subtitleFileName = QFileInfo(m_subtitleUrl.path()).fileName(); 0352 m_subtitleFormat = saveDlg.selectedNameFilter(); 0353 if(!m_subtitleFormat.isEmpty()) 0354 m_subtitleFormat.truncate(m_subtitleFormat.indexOf(QStringLiteral(" ("))); 0355 return saveSubtitle(codec); 0356 } 0357 0358 return false; 0359 } 0360 0361 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0) 0362 #define warningTwoActionsCancel warningYesNoCancel 0363 #define PrimaryAction Yes 0364 #endif 0365 0366 bool 0367 Application::closeSubtitle() 0368 { 0369 if(appSubtitle()) { 0370 if(m_translationMode && appSubtitle()->isSecondaryDirty()) { 0371 KMessageBox::ButtonCode result = KMessageBox::warningTwoActionsCancel(nullptr, 0372 i18n("Currently opened translation subtitle has unsaved changes.\nDo you want to save them?"), 0373 i18n("Close Translation Subtitle") + " - SubtitleComposer", 0374 KStandardGuiItem::save(), KStandardGuiItem::dontSave()); 0375 if(result == KMessageBox::Cancel) 0376 return false; 0377 else if(result == KMessageBox::PrimaryAction) 0378 if(!saveSubtitleTr()) 0379 return false; 0380 } 0381 0382 if(appSubtitle()->isPrimaryDirty()) { 0383 KMessageBox::ButtonCode result = KMessageBox::warningTwoActionsCancel(nullptr, 0384 i18n("Currently opened subtitle has unsaved changes.\nDo you want to save them?"), 0385 i18n("Close Subtitle") + " - SubtitleComposer", 0386 KStandardGuiItem::save(), KStandardGuiItem::dontSave()); 0387 if(result == KMessageBox::Cancel) 0388 return false; 0389 else if(result == KMessageBox::PrimaryAction) 0390 if(!saveSubtitle()) 0391 return false; 0392 } 0393 0394 if(m_translationMode) { 0395 m_translationMode = false; 0396 emit translationModeChanged(false); 0397 } 0398 0399 disconnect(appSubtitle(), &Subtitle::primaryDirtyStateChanged, this, &Application::updateTitle); 0400 disconnect(appSubtitle(), &Subtitle::secondaryDirtyStateChanged, this, &Application::updateTitle); 0401 0402 emit subtitleClosed(); 0403 0404 appUndoStack()->clear(); 0405 0406 AppGlobal::subtitle.reset(); 0407 0408 m_labSubFormat->setText(QString()); 0409 m_labSubEncoding->setText(QString()); 0410 0411 m_subtitleUrl.clear(); 0412 m_subtitleFileName.clear(); 0413 m_subtitleEncoding.clear(); 0414 m_subtitleFormat.clear(); 0415 0416 m_subtitleTrUrl.clear(); 0417 m_subtitleTrFileName.clear(); 0418 m_subtitleTrEncoding.clear(); 0419 m_subtitleTrFormat.clear(); 0420 0421 updateTitle(); 0422 } 0423 0424 return true; 0425 } 0426 0427 void 0428 Application::newSubtitleTr() 0429 { 0430 if(!closeSubtitleTr()) 0431 return; 0432 0433 m_translationMode = true; 0434 emit translationModeChanged(true); 0435 0436 updateTitle(); 0437 } 0438 0439 void 0440 Application::openSubtitleTr() 0441 { 0442 if(!appSubtitle()) 0443 return; 0444 0445 QFileDialog openDlg(m_mainWindow, i18n("Open Translation Subtitle"), QString(), buildSubtitleFilesFilter()); 0446 0447 openDlg.setModal(true); 0448 openDlg.selectUrl(m_lastSubtitleUrl); 0449 0450 if(openDlg.exec() == QDialog::Accepted) 0451 openSubtitleTr(openDlg.selectedUrls().constFirst()); 0452 } 0453 0454 void 0455 Application::openSubtitleTr(const QUrl &url, bool warnClashingUrls) 0456 { 0457 m_lastSubtitleUrl = url; 0458 0459 if(!appSubtitle()) 0460 return; 0461 0462 if(warnClashingUrls && !acceptClashingUrls(m_subtitleUrl, url)) 0463 return; 0464 0465 if(!closeSubtitleTr()) 0466 return; 0467 0468 QExplicitlySharedDataPointer<Subtitle> subtitleTr(new Subtitle()); 0469 QTextCodec *codec = codecForEncoding(KRecentFilesActionExt::encodingForUrl(url)); 0470 0471 FormatManager::Status res = FormatManager::instance().readSubtitle(*subtitleTr, false, url, &codec, &m_subtitleTrFormat); 0472 if(res != FormatManager::SUCCESS) { 0473 if(res == FormatManager::ERROR) { 0474 KMessageBox::error( 0475 m_mainWindow, 0476 i18n("<qt>Could not parse the subtitle file.<br/>" 0477 "This may have been caused by usage of the wrong encoding.</qt>")); 0478 } 0479 return; 0480 } 0481 0482 m_subtitleTrUrl = url; 0483 appSubtitle()->setSecondaryData(*subtitleTr, false); 0484 processTranslationOpened(codec, m_subtitleTrFormat); 0485 } 0486 0487 void 0488 Application::reopenSubtitleTrWithCodec(QTextCodec *codec) 0489 { 0490 if(m_subtitleTrUrl.isEmpty()) 0491 return; 0492 0493 QExplicitlySharedDataPointer<Subtitle> subtitleTr(new Subtitle()); 0494 QString subtitleTrFormat; 0495 0496 FormatManager::Status res = FormatManager::instance().readSubtitle(*subtitleTr, false, m_subtitleTrUrl, &codec, &subtitleTrFormat); 0497 if(res != FormatManager::SUCCESS) { 0498 if(res == FormatManager::ERROR) { 0499 KMessageBox::error( 0500 m_mainWindow, 0501 i18n("<qt>Could not parse the subtitle file.<br/>" 0502 "This may have been caused by usage of the wrong encoding.</qt>")); 0503 } 0504 return; 0505 } 0506 0507 appSubtitle()->setSecondaryData(*subtitleTr, false); 0508 processTranslationOpened(codec, m_subtitleTrFormat); 0509 } 0510 0511 void 0512 Application::processTranslationOpened(QTextCodec *codec, const QString &subtitleFormat) 0513 { 0514 // We don't clear the primary dirty state because the loading of the translation 0515 // only changes it when actually needed (i.e., when the translation had more lines) 0516 appSubtitle()->clearSecondaryDirty(); 0517 0518 m_subtitleFormat = subtitleFormat; 0519 0520 if(!m_subtitleTrUrl.isEmpty()) { 0521 m_subtitleTrFileName = QFileInfo(m_subtitleTrUrl.path()).fileName(); 0522 Q_ASSERT(codec != nullptr); 0523 m_subtitleTrEncoding = codec->name(); 0524 m_recentSubtitlesTrAction->addUrl(m_subtitleTrUrl, codec->name()); 0525 m_reopenSubtitleTrAsAction->setCurrentCodec(codec); 0526 m_saveSubtitleTrAsAction->setCurrentCodec(codec); 0527 } 0528 0529 const QStringList subtitleStreams = { 0530 i18nc("@item:inmenu Display primary text in video player", "Primary"), 0531 i18nc("@item:inmenu Display translation text in video player", "Translation"), 0532 }; 0533 KSelectAction *activeSubtitleStreamAction = (KSelectAction *)action(ACT_SET_ACTIVE_SUBTITLE_STREAM); 0534 activeSubtitleStreamAction->setItems(subtitleStreams); 0535 if(activeSubtitleStreamAction->currentItem() == -1) 0536 activeSubtitleStreamAction->setCurrentItem(0); 0537 0538 if(!m_translationMode) { 0539 m_translationMode = true; 0540 updateTitle(); 0541 emit translationModeChanged(true); 0542 } 0543 } 0544 0545 bool 0546 Application::saveSubtitleTr(QTextCodec *codec) 0547 { 0548 if(m_subtitleTrUrl.isEmpty() || !FormatManager::instance().hasOutput(m_subtitleTrFormat)) 0549 return saveSubtitleTrAs(codec); 0550 0551 if(!codec) 0552 codec = QTextCodec::codecForName(m_subtitleTrEncoding.toUtf8()); 0553 if(!codec) 0554 codec = QTextCodec::codecForName(SCConfig::defaultSubtitlesEncoding().toUtf8()); 0555 if(!codec) 0556 codec = QTextCodec::codecForLocale(); 0557 0558 if(FormatManager::instance().writeSubtitle(*appSubtitle(), false, m_subtitleTrUrl, codec, m_subtitleTrFormat, true)) { 0559 appSubtitle()->clearSecondaryDirty(); 0560 0561 m_reopenSubtitleTrAsAction->setCurrentCodec(codec); 0562 m_saveSubtitleTrAsAction->setCurrentCodec(codec); 0563 m_recentSubtitlesTrAction->addUrl(m_subtitleTrUrl, codec->name()); 0564 m_subtitleTrEncoding = codec->name(); 0565 0566 updateTitle(); 0567 0568 return true; 0569 } else { 0570 KMessageBox::error(m_mainWindow, i18n("There was an error saving the translation subtitle.")); 0571 return false; 0572 } 0573 } 0574 0575 bool 0576 Application::saveSubtitleTrAs(QTextCodec *codec) 0577 { 0578 QFileDialog saveDlg(m_mainWindow, i18n("Save Translation Subtitle"), QString(), buildSubtitleFilesFilter(false)); 0579 0580 saveDlg.setModal(true); 0581 saveDlg.setAcceptMode(QFileDialog::AcceptSave); 0582 saveDlg.setDirectory(QFileInfo(m_lastSubtitleUrl.toLocalFile()).absoluteDir()); 0583 saveDlg.selectNameFilter(FormatManager::instance().defaultOutput()->dialogFilter()); 0584 if(!m_subtitleUrl.isEmpty()) { 0585 saveDlg.selectUrl(m_subtitleTrUrl); 0586 const OutputFormat *fmt = FormatManager::instance().output(m_subtitleTrFormat); 0587 if(fmt) 0588 saveDlg.selectNameFilter(fmt->dialogFilter()); 0589 } 0590 0591 if(saveDlg.exec() == QDialog::Accepted) { 0592 const QUrl url = saveDlg.selectedUrls().constFirst(); 0593 if(!acceptClashingUrls(m_subtitleUrl, url)) 0594 return false; 0595 0596 m_subtitleTrUrl = url; 0597 m_subtitleTrFileName = QFileInfo(m_subtitleTrUrl.path()).fileName(); 0598 m_subtitleTrFormat = saveDlg.selectedNameFilter(); 0599 if(!m_subtitleTrFormat.isEmpty()) 0600 m_subtitleTrFormat.truncate(m_subtitleTrFormat.indexOf(QStringLiteral(" ("))); 0601 0602 return saveSubtitleTr(codec); 0603 } 0604 0605 return false; 0606 } 0607 0608 bool 0609 Application::closeSubtitleTr() 0610 { 0611 if(appSubtitle() && m_translationMode) { 0612 if(m_translationMode && appSubtitle()->isSecondaryDirty()) { 0613 KMessageBox::ButtonCode result = KMessageBox::warningTwoActionsCancel(nullptr, 0614 i18n("Currently opened translation subtitle has unsaved changes.\nDo you want to save them?"), 0615 i18n("Close Translation Subtitle") + " - SubtitleComposer", 0616 KStandardGuiItem::save(), KStandardGuiItem::dontSave()); 0617 if(result == KMessageBox::Cancel) 0618 return false; 0619 else if(result == KMessageBox::PrimaryAction) 0620 if(!saveSubtitleTr()) 0621 return false; 0622 } 0623 0624 m_translationMode = false; 0625 emit translationModeChanged(false); 0626 0627 updateTitle(); 0628 0629 m_mainWindow->m_linesWidget->setUpdatesEnabled(false); 0630 0631 // The cleaning of the translations texts shouldn't be an undoable action 0632 // QUndoStack *savedStack = appUndoStack(); 0633 // AppGlobal::undoStack = new QUndoStack(); 0634 appSubtitle()->clearSecondaryTextData(); 0635 // delete AppGlobal::undoStack; 0636 // AppGlobal::undoStack = savedStack; 0637 0638 m_mainWindow->m_linesWidget->setUpdatesEnabled(true); 0639 } 0640 0641 return true; 0642 } 0643 0644 QUrl 0645 Application::saveSplitSubtitle(const Subtitle &subtitle, const QUrl &srcUrl, QString encoding, QString format, bool primary) 0646 { 0647 QUrl dstUrl; 0648 0649 if(subtitle.linesCount()) { 0650 if(encoding.isEmpty()) 0651 encoding = "UTF-8"; 0652 0653 if(format.isEmpty()) 0654 format = FormatManager::instance().defaultOutput()->name(); 0655 0656 QFileInfo dstFileInfo; 0657 if(srcUrl.isEmpty() || !srcUrl.isLocalFile()) { 0658 QString baseName = primary ? i18n("Untitled") : i18n("Untitled Translation"); 0659 dstFileInfo = QFileInfo(QDir(System::tempDir()), baseName + FormatManager::instance().output(format)->extensions().first()); 0660 } else { 0661 dstFileInfo = QFileInfo(srcUrl.toLocalFile()); 0662 } 0663 dstUrl = srcUrl; 0664 dstUrl.setPath(dstFileInfo.path()); 0665 dstUrl = System::newUrl(dstUrl, dstFileInfo.completeBaseName() + " - " + i18nc("Suffix added to split subtitles", "split"), dstFileInfo.suffix()); 0666 0667 QTextCodec *codec = QTextCodec::codecForName(encoding.toUtf8()); 0668 if(!codec) 0669 codec = QTextCodec::codecForLocale(); 0670 0671 if(FormatManager::instance().writeSubtitle(subtitle, primary, dstUrl, codec, format, false)) { 0672 if(primary) 0673 m_recentSubtitlesAction->addUrl(dstUrl, codec->name()); 0674 else 0675 m_recentSubtitlesTrAction->addUrl(dstUrl, codec->name()); 0676 } else { 0677 dstUrl.clear(); 0678 } 0679 } 0680 0681 if(dstUrl.path().isEmpty()) { 0682 KMessageBox::error(m_mainWindow, primary ? i18n("Could not write the split subtitle file.") : i18n("Could not write the split subtitle translation file.")); 0683 } 0684 0685 return dstUrl; 0686 } 0687 0688 void 0689 Application::joinSubtitles() 0690 { 0691 static JoinSubtitlesDialog *dlg = new JoinSubtitlesDialog(m_mainWindow); 0692 0693 if(dlg->exec() == QDialog::Accepted) { 0694 QExplicitlySharedDataPointer<Subtitle> secondSubtitle(new Subtitle()); 0695 0696 const QUrl url = dlg->subtitleUrl(); 0697 QTextCodec *codec = codecForEncoding(KRecentFilesActionExt::encodingForUrl(url)); 0698 const bool primary = dlg->selectedTextsTarget() != Secondary; 0699 0700 FormatManager::Status res = FormatManager::instance().readSubtitle(*secondSubtitle, primary, url, &codec, nullptr); 0701 if(res == FormatManager::SUCCESS) { 0702 if(dlg->selectedTextsTarget() == Both) 0703 secondSubtitle->setSecondaryData(*secondSubtitle, true); 0704 0705 appSubtitle()->appendSubtitle(*secondSubtitle, dlg->shiftTime().toMillis()); 0706 } else { 0707 KMessageBox::error(m_mainWindow, i18n("Could not read the subtitle file to append.")); 0708 } 0709 } 0710 } 0711 0712 void 0713 Application::splitSubtitle() 0714 { 0715 static SplitSubtitleDialog *dlg = new SplitSubtitleDialog(m_mainWindow); 0716 0717 if(dlg->exec() != QDialog::Accepted) 0718 return; 0719 0720 QExplicitlySharedDataPointer<Subtitle> newSubtitle(new Subtitle()); 0721 appSubtitle()->splitSubtitle(*newSubtitle, dlg->splitTime().toMillis(), dlg->shiftNewSubtitle()); 0722 if(!newSubtitle->linesCount()) { 0723 KMessageBox::information(m_mainWindow, i18n("The specified time does not split the subtitles.")); 0724 return; 0725 } 0726 0727 QUrl splitUrl = saveSplitSubtitle( 0728 *newSubtitle, 0729 m_subtitleUrl, 0730 m_subtitleEncoding, 0731 m_subtitleFormat, 0732 true); 0733 0734 if(splitUrl.path().isEmpty()) { 0735 // there was an error saving the split part, undo the splitting of appSubtitle() 0736 appUndoStack()->undo(); 0737 return; 0738 } 0739 0740 QUrl splitTrUrl; 0741 if(m_translationMode) { 0742 splitTrUrl = saveSplitSubtitle(*newSubtitle, m_subtitleTrUrl, m_subtitleTrEncoding, m_subtitleTrFormat, false); 0743 0744 if(splitTrUrl.path().isEmpty()) { 0745 // there was an error saving the split part, undo the splitting of appSubtitle() 0746 appUndoStack()->undo(); 0747 return; 0748 } 0749 } 0750 0751 QStringList args; 0752 args << splitUrl.toString(QUrl::PreferLocalFile); 0753 if(m_translationMode) 0754 args << splitTrUrl.toString(QUrl::PreferLocalFile); 0755 0756 if(!QProcess::startDetached(applicationName(), args)) { 0757 KMessageBox::error(m_mainWindow, m_translationMode 0758 ? i18n("Could not open a new Subtitle Composer window.\n" "The split part was saved as %1.", splitUrl.path()) 0759 : i18n("Could not open a new Subtitle Composer window.\n" "The split parts were saved as %1 and %2.", splitUrl.path(), splitTrUrl.path())); 0760 } 0761 } 0762 0763 void 0764 Application::syncWithSubtitle() 0765 { 0766 static SyncSubtitlesDialog *dlg = new SyncSubtitlesDialog(m_mainWindow); 0767 0768 if(dlg->exec() == QDialog::Accepted) { 0769 QExplicitlySharedDataPointer<Subtitle> referenceSubtitle(new Subtitle()); 0770 0771 const QUrl url = dlg->subtitleUrl(); 0772 0773 FormatManager::Status res = FormatManager::instance().readSubtitle(*referenceSubtitle, true, url, nullptr, nullptr); 0774 if(res == FormatManager::SUCCESS) { 0775 if(dlg->adjustToReferenceSubtitle()) { 0776 if(referenceSubtitle->linesCount() <= 1) 0777 KMessageBox::error(m_mainWindow, i18n("The reference subtitle must have more than one line to proceed.")); 0778 else 0779 appSubtitle()->adjustLines(Range::full(), 0780 referenceSubtitle->firstLine()->showTime().toMillis(), 0781 referenceSubtitle->lastLine()->showTime().toMillis()); 0782 } else /*if(dlg->synchronizeToReferenceTimes())*/ { 0783 appSubtitle()->syncWithSubtitle(*referenceSubtitle); 0784 } 0785 } else { 0786 KMessageBox::error(m_mainWindow, i18n("Could not parse the reference subtitle file.")); 0787 } 0788 } 0789 }