File indexing completed on 2024-12-22 04:40:13
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 "playerwidget.h" 0009 0010 #include "appglobal.h" 0011 #include "application.h" 0012 #include "actions/useractionnames.h" 0013 #include "core/richtext/richdocument.h" 0014 #include "videoplayer/videoplayer.h" 0015 #include "widgets/layeredwidget.h" 0016 #include "widgets/attachablewidget.h" 0017 #include "widgets/pointingslider.h" 0018 #include "widgets/timeedit.h" 0019 0020 #include <QEvent> 0021 #include <QDropEvent> 0022 #include <QMimeData> 0023 #include <QKeyEvent> 0024 0025 #include <QMenu> 0026 #include <QPushButton> 0027 #include <QCursor> 0028 #include <QLabel> 0029 #include <QToolButton> 0030 #include <QGroupBox> 0031 #include <QGridLayout> 0032 0033 #include <KConfigGroup> 0034 #include <KMessageBox> 0035 #include <KLocalizedString> 0036 0037 using namespace SubtitleComposer; 0038 0039 #define HIDE_MOUSE_MSECS 1000 0040 #define UNKNOWN_LENGTH_STRING (" / " + Time().toString(false) + ' ') 0041 0042 PlayerWidget::PlayerWidget(QWidget *parent) : 0043 QWidget(parent), 0044 m_subtitle(0), 0045 m_translationMode(false), 0046 m_showTranslation(false), 0047 m_pauseAfterPlayingLine(nullptr), 0048 m_fullScreenTID(0), 0049 m_fullScreenMode(false), 0050 m_lengthString(UNKNOWN_LENGTH_STRING), 0051 m_showPositionTimeEdit(SCConfig::showPositionTimeEdit()) 0052 { 0053 m_layeredWidget = new LayeredWidget(this); 0054 m_layeredWidget->setAcceptDrops(true); 0055 m_layeredWidget->installEventFilter(this); 0056 m_layeredWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0057 0058 m_seekSlider = new PointingSlider(Qt::Horizontal, this); 0059 m_seekSlider->setTickPosition(QSlider::NoTicks); 0060 m_seekSlider->setMinimum(0); 0061 m_seekSlider->setMaximum(1000); 0062 m_seekSlider->setPageStep(10); 0063 m_seekSlider->setFocusPolicy(Qt::NoFocus); 0064 0065 m_infoControlsGroupBox = new QWidget(this); 0066 m_infoControlsGroupBox->setAcceptDrops(true); 0067 m_infoControlsGroupBox->installEventFilter(this); 0068 0069 QLabel *positionTagLabel = new QLabel(m_infoControlsGroupBox); 0070 positionTagLabel->setText(i18n("<b>Position</b>")); 0071 positionTagLabel->installEventFilter(this); 0072 0073 m_positionLabel = new QLabel(m_infoControlsGroupBox); 0074 m_positionEdit = new TimeEdit(m_infoControlsGroupBox); 0075 m_positionEdit->setFocusPolicy(Qt::NoFocus); 0076 0077 QLabel *lengthTagLabel = new QLabel(m_infoControlsGroupBox); 0078 lengthTagLabel->setText(i18n("<b>Length</b>")); 0079 lengthTagLabel->installEventFilter(this); 0080 m_lengthLabel = new QLabel(m_infoControlsGroupBox); 0081 0082 QLabel *fpsTagLabel = new QLabel(m_infoControlsGroupBox); 0083 fpsTagLabel->setText(i18n("<b>FPS</b>")); 0084 fpsTagLabel->installEventFilter(this); 0085 m_fpsLabel = new QLabel(m_infoControlsGroupBox); 0086 m_fpsLabel->setMinimumWidth(m_positionEdit->sizeHint().width()); // sets the minimum width for the whole group 0087 0088 QLabel *rateTagLabel = new QLabel(m_infoControlsGroupBox); 0089 rateTagLabel->setText(i18n("<b>Playback Rate</b>")); 0090 rateTagLabel->installEventFilter(this); 0091 m_rateLabel = new QLabel(m_infoControlsGroupBox); 0092 0093 m_volumeSlider = new PointingSlider(Qt::Vertical, this); 0094 m_volumeSlider->setFocusPolicy(Qt::NoFocus); 0095 m_volumeSlider->setTickPosition(QSlider::NoTicks); 0096 m_volumeSlider->setPageStep(5); 0097 m_volumeSlider->setMinimum(0); 0098 m_volumeSlider->setMaximum(100); 0099 m_volumeSlider->setFocusPolicy(Qt::NoFocus); 0100 0101 QGridLayout *videoControlsLayout = new QGridLayout(); 0102 videoControlsLayout->setContentsMargins(0, 0, 0, 0); 0103 videoControlsLayout->setSpacing(2); 0104 videoControlsLayout->addWidget(createToolButton(this, ACT_PLAY_PAUSE, 16), 0, 0); 0105 videoControlsLayout->addWidget(createToolButton(this, ACT_STOP, 16), 0, 1); 0106 videoControlsLayout->addWidget(createToolButton(this, ACT_SEEK_BACKWARD, 16), 0, 2); 0107 videoControlsLayout->addWidget(createToolButton(this, ACT_SEEK_FORWARD, 16), 0, 3); 0108 videoControlsLayout->addItem(new QSpacerItem(2, 2), 0, 4); 0109 videoControlsLayout->addWidget(createToolButton(this, ACT_SEEK_TO_PREVIOUS_LINE, 16), 0, 5); 0110 videoControlsLayout->addWidget(createToolButton(this, ACT_SEEK_TO_NEXT_LINE, 16), 0, 6); 0111 videoControlsLayout->addItem(new QSpacerItem(2, 2), 0, 7); 0112 videoControlsLayout->addWidget(createToolButton(this, ACT_SET_CURRENT_LINE_SHOW_TIME, 16), 0, 8); 0113 videoControlsLayout->addWidget(createToolButton(this, ACT_SET_CURRENT_LINE_HIDE_TIME, 16), 0, 9); 0114 videoControlsLayout->addItem(new QSpacerItem(2, 2), 0, 10); 0115 videoControlsLayout->addWidget(createToolButton(this, ACT_CURRENT_LINE_FOLLOWS_VIDEO, 16), 0, 11); 0116 videoControlsLayout->addItem(new QSpacerItem(2, 2), 0, 12); 0117 videoControlsLayout->addWidget(createToolButton(this, ACT_PLAY_RATE_DECREASE, 16), 0, 13); 0118 videoControlsLayout->addWidget(createToolButton(this, ACT_PLAY_RATE_INCREASE, 16), 0, 14); 0119 videoControlsLayout->addWidget(m_seekSlider, 0, 15); 0120 0121 QGridLayout *audioControlsLayout = new QGridLayout(); 0122 audioControlsLayout->setContentsMargins(0, 0, 0, 0); 0123 audioControlsLayout->addWidget(createToolButton(this, ACT_TOGGLE_MUTED, 16), 0, 0, Qt::AlignHCenter); 0124 audioControlsLayout->addWidget(m_volumeSlider, 1, 0, Qt::AlignHCenter); 0125 0126 QGridLayout *infoControlsLayout = new QGridLayout(m_infoControlsGroupBox); 0127 infoControlsLayout->setSpacing(5); 0128 infoControlsLayout->addWidget(fpsTagLabel, 0, 0); 0129 infoControlsLayout->addWidget(m_fpsLabel, 1, 0); 0130 infoControlsLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 2, 0); 0131 infoControlsLayout->addWidget(rateTagLabel, 3, 0); 0132 infoControlsLayout->addWidget(m_rateLabel, 4, 0); 0133 infoControlsLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 5, 0); 0134 infoControlsLayout->addWidget(lengthTagLabel, 6, 0); 0135 infoControlsLayout->addWidget(m_lengthLabel, 7, 0); 0136 infoControlsLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 8, 0); 0137 infoControlsLayout->addWidget(positionTagLabel, 9, 0); 0138 infoControlsLayout->addWidget(m_positionLabel, 10, 0); 0139 infoControlsLayout->addWidget(m_positionEdit, 11, 0); 0140 0141 m_mainLayout = new QGridLayout(this); 0142 m_mainLayout->setContentsMargins(0, 0, 0, 0); 0143 m_mainLayout->setSpacing(5); 0144 m_mainLayout->addWidget(m_infoControlsGroupBox, 0, 0, 2, 1); 0145 m_mainLayout->addWidget(m_layeredWidget, 0, 1); 0146 m_mainLayout->addLayout(audioControlsLayout, 0, 2); 0147 m_mainLayout->addLayout(videoControlsLayout, 1, 1); 0148 m_mainLayout->addWidget(createToolButton(this, ACT_TOGGLE_FULL_SCREEN, 16), 1, 2); 0149 0150 m_fullScreenControls = new AttachableWidget(AttachableWidget::Bottom, 4); 0151 m_fullScreenControls->setAutoFillBackground(true); 0152 m_layeredWidget->setWidgetMode(m_fullScreenControls, LayeredWidget::IgnoreResize); 0153 0154 m_fsSeekSlider = new PointingSlider(Qt::Horizontal, m_fullScreenControls); 0155 m_fsSeekSlider->setTickPosition(QSlider::NoTicks); 0156 m_fsSeekSlider->setMinimum(0); 0157 m_fsSeekSlider->setMaximum(1000); 0158 m_fsSeekSlider->setPageStep(10); 0159 0160 m_fsVolumeSlider = new PointingSlider(Qt::Horizontal, m_fullScreenControls); 0161 m_fsVolumeSlider->setFocusPolicy(Qt::NoFocus); 0162 m_fsVolumeSlider->setTickPosition(QSlider::NoTicks); 0163 m_fsVolumeSlider->setPageStep(5); 0164 m_fsVolumeSlider->setMinimum(0); 0165 m_fsVolumeSlider->setMaximum(100); 0166 0167 m_fsPositionLabel = new QLabel(m_fullScreenControls); 0168 QPalette fsPositionPalette; 0169 fsPositionPalette.setColor(m_fsPositionLabel->backgroundRole(), Qt::black); 0170 fsPositionPalette.setColor(m_fsPositionLabel->foregroundRole(), Qt::white); 0171 m_fsPositionLabel->setPalette(fsPositionPalette); 0172 m_fsPositionLabel->setAutoFillBackground(true); 0173 m_fsPositionLabel->setFrameShape(QFrame::Panel); 0174 m_fsPositionLabel->setText(Time().toString(false) + " / " + Time().toString(false)); 0175 m_fsPositionLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 0176 m_fsPositionLabel->adjustSize(); 0177 m_fsPositionLabel->setMinimumWidth(m_fsPositionLabel->width()); 0178 0179 QHBoxLayout *fullScreenControlsLayout = new QHBoxLayout(m_fullScreenControls); 0180 fullScreenControlsLayout->setContentsMargins(0, 0, 0, 0); 0181 fullScreenControlsLayout->setSpacing(0); 0182 0183 const int FS_BUTTON_SIZE = 32; 0184 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_PLAY_PAUSE, FS_BUTTON_SIZE)); 0185 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_STOP, FS_BUTTON_SIZE)); 0186 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_SEEK_BACKWARD, FS_BUTTON_SIZE)); 0187 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_SEEK_FORWARD, FS_BUTTON_SIZE)); 0188 fullScreenControlsLayout->addSpacing(3); 0189 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_SEEK_TO_PREVIOUS_LINE, FS_BUTTON_SIZE)); 0190 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_SEEK_TO_NEXT_LINE, FS_BUTTON_SIZE)); 0191 fullScreenControlsLayout->addSpacing(3); 0192 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_PLAY_RATE_DECREASE, FS_BUTTON_SIZE)); 0193 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_PLAY_RATE_INCREASE, FS_BUTTON_SIZE)); 0194 fullScreenControlsLayout->addSpacing(3); 0195 fullScreenControlsLayout->addWidget(m_fsSeekSlider, 9); 0196 fullScreenControlsLayout->addWidget(m_fsPositionLabel); 0197 fullScreenControlsLayout->addWidget(m_fsVolumeSlider, 2); 0198 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_TOGGLE_MUTED, FS_BUTTON_SIZE)); 0199 fullScreenControlsLayout->addWidget(createToolButton(m_fullScreenControls, ACT_TOGGLE_FULL_SCREEN, FS_BUTTON_SIZE)); 0200 m_fullScreenControls->adjustSize(); 0201 0202 connect(m_volumeSlider, &QAbstractSlider::valueChanged, this, &PlayerWidget::onVolumeSliderMoved); 0203 connect(m_fsVolumeSlider, &QAbstractSlider::valueChanged, this, &PlayerWidget::onVolumeSliderMoved); 0204 0205 connect(m_seekSlider, &QAbstractSlider::valueChanged, this, &PlayerWidget::onSeekSliderMoved); 0206 connect(m_fsSeekSlider, &QAbstractSlider::valueChanged, this, &PlayerWidget::onSeekSliderMoved); 0207 0208 connect(m_positionEdit, &TimeEdit::valueChanged, this, &PlayerWidget::onPositionEditValueChanged); 0209 connect(m_positionEdit, &TimeEdit::valueEntered, this, &PlayerWidget::onPositionEditValueChanged); 0210 0211 connect(SCConfig::self(), &KCoreConfigSkeleton::configChanged, this, &PlayerWidget::onConfigChanged); 0212 0213 VideoPlayer *videoPlayer = VideoPlayer::instance(); 0214 videoPlayer->init(m_layeredWidget); 0215 0216 connect(videoPlayer, &VideoPlayer::fileOpened, this, &PlayerWidget::onPlayerFileOpened); 0217 connect(videoPlayer, &VideoPlayer::fileOpenError, this, &PlayerWidget::onPlayerFileOpenError); 0218 connect(videoPlayer, &VideoPlayer::fileClosed, this, &PlayerWidget::onPlayerFileClosed); 0219 connect(videoPlayer, &VideoPlayer::playbackError, this, &PlayerWidget::onPlayerPlaybackError); 0220 connect(videoPlayer, &VideoPlayer::playing, this, &PlayerWidget::onPlayerPlaying); 0221 connect(videoPlayer, &VideoPlayer::stopped, this, &PlayerWidget::onPlayerStopped); 0222 connect(videoPlayer, &VideoPlayer::positionChanged, this, &PlayerWidget::onPlayerPositionChanged); 0223 connect(videoPlayer, &VideoPlayer::durationChanged, this, &PlayerWidget::onPlayerLengthChanged); 0224 connect(videoPlayer, &VideoPlayer::fpsChanged, this, &PlayerWidget::onPlayerFramesPerSecondChanged); 0225 connect(videoPlayer, &VideoPlayer::playSpeedChanged, this, &PlayerWidget::onPlayerPlaybackRateChanged); 0226 connect(videoPlayer, &VideoPlayer::volumeChanged, this, &PlayerWidget::onPlayerVolumeChanged); 0227 connect(videoPlayer, &VideoPlayer::muteChanged, m_fsVolumeSlider, &QWidget::setDisabled); 0228 connect(videoPlayer, &VideoPlayer::muteChanged, m_volumeSlider, &QWidget::setDisabled); 0229 0230 connect(videoPlayer, &VideoPlayer::leftClicked, this, &PlayerWidget::onPlayerLeftClicked); 0231 connect(videoPlayer, &VideoPlayer::rightClicked, this, &PlayerWidget::onPlayerRightClicked); 0232 connect(videoPlayer, &VideoPlayer::doubleClicked, this, &PlayerWidget::onPlayerDoubleClicked); 0233 0234 onPlayerFileClosed(); 0235 onConfigChanged(); // initializes the font 0236 0237 setFullScreenMode(m_fullScreenMode); 0238 0239 connect(app(), &Application::actionsReady, this, [this](){ 0240 toolButton(this, ACT_STOP)->setDefaultAction(app()->action(ACT_STOP)); 0241 toolButton(this, ACT_PLAY_PAUSE)->setDefaultAction(app()->action(ACT_PLAY_PAUSE)); 0242 toolButton(this, ACT_SEEK_BACKWARD)->setDefaultAction(app()->action(ACT_SEEK_BACKWARD)); 0243 toolButton(this, ACT_SEEK_FORWARD)->setDefaultAction(app()->action(ACT_SEEK_FORWARD)); 0244 toolButton(this, ACT_SEEK_TO_PREVIOUS_LINE)->setDefaultAction(app()->action(ACT_SEEK_TO_PREVIOUS_LINE)); 0245 toolButton(this, ACT_SEEK_TO_NEXT_LINE)->setDefaultAction(app()->action(ACT_SEEK_TO_NEXT_LINE)); 0246 toolButton(this, ACT_SET_CURRENT_LINE_SHOW_TIME)->setDefaultAction(app()->action(ACT_SET_CURRENT_LINE_SHOW_TIME)); 0247 toolButton(this, ACT_SET_CURRENT_LINE_HIDE_TIME)->setDefaultAction(app()->action(ACT_SET_CURRENT_LINE_HIDE_TIME)); 0248 toolButton(this, ACT_CURRENT_LINE_FOLLOWS_VIDEO)->setDefaultAction(app()->action(ACT_CURRENT_LINE_FOLLOWS_VIDEO)); 0249 toolButton(this, ACT_TOGGLE_MUTED)->setDefaultAction(app()->action(ACT_TOGGLE_MUTED)); 0250 toolButton(this, ACT_TOGGLE_FULL_SCREEN)->setDefaultAction(app()->action(ACT_TOGGLE_FULL_SCREEN)); 0251 toolButton(this, ACT_PLAY_RATE_DECREASE)->setDefaultAction(app()->action(ACT_PLAY_RATE_DECREASE)); 0252 toolButton(this, ACT_PLAY_RATE_INCREASE)->setDefaultAction(app()->action(ACT_PLAY_RATE_INCREASE)); 0253 0254 toolButton(m_fullScreenControls, ACT_STOP)->setDefaultAction(app()->action(ACT_STOP)); 0255 toolButton(m_fullScreenControls, ACT_PLAY_PAUSE)->setDefaultAction(app()->action(ACT_PLAY_PAUSE)); 0256 toolButton(m_fullScreenControls, ACT_SEEK_BACKWARD)->setDefaultAction(app()->action(ACT_SEEK_BACKWARD)); 0257 toolButton(m_fullScreenControls, ACT_SEEK_FORWARD)->setDefaultAction(app()->action(ACT_SEEK_FORWARD)); 0258 toolButton(m_fullScreenControls, ACT_SEEK_TO_PREVIOUS_LINE)->setDefaultAction(app()->action(ACT_SEEK_TO_PREVIOUS_LINE)); 0259 toolButton(m_fullScreenControls, ACT_SEEK_TO_NEXT_LINE)->setDefaultAction(app()->action(ACT_SEEK_TO_NEXT_LINE)); 0260 toolButton(m_fullScreenControls, ACT_TOGGLE_MUTED)->setDefaultAction(app()->action(ACT_TOGGLE_MUTED)); 0261 toolButton(m_fullScreenControls, ACT_TOGGLE_FULL_SCREEN)->setDefaultAction(app()->action(ACT_TOGGLE_FULL_SCREEN)); 0262 toolButton(m_fullScreenControls, ACT_PLAY_RATE_DECREASE)->setDefaultAction(app()->action(ACT_PLAY_RATE_DECREASE)); 0263 toolButton(m_fullScreenControls, ACT_PLAY_RATE_INCREASE)->setDefaultAction(app()->action(ACT_PLAY_RATE_INCREASE)); 0264 }); 0265 } 0266 0267 PlayerWidget::~PlayerWidget() 0268 { 0269 m_fullScreenControls->deleteLater(); 0270 } 0271 0272 QWidget * 0273 PlayerWidget::infoSidebarWidget() 0274 { 0275 return m_infoControlsGroupBox; 0276 } 0277 0278 QToolButton * 0279 PlayerWidget::toolButton(QWidget *parent, const char *name) 0280 { 0281 return parent->findChild<QToolButton *>(name); 0282 } 0283 0284 QToolButton * 0285 PlayerWidget::createToolButton(QWidget *parent, const char *name, int size) 0286 { 0287 QToolButton *toolButton = new QToolButton(parent); 0288 toolButton->setObjectName(name); 0289 toolButton->setMinimumSize(size, size); 0290 toolButton->setIconSize(size >= 32 ? QSize(size - 6, size - 6) : QSize(size, size)); 0291 toolButton->setAutoRaise(true); 0292 toolButton->setFocusPolicy(Qt::NoFocus); 0293 return toolButton; 0294 } 0295 0296 void 0297 PlayerWidget::loadConfig() 0298 { 0299 onPlayerVolumeChanged(VideoPlayer::instance()->volume()); 0300 } 0301 0302 void 0303 PlayerWidget::saveConfig() 0304 {} 0305 0306 void 0307 PlayerWidget::setFullScreenMode(bool fullScreenMode) 0308 { 0309 if(m_fullScreenMode == fullScreenMode) 0310 return; 0311 0312 m_fullScreenMode = fullScreenMode; 0313 0314 if(m_fullScreenMode) { 0315 window()->hide(); 0316 0317 // Move m_layeredWidget to a temporary widget which will be 0318 // displayed in full screen mode. 0319 // Can not call showFullScreen() on m_layeredWidget directly 0320 // because restoring the previous state is buggy under 0321 // some desktop environments / window managers. 0322 0323 auto *fullScreenWidget = new QWidget(); 0324 fullScreenWidget->installEventFilter(this); 0325 auto *fullScreenLayout = new QHBoxLayout(); 0326 fullScreenLayout->setContentsMargins(0, 0, 0, 0); 0327 fullScreenWidget->setLayout(fullScreenLayout); 0328 m_layeredWidget->setParent(fullScreenWidget); 0329 fullScreenLayout->addWidget(m_layeredWidget); 0330 fullScreenWidget->showFullScreen(); 0331 0332 m_layeredWidget->unsetCursor(); 0333 m_layeredWidget->setMouseTracking(true); 0334 m_fullScreenControls->attach(m_layeredWidget); 0335 0336 m_fullScreenTID = startTimer(HIDE_MOUSE_MSECS); 0337 0338 VideoPlayer::instance()->subtitleOverlay().setBottomPadding(m_fullScreenControls->height()); 0339 } else { 0340 if(m_fullScreenTID) { 0341 killTimer(m_fullScreenTID); 0342 m_fullScreenTID = 0; 0343 } 0344 0345 m_fullScreenControls->dettach(); 0346 m_layeredWidget->setMouseTracking(false); 0347 m_layeredWidget->unsetCursor(); 0348 0349 // delete temporary parent widget later and set this as parent again 0350 m_layeredWidget->parent()->deleteLater(); 0351 m_layeredWidget->setParent(this); 0352 0353 m_mainLayout->addWidget(m_layeredWidget, 0, 1); 0354 0355 VideoPlayer::instance()->subtitleOverlay().setBottomPadding(0); 0356 0357 window()->show(); 0358 } 0359 } 0360 0361 void 0362 PlayerWidget::timerEvent(QTimerEvent *event) 0363 { 0364 Q_UNUSED(event); 0365 if(m_currentCursorPos != m_savedCursorPos) { 0366 m_savedCursorPos = m_currentCursorPos; 0367 } else if(!m_fullScreenControls->underMouse()) { 0368 if(m_layeredWidget->cursor().shape() != Qt::BlankCursor) 0369 m_layeredWidget->setCursor(QCursor(Qt::BlankCursor)); 0370 if(m_fullScreenControls->isAttached()) 0371 m_fullScreenControls->toggleVisible(false); 0372 } 0373 } 0374 0375 bool 0376 PlayerWidget::eventFilter(QObject *object, QEvent *event) 0377 { 0378 if(object == m_layeredWidget) { 0379 switch(event->type()) { 0380 case QEvent::DragEnter: 0381 case QEvent::Drop: 0382 foreach(const QUrl &url, static_cast<QDropEvent *>(event)->mimeData()->urls()) { 0383 if(url.scheme() == QLatin1String("file")) { 0384 event->accept(); 0385 if(event->type() == QEvent::Drop) 0386 app()->openVideo(url); 0387 return true; // eat event 0388 } 0389 } 0390 event->ignore(); 0391 return true; 0392 0393 case QEvent::DragMove: 0394 return true; // eat event 0395 0396 case QEvent::KeyPress: { 0397 // NOTE: when on full screen mode, the keyboard input is received but 0398 // for some reason it doesn't trigger the correct actions automatically 0399 // so we process the event and handle the issue ourselves. 0400 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); 0401 if(m_fullScreenMode && keyEvent->key() == Qt::Key_Escape) { 0402 app()->action(ACT_TOGGLE_FULL_SCREEN)->trigger(); 0403 return true; 0404 } 0405 return app()->triggerAction(QKeySequence((keyEvent->modifiers() & ~Qt::KeypadModifier) | keyEvent->key())); 0406 } 0407 0408 case QEvent::MouseMove: { 0409 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); 0410 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0411 if(mouseEvent->globalPos() != m_currentCursorPos) { 0412 m_currentCursorPos = mouseEvent->globalPos(); 0413 #else 0414 if(mouseEvent->globalPosition() != m_currentCursorPos) { 0415 m_currentCursorPos = mouseEvent->globalPosition(); 0416 #endif 0417 if(m_layeredWidget->cursor().shape() == Qt::BlankCursor) 0418 m_layeredWidget->unsetCursor(); 0419 if(m_fullScreenControls->isAttached()) 0420 m_fullScreenControls->toggleVisible(true); 0421 } 0422 break; 0423 } 0424 0425 default: 0426 break; 0427 } 0428 } else if(object == m_infoControlsGroupBox || object->parent() == m_infoControlsGroupBox) { 0429 if(event->type() != QEvent::MouseButtonRelease) 0430 return false; 0431 0432 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); 0433 0434 if(mouseEvent->button() != Qt::RightButton) 0435 return false; 0436 0437 QMenu menu; 0438 QAction *action = menu.addAction(i18n("Show editable position control")); 0439 action->setCheckable(true); 0440 action->setChecked(SCConfig::showPositionTimeEdit()); 0441 0442 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0443 if(menu.exec(mouseEvent->globalPos()) == action) 0444 #else 0445 if(menu.exec(mouseEvent->globalPosition().toPoint()) == action) 0446 #endif 0447 SCConfig::setShowPositionTimeEdit(!SCConfig::showPositionTimeEdit()); 0448 0449 return true; // eat event 0450 } else if(m_fullScreenMode && object == m_layeredWidget->parentWidget() && event->type() == QEvent::Close) { 0451 app()->action(ACT_TOGGLE_FULL_SCREEN)->trigger(); 0452 event->ignore(); 0453 return true; // eat event 0454 } 0455 0456 return false; 0457 } 0458 0459 void 0460 PlayerWidget::setSubtitle(Subtitle *subtitle) 0461 { 0462 if(m_subtitle) { 0463 disconnect(m_subtitle.constData(), &Subtitle::linesInserted, this, &PlayerWidget::setPlayingLineFromVideo); 0464 disconnect(m_subtitle.constData(), &Subtitle::linesRemoved, this, &PlayerWidget::setPlayingLineFromVideo); 0465 0466 m_subtitle = nullptr; 0467 0468 setPlayingLine(nullptr); 0469 } 0470 0471 m_subtitle = subtitle; 0472 0473 if(m_subtitle) { 0474 connect(m_subtitle.constData(), &Subtitle::linesInserted, this, &PlayerWidget::setPlayingLineFromVideo); 0475 connect(m_subtitle.constData(), &Subtitle::linesRemoved, this, &PlayerWidget::setPlayingLineFromVideo); 0476 } 0477 } 0478 0479 void 0480 PlayerWidget::setTranslationMode(bool enabled) 0481 { 0482 m_translationMode = enabled; 0483 0484 if(!m_translationMode) 0485 setShowTranslation(false); 0486 } 0487 0488 void 0489 PlayerWidget::setShowTranslation(bool showTranslation) 0490 { 0491 if(m_showTranslation == showTranslation) 0492 return; 0493 0494 m_showTranslation = showTranslation; 0495 0496 setPlayingLine(nullptr); 0497 setPlayingLineFromVideo(); 0498 } 0499 0500 void 0501 PlayerWidget::increaseFontSize(int size) 0502 { 0503 SCConfig::setFontSize(SCConfig::fontSize() + size); 0504 VideoPlayer::instance()->subtitleOverlay().setFontSize(SCConfig::fontSize()); 0505 } 0506 0507 void 0508 PlayerWidget::decreaseFontSize(int size) 0509 { 0510 SCConfig::setFontSize(SCConfig::fontSize() - size); 0511 VideoPlayer::instance()->subtitleOverlay().setFontSize(SCConfig::fontSize()); 0512 } 0513 0514 void 0515 PlayerWidget::updatePlayingLine(const Time &videoPosition) 0516 { 0517 if(!m_subtitle || m_subtitle->isEmpty()) { 0518 setPlayingLine(nullptr); 0519 return; 0520 } 0521 0522 if(m_playingLine && m_playingLine->containsTime(videoPosition)) 0523 return; // playing line is still valid 0524 0525 SubtitleLine *firstLine = m_subtitle->firstLine(); 0526 SubtitleLine *lastLine = m_subtitle->lastLine(); 0527 bool pnValid = true; 0528 if(m_prevLine ? videoPosition < m_prevLine->showTime() : m_nextLine != firstLine) 0529 pnValid = false; 0530 else if(m_nextLine ? videoPosition > m_nextLine->hideTime() : m_prevLine != lastLine) 0531 pnValid = false; 0532 0533 if(!pnValid) { 0534 // prev/next line are invalid 0535 m_prevLine = m_nextLine = nullptr; 0536 int first = 0; 0537 int last = m_subtitle->lastIndex(); 0538 if(videoPosition < firstLine->showTime()) { 0539 m_nextLine = firstLine; 0540 } else if(videoPosition > lastLine->hideTime()) { 0541 m_prevLine = lastLine; 0542 } else { 0543 while(last - first > 1) { 0544 // log2 search lines 0545 int mid = (first + last) / 2; 0546 SubtitleLine *line = m_subtitle->at(mid); 0547 if(videoPosition <= line->hideTime()) 0548 last = mid; 0549 if(videoPosition >= line->showTime()) 0550 first = mid; 0551 } 0552 m_prevLine = m_subtitle->at(first); 0553 m_nextLine = m_subtitle->at(last); 0554 } 0555 } 0556 0557 if(m_prevLine && m_prevLine->containsTime(videoPosition)) 0558 setPlayingLine(m_prevLine); 0559 else if(m_nextLine && m_nextLine->containsTime(videoPosition)) 0560 setPlayingLine(m_nextLine); 0561 else 0562 setPlayingLine(nullptr); 0563 } 0564 0565 void 0566 PlayerWidget::pauseAfterPlayingLine(const SubtitleLine *line) 0567 { 0568 m_pauseAfterPlayingLine = line; 0569 } 0570 0571 void 0572 PlayerWidget::setPlayingLineFromVideo() 0573 { 0574 updatePlayingLine(VideoPlayer::instance()->position() * 1000.); 0575 } 0576 0577 void 0578 PlayerWidget::setPlayingLine(SubtitleLine *line) 0579 { 0580 if(m_subtitle && line && m_subtitle != line->subtitle()) 0581 line = nullptr; 0582 0583 if(m_playingLine == line) 0584 return; 0585 0586 SubtitleTextOverlay &ovr = VideoPlayer::instance()->subtitleOverlay(); 0587 if(m_playingLine) { 0588 disconnect(m_playingLine, &SubtitleLine::showTimeChanged, this, &PlayerWidget::setPlayingLineFromVideo); 0589 disconnect(m_playingLine, &SubtitleLine::hideTimeChanged, this, &PlayerWidget::setPlayingLineFromVideo); 0590 disconnect(m_playingLine, &SubtitleLine::positionChanged, &ovr, &SubtitleTextOverlay::forceRepaint); 0591 } 0592 0593 emit playingLineChanged(m_playingLine = line); 0594 0595 if(m_playingLine) { 0596 connect(m_playingLine, &SubtitleLine::showTimeChanged, this, &PlayerWidget::setPlayingLineFromVideo); 0597 connect(m_playingLine, &SubtitleLine::hideTimeChanged, this, &PlayerWidget::setPlayingLineFromVideo); 0598 connect(m_playingLine, &SubtitleLine::positionChanged, &ovr, &SubtitleTextOverlay::forceRepaint); 0599 ovr.setDoc(m_showTranslation ? m_playingLine->secondaryDoc() : m_playingLine->primaryDoc()); 0600 ovr.setDocRect(&m_playingLine->pos()); 0601 } else { 0602 ovr.setDoc(nullptr); 0603 ovr.setDocRect(nullptr); 0604 } 0605 } 0606 0607 void 0608 PlayerWidget::updatePositionEditVisibility() 0609 { 0610 if(m_showPositionTimeEdit && VideoPlayer::instance()->state() >= VideoPlayer::Playing) 0611 m_positionEdit->show(); 0612 else 0613 m_positionEdit->hide(); 0614 } 0615 0616 void 0617 PlayerWidget::onVolumeSliderMoved(int value) 0618 { 0619 VideoPlayer::instance()->setVolume(value); 0620 } 0621 0622 void 0623 PlayerWidget::onSeekSliderMoved(int value) 0624 { 0625 VideoPlayer *videoPlayer = VideoPlayer::instance(); 0626 pauseAfterPlayingLine(nullptr); 0627 videoPlayer->seek(videoPlayer->duration() * value / 1000.0); 0628 0629 Time time((long)(videoPlayer->duration() * value)); 0630 0631 m_positionLabel->setText(time.toString()); 0632 m_fsPositionLabel->setText(time.toString(false) + m_lengthString); 0633 0634 if(m_showPositionTimeEdit) 0635 m_positionEdit->setValue(time.toMillis()); 0636 } 0637 0638 void 0639 PlayerWidget::onPositionEditValueChanged(int position) 0640 { 0641 if(m_positionEdit->hasFocus()) { 0642 pauseAfterPlayingLine(nullptr); 0643 VideoPlayer::instance()->seek(position / 1000.0); 0644 } 0645 } 0646 0647 void 0648 PlayerWidget::onConfigChanged() 0649 { 0650 if(m_showPositionTimeEdit != SCConfig::showPositionTimeEdit()) { 0651 m_showPositionTimeEdit = SCConfig::showPositionTimeEdit(); 0652 updatePositionEditVisibility(); 0653 } 0654 0655 SubtitleTextOverlay &subtitleOverlay = VideoPlayer::instance()->subtitleOverlay(); 0656 subtitleOverlay.setTextColor(SCConfig::fontColor()); 0657 subtitleOverlay.setFontFamily(SCConfig::fontFamily()); 0658 subtitleOverlay.setFontSize(SCConfig::fontSize()); 0659 subtitleOverlay.setOutlineColor(SCConfig::outlineColor()); 0660 subtitleOverlay.setOutlineWidth(SCConfig::outlineWidth()); 0661 } 0662 0663 void 0664 PlayerWidget::onPlayerFileOpened(const QString & /*filePath */) 0665 { 0666 m_infoControlsGroupBox->setEnabled(true); 0667 0668 updatePositionEditVisibility(); 0669 } 0670 0671 void 0672 PlayerWidget::onPlayerFileOpenError(const QString &filePath, const QString &reason) 0673 { 0674 QString message = i18n("<qt>There was an error opening media file %1.</qt>", filePath); 0675 if(!reason.isEmpty()) 0676 message += "\n" + reason; 0677 KMessageBox::error(this, message); 0678 } 0679 0680 void 0681 PlayerWidget::onPlayerFileClosed() 0682 { 0683 setPlayingLine(nullptr); 0684 0685 m_infoControlsGroupBox->setEnabled(false); 0686 0687 updatePositionEditVisibility(); 0688 m_positionEdit->setValue(0); 0689 0690 m_positionLabel->setText(i18n("<i>Unknown</i>")); 0691 m_lengthLabel->setText(i18n("<i>Unknown</i>")); 0692 m_fpsLabel->setText(i18n("<i>Unknown</i>")); 0693 m_rateLabel->setText(i18n("<i>Unknown</i>")); 0694 0695 m_lengthString = UNKNOWN_LENGTH_STRING; 0696 m_fsPositionLabel->setText(Time().toString(false) + m_lengthString); 0697 0698 m_seekSlider->setEnabled(false); 0699 m_fsSeekSlider->setEnabled(false); 0700 } 0701 0702 void 0703 PlayerWidget::onPlayerPlaybackError(const QString &errorMessage) 0704 { 0705 if(errorMessage.isEmpty()) 0706 KMessageBox::error(this, i18n("Unexpected error when playing file."), i18n("Error Playing File")); 0707 else 0708 KMessageBox::detailedError(this, i18n("Unexpected error when playing file."), errorMessage, i18n("Error Playing File")); 0709 } 0710 0711 void 0712 PlayerWidget::onPlayerPlaying() 0713 { 0714 m_seekSlider->setEnabled(true); 0715 m_fsSeekSlider->setEnabled(true); 0716 0717 updatePositionEditVisibility(); 0718 } 0719 0720 void 0721 PlayerWidget::onPlayerPositionChanged(double seconds) 0722 { 0723 const Time videoPosition(seconds * 1000.); 0724 0725 // pause if requested 0726 if(m_pauseAfterPlayingLine) { 0727 const Time &pauseTime = m_pauseAfterPlayingLine->hideTime(); 0728 if(videoPosition >= pauseTime) { 0729 VideoPlayer *videoPlayer = VideoPlayer::instance(); 0730 m_pauseAfterPlayingLine = nullptr; 0731 videoPlayer->pause(); 0732 videoPlayer->seek(pauseTime.toSeconds()); 0733 return; 0734 } 0735 } 0736 0737 m_positionLabel->setText(videoPosition.toString()); 0738 m_fsPositionLabel->setText(videoPosition.toString(false) + m_lengthString); 0739 0740 if(m_showPositionTimeEdit && !m_positionEdit->hasFocus()) { 0741 QSignalBlocker sb(m_positionEdit); 0742 m_positionEdit->setValue(videoPosition.toMillis()); 0743 } 0744 0745 updatePlayingLine(videoPosition); 0746 0747 QSignalBlocker s1(m_seekSlider), s2(m_fsSeekSlider); 0748 const int sliderValue = int((seconds / VideoPlayer::instance()->duration()) * 1000.0); 0749 m_seekSlider->setValue(sliderValue); 0750 m_fsSeekSlider->setValue(sliderValue); 0751 } 0752 0753 void 0754 PlayerWidget::onPlayerLengthChanged(double seconds) 0755 { 0756 if(seconds > 0) { 0757 m_lengthLabel->setText(Time((long)(seconds * 1000)).toString()); 0758 m_lengthString = " / " + m_lengthLabel->text().left(8) + ' '; 0759 } else { 0760 m_lengthLabel->setText(i18n("<i>Unknown</i>")); 0761 m_lengthString = UNKNOWN_LENGTH_STRING; 0762 } 0763 } 0764 0765 void 0766 PlayerWidget::onPlayerFramesPerSecondChanged(double fps) 0767 { 0768 m_fpsLabel->setText(fps > 0 ? QString::number(fps, 'f', 3) : i18n("<i>Unknown</i>")); 0769 } 0770 0771 void 0772 PlayerWidget::onPlayerPlaybackRateChanged(double rate) 0773 { 0774 m_rateLabel->setText(rate > .0 ? QStringLiteral("%1x").arg(rate, 0, 'g', 3) : i18n("<i>Unknown</i>")); 0775 } 0776 0777 void 0778 PlayerWidget::onPlayerStopped() 0779 { 0780 onPlayerPositionChanged(0); 0781 0782 m_seekSlider->setEnabled(false); 0783 m_fsSeekSlider->setEnabled(false); 0784 0785 setPlayingLine(nullptr); 0786 0787 updatePositionEditVisibility(); 0788 } 0789 0790 void 0791 PlayerWidget::onPlayerVolumeChanged(double volume) 0792 { 0793 QSignalBlocker s1(m_volumeSlider), s2(m_fsVolumeSlider); 0794 m_volumeSlider->setValue(int(volume + 0.5)); 0795 m_fsVolumeSlider->setValue(int(volume + 0.5)); 0796 } 0797 0798 void 0799 PlayerWidget::onPlayerLeftClicked(const QPointF &point) 0800 { 0801 Q_UNUSED(point); 0802 VideoPlayer::instance()->togglePlayPaused(); 0803 } 0804 0805 void 0806 PlayerWidget::onPlayerRightClicked(const QPointF &point) 0807 { 0808 static QMenu *menu = new QMenu(this); 0809 0810 menu->clear(); 0811 0812 menu->addAction(app()->action(ACT_OPEN_VIDEO)); 0813 menu->addAction(app()->action(ACT_CLOSE_VIDEO)); 0814 0815 menu->addSeparator(); 0816 0817 menu->addAction(app()->action(ACT_TOGGLE_FULL_SCREEN)); 0818 0819 menu->addSeparator(); 0820 0821 menu->addAction(app()->action(ACT_STOP)); 0822 menu->addAction(app()->action(ACT_PLAY_PAUSE)); 0823 menu->addAction(app()->action(ACT_SEEK_BACKWARD)); 0824 menu->addAction(app()->action(ACT_SEEK_FORWARD)); 0825 0826 menu->addSeparator(); 0827 0828 menu->addAction(app()->action(ACT_SEEK_TO_PREVIOUS_LINE)); 0829 menu->addAction(app()->action(ACT_SEEK_TO_NEXT_LINE)); 0830 0831 menu->addSeparator(); 0832 0833 menu->addAction(app()->action(ACT_PLAY_RATE_DECREASE)); 0834 menu->addAction(app()->action(ACT_PLAY_RATE_INCREASE)); 0835 0836 menu->addSeparator(); 0837 0838 menu->addAction(app()->action(ACT_SET_ACTIVE_AUDIO_STREAM)); 0839 menu->addAction(app()->action(ACT_INCREASE_VOLUME)); 0840 menu->addAction(app()->action(ACT_DECREASE_VOLUME)); 0841 menu->addAction(app()->action(ACT_TOGGLE_MUTED)); 0842 0843 menu->addSeparator(); 0844 0845 if(m_translationMode) 0846 menu->addAction(app()->action(ACT_SET_ACTIVE_SUBTITLE_STREAM)); 0847 0848 menu->addAction(app()->action(ACT_INCREASE_SUBTITLE_FONT)); 0849 menu->addAction(app()->action(ACT_DECREASE_SUBTITLE_FONT)); 0850 0851 // NOTE do not use popup->exec() here!!! it freezes the application 0852 // when using the mplayer backend. i think it's related to the fact 0853 // that exec() creates a different event loop and the mplayer backend 0854 // depends on the main loop for catching synchronization signals 0855 menu->popup(point.toPoint()); 0856 } 0857 0858 void 0859 PlayerWidget::onPlayerDoubleClicked(const QPointF &point) 0860 { 0861 Q_UNUSED(point); 0862 app()->toggleFullScreenMode(); 0863 }