File indexing completed on 2024-05-12 04:57:14

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 #include "config.h"
0009 
0010 #include "appglobal.h"
0011 #include "application.h"
0012 #include "actions/krecentfilesactionext.h"
0013 #include "actions/useraction.h"
0014 #include "actions/useractionnames.h"
0015 #include "configs/configdialog.h"
0016 #include "core/richtext/richdocument.h"
0017 #include "core/subtitleiterator.h"
0018 #include "core/undo/undostack.h"
0019 #include "gui/currentlinewidget.h"
0020 #include "gui/subtitlemeta/subtitlemetawidget.h"
0021 #include "helpers/common.h"
0022 #include "dialogs/actionwithtargetdialog.h"
0023 #include "dialogs/shifttimesdialog.h"
0024 #include "dialogs/adjusttimesdialog.h"
0025 #include "dialogs/durationlimitsdialog.h"
0026 #include "dialogs/autodurationsdialog.h"
0027 #include "dialogs/changetextscasedialog.h"
0028 #include "dialogs/fixoverlappingtimesdialog.h"
0029 #include "dialogs/fixpunctuationdialog.h"
0030 #include "dialogs/smarttextsadjustdialog.h"
0031 #include "dialogs/changeframeratedialog.h"
0032 #include "dialogs/insertlinedialog.h"
0033 #include "dialogs/removelinesdialog.h"
0034 #include "dialogs/intinputdialog.h"
0035 #include "dialogs/subtitlecolordialog.h"
0036 #include "errors/errorfinder.h"
0037 #include "errors/errortracker.h"
0038 #include "formats/formatmanager.h"
0039 #include "formats/outputformat.h"
0040 #include "formats/textdemux/textdemux.h"
0041 #include "helpers/commondefs.h"
0042 #include "helpers/fileloadhelper.h"
0043 #include "helpers/filesavehelper.h"
0044 #include "gui/treeview/lineswidget.h"
0045 #include "mainwindow.h"
0046 #include "gui/playerwidget.h"
0047 #include "scripting/scriptsmanager.h"
0048 #include "speechprocessor/speechprocessor.h"
0049 #include "utils/finder.h"
0050 #include "utils/replacer.h"
0051 #include "utils/speller.h"
0052 #include "videoplayer/videoplayer.h"
0053 #include "gui/waveform/waveformwidget.h"
0054 
0055 #include <limits>
0056 
0057 #include <QAction>
0058 #include <QByteArrayList>
0059 #include <QDir>
0060 #include <QDockWidget>
0061 #include <QFileDialog>
0062 #include <QFileInfo>
0063 #include <QGridLayout>
0064 #include <QKeySequence>
0065 #include <QLabel>
0066 #include <QMenuBar>
0067 #include <QStatusBar>
0068 #include <QStringBuilder>
0069 #include <QTextCodec>
0070 #include <QThread>
0071 #include <QUndoGroup>
0072 #include <QUndoStack>
0073 
0074 #include <KActionCollection>
0075 #include <KCharsets>
0076 #include <KComboBox>
0077 #include <KConfig>
0078 #include <KMessageBox>
0079 #include <KSelectAction>
0080 #include <KStandardShortcut>
0081 #include <KStandardAction>
0082 #include <KToggleAction>
0083 #include <KToolBar>
0084 #include <kxmlgui_version.h>
0085 
0086 using namespace SubtitleComposer;
0087 
0088 static void
0089 setupIconTheme(int argc, char **argv)
0090 {
0091 #ifdef SC_BUNDLE_SYSTEM_THEME
0092     QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << $(":/icons"));
0093 #endif
0094     if(QIcon::themeName().isEmpty())
0095         QIcon::setThemeName($("breeze"));
0096 
0097     const QStringList fallbackPaths = {
0098         $(":/icons-fallback/breeze/actions/22"),
0099         $(":/icons-fallback/breeze/apps/256"),
0100         $(":/icons-fallback/breeze/apps/128"),
0101         $(":/icons-fallback/breeze/apps/48"),
0102         $(":/icons-fallback/breeze/apps/32"),
0103         $(":/icons-fallback/breeze/apps/16"),
0104     };
0105 
0106 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
0107     QIcon::setFallbackSearchPaths(QIcon::fallbackSearchPaths()
0108 #else
0109     QIcon::setThemeSearchPaths(QIcon::themeSearchPaths()
0110 #endif
0111             // access the icons through breeze theme path
0112             << $(":/icons-fallback")
0113             // or directly as fallback
0114             << fallbackPaths);
0115 
0116 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
0117     if(QIcon::fallbackThemeName().isEmpty())
0118         QIcon::setFallbackThemeName($("breeze"));
0119 #endif
0120 
0121 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
0122     // LXQt just ignores QIcon::fallbackSearchPaths()
0123     QString platformThemeName = QString::fromLocal8Bit(qgetenv("QT_QPA_PLATFORMTHEME"));
0124     for(int i = 0; i < argc; i++) {
0125         if(strcmp(argv[i], "-platformtheme") == 0 && ++i < argc) {
0126             platformThemeName = QString::fromLocal8Bit(argv[i]);
0127             break;
0128         }
0129     }
0130 
0131     if(platformThemeName == $("lxqt"))
0132         QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << fallbackPaths);
0133 #else
0134     Q_UNUSED(argc)
0135     Q_UNUSED(argv)
0136 #endif
0137 }
0138 
0139 Application::Application(int &argc, char **argv) :
0140     QApplication(argc, argv),
0141     m_translationMode(false),
0142     m_textDemux(nullptr),
0143     m_speechProcessor(nullptr),
0144     m_lastFoundLine(nullptr),
0145     m_lastSubtitleUrl(QDir::homePath()),
0146     m_lastVideoUrl(QDir::homePath()),
0147     m_linkCurrentLineToPosition(false)
0148 {
0149     AppGlobal::app = this;
0150 
0151     KLocalizedString::setApplicationDomain("subtitlecomposer");
0152 
0153     setupIconTheme(argc, argv);
0154 
0155 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0156     setAttribute(Qt::AA_UseHighDpiPixmaps, true);
0157 #endif
0158 }
0159 
0160 void
0161 Application::createMainWindow()
0162 {
0163     m_mainWindow = new MainWindow();
0164     connect(m_mainWindow, &QObject::destroyed, this, [&](){ m_mainWindow = nullptr; });
0165 
0166     m_finder = new Finder(m_mainWindow->m_linesWidget);
0167     m_replacer = new Replacer(m_mainWindow->m_linesWidget);
0168     m_errorFinder = new ErrorFinder(m_mainWindow->m_linesWidget);
0169     m_speller = new Speller(m_mainWindow->m_linesWidget);
0170 
0171     m_errorTracker = new ErrorTracker(this);
0172 
0173     QStatusBar *statusBar = m_mainWindow->statusBar();
0174     QMargins m = statusBar->contentsMargins();
0175     m.setRight(m.left() + 5);
0176     statusBar->setContentsMargins(m);
0177 
0178     m_labSubFormat = new QLabel();
0179     statusBar->addPermanentWidget(m_labSubFormat);
0180 
0181     m_labSubEncoding = new QLabel();
0182     statusBar->addPermanentWidget(m_labSubEncoding);
0183 
0184     m_textDemux = new TextDemux(m_mainWindow);
0185     statusBar->addPermanentWidget(m_textDemux->progressWidget());
0186 
0187     m_speechProcessor = new SpeechProcessor(m_mainWindow);
0188     statusBar->addPermanentWidget(m_speechProcessor->progressWidget());
0189 
0190     m_scriptsManager = new ScriptsManager(this);
0191 
0192     AppGlobal::undoStack = new UndoStack(m_mainWindow);
0193 
0194     UserActionManager *actionManager = UserActionManager::instance();
0195     actionManager->setLinesWidget(m_mainWindow->m_linesWidget);
0196     actionManager->setFullScreenMode(false);
0197 
0198     setupActions();
0199 
0200     connect(SCConfig::self(), &KCoreConfigSkeleton::configChanged, this, &Application::onConfigChanged);
0201 
0202     VideoPlayer *videoPlayer = VideoPlayer::instance();
0203     connect(videoPlayer, &VideoPlayer::fileOpened, this, &Application::onPlayerFileOpened);
0204     connect(videoPlayer, &VideoPlayer::playing, this, &Application::onPlayerPlaying);
0205     connect(videoPlayer, &VideoPlayer::paused, this, &Application::onPlayerPaused);
0206     connect(videoPlayer, &VideoPlayer::stopped, this, &Application::onPlayerStopped);
0207     connect(videoPlayer, &VideoPlayer::audioStreamsChanged, this, &Application::onPlayerAudioStreamsChanged);
0208     connect(videoPlayer, &VideoPlayer::textStreamsChanged, this, &Application::onPlayerTextStreamsChanged);
0209     connect(videoPlayer, &VideoPlayer::activeAudioStreamChanged, this, &Application::onPlayerActiveAudioStreamChanged);
0210     connect(videoPlayer, &VideoPlayer::muteChanged, this, &Application::onPlayerMuteChanged);
0211 
0212 #define CONNECT_SUB(c, x) \
0213     connect(this, &Application::subtitleOpened, x, &c::setSubtitle); \
0214     connect(this, &Application::subtitleClosed, x, [&](){ x->setSubtitle(nullptr); });
0215 
0216     CONNECT_SUB(UserActionManager, UserActionManager::instance());
0217     CONNECT_SUB(PlayerWidget, m_mainWindow->m_playerWidget);
0218     CONNECT_SUB(SubtitleMetaWidget, m_mainWindow->m_metaWidget);
0219     CONNECT_SUB(LinesWidget, m_mainWindow->m_linesWidget);
0220     CONNECT_SUB(CurrentLineWidget, m_mainWindow->m_curLineWidget);
0221     CONNECT_SUB(Finder, m_finder);
0222     CONNECT_SUB(Replacer, m_replacer);
0223     CONNECT_SUB(ErrorFinder, m_errorFinder);
0224     CONNECT_SUB(Speller, m_speller);
0225     CONNECT_SUB(ErrorTracker, m_errorTracker);
0226     CONNECT_SUB(ScriptsManager, m_scriptsManager);
0227     CONNECT_SUB(WaveformWidget, m_mainWindow->m_waveformWidget);
0228     CONNECT_SUB(SpeechProcessor, m_speechProcessor);
0229 
0230 #define CONNECT_TRANS(c, x) \
0231     connect(this, &Application::translationModeChanged, x, &c::setTranslationMode);
0232 
0233     CONNECT_TRANS(UserActionManager, UserActionManager::instance());
0234     CONNECT_TRANS(PlayerWidget, m_mainWindow->m_playerWidget);
0235     CONNECT_TRANS(WaveformWidget, m_mainWindow->m_waveformWidget);
0236     CONNECT_TRANS(LinesWidget, m_mainWindow->m_linesWidget);
0237     CONNECT_TRANS(CurrentLineWidget, m_mainWindow->m_curLineWidget);
0238     CONNECT_TRANS(Finder, m_finder);
0239     CONNECT_TRANS(Replacer, m_replacer);
0240     CONNECT_TRANS(ErrorFinder, m_errorFinder);
0241     CONNECT_TRANS(Speller, m_speller);
0242 
0243     connect(this, &Application::fullScreenModeChanged, actionManager, &UserActionManager::setFullScreenMode);
0244 
0245     connect(m_mainWindow->m_waveformWidget, &WaveformWidget::doubleClick, this, &Application::onWaveformDoubleClicked);
0246     connect(m_mainWindow->m_waveformWidget, &WaveformWidget::middleMouseDown, this, &Application::onWaveformMiddleMouse);
0247     connect(m_mainWindow->m_waveformWidget, &WaveformWidget::middleMouseMove, this, &Application::onWaveformMiddleMouse);
0248     connect(m_mainWindow->m_waveformWidget, &WaveformWidget::middleMouseUp, this, &Application::onWaveformMiddleMouse);
0249 
0250     connect(m_mainWindow->m_linesWidget, &LinesWidget::currentLineChanged, m_mainWindow->m_metaWidget, &SubtitleMetaWidget::setCurrentLine);
0251 
0252     connect(m_mainWindow->m_linesWidget, &LinesWidget::currentLineChanged, m_mainWindow->m_curLineWidget, &CurrentLineWidget::setCurrentLine);
0253     connect(m_mainWindow->m_linesWidget, &LinesWidget::lineDoubleClicked, this, &Application::onLineDoubleClicked);
0254 
0255     connect(m_mainWindow->m_playerWidget, &PlayerWidget::playingLineChanged, this, &Application::onPlayingLineChanged);
0256 
0257     connect(m_finder, &Finder::found, this, &Application::onHighlightLine);
0258     connect(m_replacer, &Replacer::found, this, &Application::onHighlightLine);
0259     connect(m_errorFinder, &ErrorFinder::found, this, [this](SubtitleLine *l){ onHighlightLine(l); });
0260     connect(m_speller, &Speller::misspelled, this, &Application::onHighlightLine);
0261 
0262     connect(m_textDemux, &TextDemux::onError, this, [&](const QString &message){ KMessageBox::error(m_mainWindow, message); });
0263 
0264     connect(m_speechProcessor, &SpeechProcessor::onError, this, [&](const QString &message){ KMessageBox::error(m_mainWindow, message); });
0265 
0266     m_mainWindow->setupGUI();
0267 
0268     // Workaround for https://phabricator.kde.org/D13808
0269     // menubar can't be hidden so always show it
0270     m_mainWindow->findChild<QMenuBar *>(QString(), Qt::FindDirectChildrenOnly)->show();
0271 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 1) && KXMLGUI_VERSION < QT_VERSION_CHECK(5, 49, 0)
0272     // show rest if we're on broken KF5/Qt
0273     m_mainWindow->findChild<QStatusBar *>(QString(), Qt::FindDirectChildrenOnly)->show();
0274     m_mainWindow->findChild<QDockWidget *>(QStringLiteral("player_dock"), Qt::FindDirectChildrenOnly)->show();
0275     m_mainWindow->findChild<QDockWidget *>(QStringLiteral("waveform_dock"), Qt::FindDirectChildrenOnly)->show();
0276     foreach(KToolBar *toolbar, m_mainWindow->toolBars())
0277         toolbar->show();
0278 #endif
0279 
0280     m_scriptsManager->reloadScripts();
0281 
0282     loadConfig();
0283 }
0284 
0285 Application::~Application()
0286 {
0287     // NOTE: The Application destructor is called after all widgets are destroyed
0288     // (NOT BEFORE). Therefore is not possible to save the program settings (nor do
0289     // pretty much anything) at this point.
0290 
0291     // delete m_mainWindow; the window is destroyed when it's closed
0292     AppGlobal::app = nullptr;
0293 }
0294 
0295 void
0296 Application::loadConfig()
0297 {
0298     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0299     KConfigGroup group(config->group("Application Settings"));
0300 
0301     m_lastSubtitleUrl = QUrl(group.readPathEntry("LastSubtitleUrl", QDir::homePath()));
0302     m_recentSubtitlesAction->loadEntries(config->group("Recent Subtitles"));
0303     m_recentSubtitlesTrAction->loadEntries(config->group("Recent Translation Subtitles"));
0304 
0305     m_lastVideoUrl = QUrl(group.readPathEntry("LastVideoUrl", QDir::homePath()));
0306     m_recentVideosAction->loadEntries(config->group("Recent Videos"));
0307 
0308     VideoPlayer *videoPlayer = VideoPlayer::instance();
0309     videoPlayer->setMuted(group.readEntry<bool>("Muted", false));
0310     videoPlayer->setVolume(group.readEntry<double>("Volume", 100.0));
0311 
0312     ((KToggleAction *)action(ACT_TOGGLE_MUTED))->setChecked(videoPlayer->isMuted());
0313 
0314     KConfigGroup wfGroup(config->group("Waveform Widget"));
0315     action(ACT_WAVEFORM_AUTOSCROLL)->setChecked(wfGroup.readEntry<bool>("AutoScroll", true));
0316     if(wfGroup.hasKey("Zoom"))
0317         m_mainWindow->m_waveformWidget->setZoom(wfGroup.readEntry<quint32>("Zoom", 0));
0318 
0319     m_mainWindow->loadConfig();
0320     m_mainWindow->m_playerWidget->loadConfig();
0321     m_mainWindow->m_linesWidget->loadConfig();
0322     m_mainWindow->m_curLineWidget->loadConfig();
0323 }
0324 
0325 void
0326 Application::saveConfig()
0327 {
0328     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0329     KConfigGroup group(config->group("Application Settings"));
0330 
0331     group.writePathEntry("LastSubtitleUrl", m_lastSubtitleUrl.toString());
0332     m_recentSubtitlesAction->saveEntries(config->group("Recent Subtitles"));
0333     m_recentSubtitlesTrAction->saveEntries(config->group("Recent Translation Subtitles"));
0334 
0335     group.writePathEntry("LastVideoUrl", m_lastVideoUrl.toString());
0336     m_recentVideosAction->saveEntries(config->group("Recent Videos"));
0337 
0338     VideoPlayer *videoPlayer = VideoPlayer::instance();
0339     group.writeEntry("Muted", videoPlayer->isMuted());
0340     group.writeEntry("Volume", videoPlayer->volume());
0341 
0342     KConfigGroup wfGroup(config->group("Waveform Widget"));
0343     wfGroup.writeEntry("AutoScroll", m_mainWindow->m_waveformWidget->autoScroll());
0344     wfGroup.writeEntry("Zoom", m_mainWindow->m_waveformWidget->zoom());
0345 
0346     m_mainWindow->saveConfig();
0347     m_mainWindow->m_playerWidget->saveConfig();
0348     m_mainWindow->m_linesWidget->saveConfig();
0349     m_mainWindow->m_curLineWidget->saveConfig();
0350 }
0351 
0352 bool
0353 Application::triggerAction(const QKeySequence &keySequence)
0354 {
0355     QList<QAction *> actions = m_mainWindow->actionCollection()->actions();
0356 
0357     for(QList<QAction *>::ConstIterator it = actions.constBegin(), end = actions.constEnd(); it != end; ++it) {
0358         if((*it)->isEnabled()) {
0359             if(QAction * action = qobject_cast<QAction *>(*it)) {
0360                 QKeySequence shortcut = action->shortcut();
0361                 if(shortcut.matches(keySequence) == QKeySequence::ExactMatch) {
0362                     action->trigger();
0363                     return true;
0364                 }
0365             } else {
0366                 if((*it)->shortcut() == keySequence) {
0367                     (*it)->trigger();
0368                     return true;
0369                 }
0370             }
0371         }
0372     }
0373 
0374     return false;
0375 }
0376 
0377 const QStringList &
0378 Application::availableEncodingNames() const
0379 {
0380     static QStringList encodingNames;
0381 
0382     if(encodingNames.empty()) {
0383         QStringList encodings(KCharsets::charsets()->availableEncodingNames());
0384         for(QStringList::Iterator it = encodings.begin(); it != encodings.end(); ++it) {
0385             QTextCodec *codec = QTextCodec::codecForName(it->toUtf8());
0386             if(codec)
0387                 encodingNames.append(codec->name().toUpper());
0388         }
0389         encodingNames.sort();
0390     }
0391 
0392     return encodingNames;
0393 }
0394 
0395 void
0396 Application::showPreferences()
0397 {
0398     (new ConfigDialog(m_mainWindow, "scconfig", SCConfig::self()))->show();
0399 }
0400 
0401 /// BEGIN ACTION HANDLERS
0402 
0403 Time
0404 Application::videoPosition(bool compensate)
0405 {
0406     VideoPlayer *videoPlayer = VideoPlayer::instance();
0407     if(compensate && !videoPlayer->isPaused())
0408         return Time(double(videoPlayer->position()) * 1000. - SCConfig::grabbedPositionCompensation());
0409     else
0410         return Time(double(videoPlayer->position()) * 1000.);
0411 }
0412 
0413 void
0414 Application::insertBeforeCurrentLine()
0415 {
0416     static InsertLineDialog *dlg = new InsertLineDialog(true, m_mainWindow);
0417 
0418     if(dlg->exec() == QDialog::Accepted) {
0419         SubtitleLine *newLine;
0420         {
0421             LinesWidgetScrollToModelDetacher detacher(*m_mainWindow->m_linesWidget);
0422             SubtitleLine *currentLine = m_mainWindow->m_linesWidget->currentLine();
0423             newLine = appSubtitle()->insertNewLine(currentLine ? currentLine->index() : 0, false, dlg->selectedTextsTarget());
0424         }
0425         m_mainWindow->m_linesWidget->setCurrentLine(newLine, true);
0426     }
0427 }
0428 
0429 void
0430 Application::insertAfterCurrentLine()
0431 {
0432     static InsertLineDialog *dlg = new InsertLineDialog(false, m_mainWindow);
0433 
0434     if(dlg->exec() == QDialog::Accepted) {
0435         SubtitleLine *newLine;
0436         {
0437             LinesWidgetScrollToModelDetacher detacher(*m_mainWindow->m_linesWidget);
0438 
0439             SubtitleLine *currentLine = m_mainWindow->m_linesWidget->currentLine();
0440             newLine = appSubtitle()->insertNewLine(currentLine ? currentLine->index() + 1 : 0, true, dlg->selectedTextsTarget());
0441         }
0442         m_mainWindow->m_linesWidget->setCurrentLine(newLine, true);
0443     }
0444 }
0445 
0446 void
0447 Application::removeSelectedLines()
0448 {
0449     static RemoveLinesDialog *dlg = new RemoveLinesDialog(m_mainWindow);
0450 
0451     if(dlg->exec() == QDialog::Accepted) {
0452         RangeList selectionRanges = m_mainWindow->m_linesWidget->selectionRanges();
0453 
0454         if(selectionRanges.isEmpty())
0455             return;
0456 
0457         {
0458             LinesWidgetScrollToModelDetacher detacher(*m_mainWindow->m_linesWidget);
0459             appSubtitle()->removeLines(selectionRanges, dlg->selectedTextsTarget());
0460         }
0461 
0462         int firstIndex = selectionRanges.firstIndex();
0463         if(firstIndex < appSubtitle()->linesCount())
0464             m_mainWindow->m_linesWidget->setCurrentLine(appSubtitle()->line(firstIndex), true);
0465         else if(firstIndex - 1 < appSubtitle()->linesCount())
0466             m_mainWindow->m_linesWidget->setCurrentLine(appSubtitle()->line(firstIndex - 1), true);
0467     }
0468 }
0469 
0470 void
0471 Application::joinSelectedLines()
0472 {
0473     const RangeList &ranges = m_mainWindow->m_linesWidget->selectionRanges();
0474 
0475 //  if ( ranges.count() > 1 && KMessageBox::Continue != KMessageBox::warningContinueCancel(
0476 //      m_mainWindow,
0477 //      i18n( "Current selection has multiple ranges.\nContinuing will join them all." ),
0478 //      i18n( "Join Lines" ) ) )
0479 //      return;
0480 
0481     appSubtitle()->joinLines(ranges);
0482 }
0483 
0484 void
0485 Application::splitSelectedLines()
0486 {
0487     appSubtitle()->splitLines(m_mainWindow->m_linesWidget->selectionRanges());
0488 }
0489 
0490 void
0491 Application::selectAllLines()
0492 {
0493     m_mainWindow->m_linesWidget->selectAll();
0494 }
0495 
0496 void
0497 Application::gotoLine()
0498 {
0499     IntInputDialog gotoLineDlg(i18n("Go to Line"), i18n("&Go to line:"), 1, appSubtitle()->linesCount(), m_mainWindow->m_linesWidget->currentLineIndex() + 1);
0500 
0501     if(gotoLineDlg.exec() == QDialog::Accepted)
0502         m_mainWindow->m_linesWidget->setCurrentLine(appSubtitle()->line(gotoLineDlg.value() - 1), true);
0503 }
0504 
0505 void
0506 Application::find()
0507 {
0508     m_lastFoundLine = nullptr;
0509     m_finder->find(m_mainWindow->m_linesWidget->selectionRanges(), m_mainWindow->m_linesWidget->currentLineIndex(), m_mainWindow->m_curLineWidget->focusedText(), false);
0510 }
0511 
0512 void
0513 Application::findNext()
0514 {
0515     if(!m_finder->findNext()) {
0516         m_lastFoundLine = nullptr;
0517         m_finder->find(m_mainWindow->m_linesWidget->selectionRanges(), m_mainWindow->m_linesWidget->currentLineIndex(), m_mainWindow->m_curLineWidget->focusedText(), false);
0518     }
0519 }
0520 
0521 void
0522 Application::findPrevious()
0523 {
0524     if(!m_finder->findPrevious()) {
0525         m_lastFoundLine = nullptr;
0526         m_finder->find(m_mainWindow->m_linesWidget->selectionRanges(), m_mainWindow->m_linesWidget->currentLineIndex(), m_mainWindow->m_curLineWidget->focusedText(), true);
0527     }
0528 }
0529 
0530 void
0531 Application::replace()
0532 {
0533     m_replacer->replace(m_mainWindow->m_linesWidget->selectionRanges(), m_mainWindow->m_linesWidget->currentLineIndex(), m_mainWindow->m_curLineWidget->focusedText());
0534 }
0535 
0536 void
0537 Application::spellCheck()
0538 {
0539     m_speller->spellCheck(m_mainWindow->m_linesWidget->currentLineIndex());
0540 }
0541 
0542 void
0543 Application::retrocedeCurrentLine()
0544 {
0545     SubtitleLine *currentLine = m_mainWindow->m_linesWidget->currentLine();
0546     if(currentLine && currentLine->prevLine())
0547         m_mainWindow->m_linesWidget->setCurrentLine(currentLine->prevLine(), true);
0548 }
0549 
0550 void
0551 Application::advanceCurrentLine()
0552 {
0553     SubtitleLine *currentLine = m_mainWindow->m_linesWidget->currentLine();
0554     if(currentLine && currentLine->nextLine())
0555         m_mainWindow->m_linesWidget->setCurrentLine(currentLine->nextLine(), true);
0556 }
0557 
0558 void
0559 Application::toggleSelectedLinesBold()
0560 {
0561     appSubtitle()->toggleStyleFlag(m_mainWindow->m_linesWidget->selectionRanges(), RichString::Bold);
0562 }
0563 
0564 void
0565 Application::toggleSelectedLinesItalic()
0566 {
0567     appSubtitle()->toggleStyleFlag(m_mainWindow->m_linesWidget->selectionRanges(), RichString::Italic);
0568 }
0569 
0570 void
0571 Application::toggleSelectedLinesUnderline()
0572 {
0573     appSubtitle()->toggleStyleFlag(m_mainWindow->m_linesWidget->selectionRanges(), RichString::Underline);
0574 }
0575 
0576 void
0577 Application::toggleSelectedLinesStrikeThrough()
0578 {
0579     appSubtitle()->toggleStyleFlag(m_mainWindow->m_linesWidget->selectionRanges(), RichString::StrikeThrough);
0580 }
0581 
0582 void
0583 Application::changeSelectedLinesColor()
0584 {
0585     const RangeList range = m_mainWindow->m_linesWidget->selectionRanges();
0586     SubtitleIterator it(*appSubtitle(), range);
0587     if(!it.current())
0588         return;
0589 
0590     QColor color = SubtitleColorDialog::getColor(QColor(it.current()->primaryDoc()->styleColorAt(0)), m_mainWindow);
0591     if(color.isValid())
0592         appSubtitle()->changeTextColor(range, color.rgba());
0593 }
0594 
0595 void
0596 Application::shiftLines()
0597 {
0598     static ShiftTimesDialog *dlg = new ShiftTimesDialog(m_mainWindow);
0599 
0600     dlg->resetShiftTime();
0601 
0602     if(dlg->exec() == QDialog::Accepted)
0603         appSubtitle()->shiftLines(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()), dlg->shiftTimeMillis());
0604 }
0605 
0606 void
0607 Application::shiftSelectedLinesForwards()
0608 {
0609     appSubtitle()->shiftLines(m_mainWindow->m_linesWidget->selectionRanges(), SCConfig::linesQuickShiftAmount());
0610 }
0611 
0612 void
0613 Application::shiftSelectedLinesBackwards()
0614 {
0615     appSubtitle()->shiftLines(m_mainWindow->m_linesWidget->selectionRanges(), -SCConfig::linesQuickShiftAmount());
0616 }
0617 
0618 void
0619 Application::adjustLines()
0620 {
0621     static AdjustTimesDialog *dlg = new AdjustTimesDialog(m_mainWindow);
0622 
0623     dlg->setFirstLineTime(appSubtitle()->firstLine()->showTime());
0624     dlg->setLastLineTime(appSubtitle()->lastLine()->showTime());
0625 
0626     if(dlg->exec() == QDialog::Accepted)
0627         appSubtitle()->adjustLines(Range::full(), dlg->firstLineTime().toMillis(), dlg->lastLineTime().toMillis());
0628 }
0629 
0630 void
0631 Application::changeFrameRate()
0632 {
0633     static ChangeFrameRateDialog *dlg = new ChangeFrameRateDialog(appSubtitle()->framesPerSecond(), m_mainWindow);
0634 
0635     dlg->setFromFramesPerSecond(appSubtitle()->framesPerSecond());
0636 
0637     if(dlg->exec() == QDialog::Accepted) {
0638         appSubtitle()->changeFramesPerSecond(dlg->toFramesPerSecond(), dlg->fromFramesPerSecond());
0639     }
0640 }
0641 
0642 void
0643 Application::enforceDurationLimits()
0644 {
0645     static DurationLimitsDialog *dlg = new DurationLimitsDialog(SCConfig::minDuration(),
0646                                                                 SCConfig::maxDuration(),
0647                                                                 m_mainWindow);
0648 
0649     if(dlg->exec() == QDialog::Accepted) {
0650         appSubtitle()->applyDurationLimits(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0651             dlg->enforceMinDuration() ? dlg->minDuration() : Time(),
0652             dlg->enforceMaxDuration() ? dlg->maxDuration() : Time(std::numeric_limits<double>::max()),
0653             !dlg->preventOverlap());
0654     }
0655 }
0656 
0657 void
0658 Application::setAutoDurations()
0659 {
0660     static AutoDurationsDialog *dlg = new AutoDurationsDialog(60, 50, 50, m_mainWindow);
0661 
0662     if(dlg->exec() == QDialog::Accepted) {
0663         appSubtitle()->setAutoDurations(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0664                                      dlg->charMillis(), dlg->wordMillis(), dlg->lineMillis(),
0665                                      !dlg->preventOverlap(), dlg->calculationMode());
0666     }
0667 }
0668 
0669 void
0670 Application::maximizeDurations()
0671 {
0672     static auto *dlg = new ActionWithLinesTargetDialog(i18n("Maximize Durations"), m_mainWindow);
0673 
0674     if(dlg->exec() == QDialog::Accepted)
0675         appSubtitle()->setMaximumDurations(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()));
0676 }
0677 
0678 void
0679 Application::fixOverlappingLines()
0680 {
0681     static FixOverlappingTimesDialog *dlg = new FixOverlappingTimesDialog(m_mainWindow);
0682 
0683     if(dlg->exec() == QDialog::Accepted)
0684         appSubtitle()->fixOverlappingLines(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()), dlg->minimumInterval());
0685 }
0686 
0687 void
0688 Application::breakLines()
0689 {
0690     static SmartTextsAdjustDialog *dlg = new SmartTextsAdjustDialog(30, m_mainWindow);
0691 
0692     if(dlg->exec() == QDialog::Accepted)
0693         appSubtitle()->breakLines(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0694                                dlg->minLengthForLineBreak(), dlg->selectedTextsTarget());
0695 }
0696 
0697 void
0698 Application::unbreakTexts()
0699 {
0700     static ActionWithLinesAndTextsTargetDialog *dlg = new ActionWithLinesAndTextsTargetDialog(i18n("Unbreak Lines"),
0701                                                                                               m_mainWindow);
0702 
0703     if(dlg->exec() == QDialog::Accepted)
0704         appSubtitle()->unbreakTexts(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0705                                  dlg->selectedTextsTarget());
0706 }
0707 
0708 void
0709 Application::simplifySpaces()
0710 {
0711     static ActionWithLinesAndTextsTargetDialog *dlg = new ActionWithLinesAndTextsTargetDialog(i18n("Simplify Spaces"),
0712                                                                                               m_mainWindow);
0713 
0714     if(dlg->exec() == QDialog::Accepted)
0715         appSubtitle()->simplifyTextWhiteSpace(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0716                                            dlg->selectedTextsTarget());
0717 }
0718 
0719 void
0720 Application::changeCase()
0721 {
0722     static ChangeTextsCaseDialog *dlg = new ChangeTextsCaseDialog(m_mainWindow);
0723 
0724     if(dlg->exec() == QDialog::Accepted) {
0725         switch(dlg->caseOperation()) {
0726         case ChangeTextsCaseDialog::Upper:
0727             appSubtitle()->upperCase(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0728                                   dlg->selectedTextsTarget());
0729             break;
0730         case ChangeTextsCaseDialog::Lower:
0731             appSubtitle()->lowerCase(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0732                                   dlg->selectedTextsTarget());
0733             break;
0734         case ChangeTextsCaseDialog::Title:
0735             appSubtitle()->titleCase(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0736                                   dlg->lowerFirst(), dlg->selectedTextsTarget());
0737             break;
0738         case ChangeTextsCaseDialog::Sentence:
0739             appSubtitle()->sentenceCase(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0740                                      dlg->lowerFirst(), dlg->selectedTextsTarget());
0741             break;
0742         }
0743     }
0744 }
0745 
0746 void
0747 Application::fixPunctuation()
0748 {
0749     static FixPunctuationDialog *dlg = new FixPunctuationDialog(m_mainWindow);
0750 
0751     if(dlg->exec() == QDialog::Accepted) {
0752         appSubtitle()->fixPunctuation(m_mainWindow->m_linesWidget->targetRanges(dlg->selectedLinesTarget()),
0753                                    dlg->spaces(), dlg->quotes(), dlg->englishI(),
0754                                    dlg->ellipsis(), dlg->selectedTextsTarget());
0755     }
0756 }
0757 
0758 void
0759 Application::openVideo(const QUrl &url)
0760 {
0761     if(url.scheme() != QStringLiteral("file"))
0762         return;
0763 
0764     VideoPlayer *videoPlayer = VideoPlayer::instance();
0765     videoPlayer->closeFile();
0766     videoPlayer->openFile(url.toLocalFile());
0767 }
0768 
0769 void
0770 Application::openVideo()
0771 {
0772     QFileDialog openDlg(m_mainWindow, i18n("Open Video"), QString(), buildMediaFilesFilter());
0773 
0774     openDlg.setModal(true);
0775     openDlg.selectUrl(m_lastVideoUrl);
0776 
0777     if(openDlg.exec() == QDialog::Accepted) {
0778         m_lastVideoUrl = openDlg.selectedUrls().first();
0779         openVideo(m_lastVideoUrl);
0780     }
0781 }
0782 
0783 void
0784 Application::toggleFullScreenMode()
0785 {
0786     setFullScreenMode(!m_mainWindow->m_playerWidget->fullScreenMode());
0787 }
0788 
0789 void
0790 Application::setFullScreenMode(bool enabled)
0791 {
0792     if(enabled != m_mainWindow->m_playerWidget->fullScreenMode()) {
0793         m_mainWindow->m_playerWidget->setFullScreenMode(enabled);
0794 
0795         KToggleAction *toggleFullScreenAction = static_cast<KToggleAction *>(action(ACT_TOGGLE_FULL_SCREEN));
0796         toggleFullScreenAction->setChecked(enabled);
0797 
0798         emit fullScreenModeChanged(enabled);
0799     }
0800 }
0801 
0802 void
0803 Application::seekBackward()
0804 {
0805     VideoPlayer *videoPlayer = VideoPlayer::instance();
0806     double position = videoPlayer->position() - SCConfig::seekJumpLength();
0807     m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
0808     videoPlayer->seek(position > 0.0 ? position : 0.0);
0809 }
0810 
0811 void
0812 Application::seekForward()
0813 {
0814     VideoPlayer *videoPlayer = VideoPlayer::instance();
0815     double position = videoPlayer->position() + SCConfig::seekJumpLength();
0816     m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
0817     videoPlayer->seek(position <= videoPlayer->duration() ? position : videoPlayer->duration());
0818 }
0819 
0820 void
0821 Application::stepBackward()
0822 {
0823     VideoPlayer::instance()->step(-SCConfig::stepJumpLength());
0824 }
0825 
0826 void
0827 Application::stepForward()
0828 {
0829     VideoPlayer::instance()->step(SCConfig::stepJumpLength());
0830 }
0831 
0832 void
0833 Application::seekToPrevLine()
0834 {
0835     int selectedIndex = m_mainWindow->m_linesWidget->firstSelectedIndex();
0836     if(selectedIndex < 0)
0837         return;
0838     SubtitleLine *currentLine = appSubtitle()->line(selectedIndex);
0839     if(currentLine) {
0840         SubtitleLine *prevLine = currentLine->prevLine();
0841         if(prevLine) {
0842             m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
0843             VideoPlayer::instance()->seek(prevLine->showTime().toSeconds() - SCConfig::jumpLineOffset() / 1000.0);
0844             m_mainWindow->m_linesWidget->setCurrentLine(prevLine);
0845             m_mainWindow->m_curLineWidget->setCurrentLine(prevLine);
0846         }
0847     }
0848 }
0849 
0850 void
0851 Application::playOnlyCurrentLine()
0852 {
0853     int selectedIndex = m_mainWindow->m_linesWidget->firstSelectedIndex();
0854     if(selectedIndex < 0)
0855         return;
0856     SubtitleLine *currentLine = appSubtitle()->line(selectedIndex);
0857     if(currentLine) {
0858         VideoPlayer *videoPlayer = VideoPlayer::instance();
0859         if(!videoPlayer->isPlaying())
0860             videoPlayer->play();
0861         videoPlayer->seek(currentLine->showTime().toSeconds() - SCConfig::jumpLineOffset() / 1000.0);
0862         m_mainWindow->m_playerWidget->pauseAfterPlayingLine(currentLine);
0863     }
0864 }
0865 
0866 void
0867 Application::seekToCurrentLine()
0868 {
0869     int selectedIndex = m_mainWindow->m_linesWidget->firstSelectedIndex();
0870     if(selectedIndex < 0)
0871         return;
0872     SubtitleLine *currentLine = appSubtitle()->line(selectedIndex);
0873     if(currentLine) {
0874         m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
0875         VideoPlayer::instance()->seek(currentLine->showTime().toSeconds() - SCConfig::jumpLineOffset() / 1000.0);
0876     }
0877 }
0878 
0879 void
0880 Application::seekToNextLine()
0881 {
0882     int selectedIndex = m_mainWindow->m_linesWidget->firstSelectedIndex();
0883     if(selectedIndex < 0)
0884         return;
0885     SubtitleLine *currentLine = appSubtitle()->line(selectedIndex);
0886     if(currentLine) {
0887         SubtitleLine *nextLine = currentLine->nextLine();
0888         if(nextLine) {
0889             m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
0890             VideoPlayer::instance()->seek(nextLine->showTime().toSeconds() - SCConfig::jumpLineOffset() / 1000.0);
0891             m_mainWindow->m_linesWidget->setCurrentLine(nextLine);
0892             m_mainWindow->m_curLineWidget->setCurrentLine(nextLine);
0893         }
0894     }
0895 }
0896 
0897 void
0898 Application::playrateIncrease()
0899 {
0900     VideoPlayer *videoPlayer = VideoPlayer::instance();
0901     const double speed = videoPlayer->playSpeed();
0902     videoPlayer->playSpeed(speed + (speed >= 2.0 ? .5 : .1));
0903 }
0904 
0905 void
0906 Application::playrateDecrease()
0907 {
0908     VideoPlayer *videoPlayer = VideoPlayer::instance();
0909     const double speed = videoPlayer->playSpeed();
0910     videoPlayer->playSpeed(speed - (speed > 2.0 ? .5 : .1));
0911 }
0912 
0913 void
0914 Application::setCurrentLineShowTimeFromVideo()
0915 {
0916     SubtitleLine *currentLine = m_mainWindow->m_linesWidget->currentLine();
0917     if(currentLine)
0918         currentLine->setShowTime(videoPosition(true));
0919 }
0920 
0921 void
0922 Application::setCurrentLineHideTimeFromVideo()
0923 {
0924     SubtitleLine *currentLine = m_mainWindow->m_linesWidget->currentLine();
0925     if(currentLine)
0926         currentLine->setHideTime(videoPosition(true));
0927 }
0928 
0929 void
0930 Application::setActiveSubtitleStream(int subtitleStream)
0931 {
0932     KSelectAction *activeSubtitleStreamAction = (KSelectAction *)action(ACT_SET_ACTIVE_SUBTITLE_STREAM);
0933     activeSubtitleStreamAction->setCurrentItem(subtitleStream);
0934 
0935     const bool translationSelected = bool(subtitleStream);
0936     m_mainWindow->m_playerWidget->setShowTranslation(translationSelected);
0937     m_mainWindow->m_waveformWidget->setShowTranslation(translationSelected);
0938     m_speller->setUseTranslation(translationSelected);
0939 }
0940 
0941 
0942 void
0943 Application::anchorToggle()
0944 {
0945     appSubtitle()->toggleLineAnchor(m_mainWindow->m_linesWidget->currentLine());
0946 }
0947 
0948 void
0949 Application::anchorRemoveAll()
0950 {
0951     appSubtitle()->removeAllAnchors();
0952 }
0953 
0954 void
0955 Application::shiftToVideoPosition()
0956 {
0957     SubtitleLine *currentLine = m_mainWindow->m_linesWidget->currentLine();
0958     if(currentLine)
0959         appSubtitle()->shiftLines(Range::full(), videoPosition(true).toMillis() - currentLine->showTime().toMillis());
0960 }
0961 
0962 /// END ACTION HANDLERS
0963 
0964 void
0965 Application::updateTitle()
0966 {
0967     if(appSubtitle()) {
0968         if(m_translationMode) {
0969             const QString caption = QStringLiteral("%1%2 | %3").arg(
0970                 m_subtitleUrl.isEmpty() ? i18n("Untitled") : m_subtitleFileName,
0971                 appSubtitle()->isPrimaryDirty() ? $(" *") : QString(),
0972                 m_subtitleTrUrl.isEmpty() ? i18n("Untitled Translation") : m_subtitleTrFileName
0973             );
0974             m_mainWindow->setCaption(caption, appSubtitle()->isSecondaryDirty());
0975         } else {
0976             m_mainWindow->setCaption(
0977                         m_subtitleUrl.isEmpty() ? i18n("Untitled") : (m_subtitleUrl.isLocalFile() ? m_subtitleUrl.toLocalFile() : m_subtitleUrl.toString(QUrl::PreferLocalFile)),
0978                         appSubtitle()->isPrimaryDirty());
0979         }
0980     } else {
0981         m_mainWindow->setCaption(QString());
0982     }
0983 }
0984 
0985 void
0986 Application::onWaveformDoubleClicked(Time time)
0987 {
0988     VideoPlayer *videoPlayer = VideoPlayer::instance();
0989     if(videoPlayer->state() == VideoPlayer::Stopped)
0990         videoPlayer->play();
0991 
0992     m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
0993     videoPlayer->seek(time.toSeconds());
0994 }
0995 
0996 void
0997 Application::onWaveformMiddleMouse(Time time)
0998 {
0999     m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
1000     VideoPlayer::instance()->seek(time.toSeconds());
1001 }
1002 
1003 void
1004 Application::onLineDoubleClicked(SubtitleLine *line)
1005 {
1006     VideoPlayer *videoPlayer = VideoPlayer::instance();
1007     if(videoPlayer->state() == VideoPlayer::Stopped)
1008         videoPlayer->play();
1009 
1010     int mseconds = line->showTime().toMillis() - SCConfig::seekOffsetOnDoubleClick();
1011     m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
1012     videoPlayer->seek(mseconds > 0 ? mseconds / 1000.0 : 0.0);
1013 
1014     if(videoPlayer->state() != VideoPlayer::Playing && SCConfig::unpauseOnDoubleClick())
1015         videoPlayer->play();
1016 
1017     m_mainWindow->m_waveformWidget->setScrollPosition(line->showTime().toMillis());
1018 }
1019 
1020 void
1021 Application::onHighlightLine(SubtitleLine *line, bool primary, int firstIndex, int lastIndex)
1022 {
1023     if(m_mainWindow->m_playerWidget->fullScreenMode()) {
1024         if(m_lastFoundLine != line) {
1025             m_lastFoundLine = line;
1026 
1027             m_mainWindow->m_playerWidget->pauseAfterPlayingLine(nullptr);
1028             VideoPlayer::instance()->seek(line->showTime().toSeconds());
1029         }
1030     } else {
1031         m_mainWindow->m_linesWidget->setCurrentLine(line, true);
1032 
1033         if(firstIndex >= 0 && lastIndex >= 0) {
1034             if(primary)
1035                 m_mainWindow->m_curLineWidget->selectPrimaryText(firstIndex, lastIndex);
1036             else
1037                 m_mainWindow->m_curLineWidget->selectTranslationText(firstIndex, lastIndex);
1038         }
1039     }
1040 }
1041 
1042 void
1043 Application::onPlayingLineChanged(SubtitleLine *line)
1044 {
1045     m_mainWindow->m_linesWidget->setPlayingLine(line);
1046 
1047     if(m_linkCurrentLineToPosition)
1048         m_mainWindow->m_linesWidget->setCurrentLine(line, true);
1049 }
1050 
1051 void
1052 Application::onLinkCurrentLineToVideoToggled(bool value)
1053 {
1054     if(m_linkCurrentLineToPosition != value) {
1055         m_linkCurrentLineToPosition = value;
1056 
1057         if(m_linkCurrentLineToPosition)
1058             m_mainWindow->m_linesWidget->setCurrentLine(m_mainWindow->m_playerWidget->playingLine(), true);
1059     }
1060 }
1061 
1062 void
1063 Application::onPlayerFileOpened(const QString &filePath)
1064 {
1065     m_recentVideosAction->addUrl(QUrl::fromLocalFile(filePath));
1066 }
1067 
1068 void
1069 Application::onPlayerPlaying()
1070 {
1071     QAction *playPauseAction = action(ACT_PLAY_PAUSE);
1072     playPauseAction->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause")));
1073     playPauseAction->setText(i18n("Pause"));
1074 }
1075 
1076 void
1077 Application::onPlayerPaused()
1078 {
1079     QAction *playPauseAction = action(ACT_PLAY_PAUSE);
1080     playPauseAction->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
1081     playPauseAction->setText(i18n("Play"));
1082 }
1083 
1084 void
1085 Application::onPlayerStopped()
1086 {
1087     onPlayerPaused();
1088 }
1089 
1090 void
1091 Application::onPlayerTextStreamsChanged(const QStringList &textStreams)
1092 {
1093     QAction *demuxTextStreamAction = (KSelectAction *)action(ACT_DEMUX_TEXT_STREAM);
1094     QMenu *menu = demuxTextStreamAction->menu();
1095     menu->clear();
1096     int i = 0;
1097     foreach(const QString &textStream, textStreams)
1098         menu->addAction(textStream)->setData(QVariant::fromValue<int>(i++));
1099     demuxTextStreamAction->setEnabled(i > 0);
1100 }
1101 
1102 void
1103 Application::onPlayerAudioStreamsChanged(const QStringList &audioStreams)
1104 {
1105     KSelectAction *activeAudioStreamAction = (KSelectAction *)action(ACT_SET_ACTIVE_AUDIO_STREAM);
1106     activeAudioStreamAction->setItems(audioStreams);
1107 
1108     QAction *speechImportStreamAction = (KSelectAction *)action(ACT_ASR_IMPORT_AUDIO_STREAM);
1109     QMenu *speechImportStreamActionMenu = speechImportStreamAction->menu();
1110     speechImportStreamActionMenu->clear();
1111     int i = 0;
1112     foreach(const QString &audioStream, audioStreams)
1113         speechImportStreamActionMenu->addAction(audioStream)->setData(QVariant::fromValue<int>(i++));
1114     speechImportStreamAction->setEnabled(i > 0);
1115 }
1116 
1117 void
1118 Application::onPlayerActiveAudioStreamChanged(int audioStream)
1119 {
1120     KSelectAction *activeAudioStreamAction = (KSelectAction *)action(ACT_SET_ACTIVE_AUDIO_STREAM);
1121     if(audioStream >= 0) {
1122         activeAudioStreamAction->setCurrentItem(audioStream);
1123         m_mainWindow->m_waveformWidget->setAudioStream(VideoPlayer::instance()->filePath(), audioStream);
1124     } else {
1125         m_mainWindow->m_waveformWidget->setNullAudioStream(VideoPlayer::instance()->duration() * 1000);
1126     }
1127 }
1128 
1129 void
1130 Application::onPlayerMuteChanged(bool muted)
1131 {
1132     KToggleAction *toggleMutedAction = (KToggleAction *)action(ACT_TOGGLE_MUTED);
1133     toggleMutedAction->setChecked(muted);
1134 }
1135 
1136 void
1137 Application::updateActionTexts()
1138 {
1139     const int shiftAmount = SCConfig::linesQuickShiftAmount();
1140     const int jumpLength = SCConfig::seekJumpLength();
1141 
1142     action(ACT_SEEK_BACKWARD)->setStatusTip(i18np("Seek backwards 1 second", "Seek backwards %1 seconds", jumpLength));
1143     action(ACT_SEEK_FORWARD)->setStatusTip(i18np("Seek forwards 1 second", "Seek forwards %1 seconds", jumpLength));
1144 
1145     QAction *shiftSelectedLinesFwdAction = action(ACT_SHIFT_SELECTED_LINES_FORWARDS);
1146     shiftSelectedLinesFwdAction->setText(i18np("Shift %2%1 millisecond", "Shift %2%1 milliseconds", shiftAmount, "+"));
1147     shiftSelectedLinesFwdAction->setStatusTip(i18np("Shift selected lines %2%1 millisecond", "Shift selected lines %2%1 milliseconds", shiftAmount, "+"));
1148 
1149     QAction *shiftSelectedLinesBwdAction = action(ACT_SHIFT_SELECTED_LINES_BACKWARDS);
1150     shiftSelectedLinesBwdAction->setText(i18np("Shift %2%1 millisecond", "Shift %2%1 milliseconds", shiftAmount, "-"));
1151     shiftSelectedLinesBwdAction->setStatusTip(i18np("Shift selected lines %2%1 millisecond", "Shift selected lines %2%1 milliseconds", shiftAmount, "-"));
1152 }
1153 
1154 void
1155 Application::onConfigChanged()
1156 {
1157     updateActionTexts();
1158 }
1159