File indexing completed on 2024-04-28 04:48:38
0001 /* 0002 SPDX-FileCopyrightText: 2005 Max Howell <max.howell@methylblue.com> 0003 SPDX-FileCopyrightText: 2007 Ian Monroe <ian@monroe.nu> 0004 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include "mainWindow.h" 0010 0011 #include <QActionGroup> 0012 #include <QApplication> 0013 #include <QDBusInterface> 0014 #include <QDBusReply> 0015 #include <QDBusUnixFileDescriptor> 0016 #include <QDebug> 0017 #include <QDockWidget> 0018 #include <QDragEnterEvent> 0019 #include <QDropEvent> 0020 #include <QEvent> //::stateChanged() 0021 #include <QFileDialog> 0022 #include <QInputDialog> 0023 #include <QKeyEvent> 0024 #include <QLabel> 0025 #include <QLayout> //ctor 0026 #include <QMenu> 0027 #include <QMenuBar> 0028 #include <QMimeData> 0029 #include <QMouseEvent> 0030 #include <QObject> 0031 #include <QStatusBar> 0032 #include <QTimer> 0033 0034 #include <KAboutData> 0035 #include <KActionMenu> 0036 #include <KCursor> 0037 #include <KIO/StatJob> 0038 #include <KIO/UDSEntry> 0039 #include <KJobWidgets> 0040 #include <KLocalizedString> 0041 #include <KProtocolInfo> 0042 #include <KSharedConfig> 0043 #include <KSqueezedTextLabel> 0044 #include <KToggleFullScreenAction> 0045 #include <KToolBar> 0046 #include <KXMLGUIFactory> 0047 #include <kio_version.h> 0048 0049 #include <phonon/BackendCapabilities> 0050 #include <phonon/VideoWidget> 0051 0052 #include <Solid/Device> 0053 #include <Solid/OpticalDisc> 0054 0055 #include "actions.h" 0056 #include "discSelectionDialog.h" 0057 #include "fullScreenToolBarHandler.h" 0058 #include "messageBox.h" 0059 #include "mpris2/mpris2.h" 0060 #include "playlistFile.h" 0061 #include "theStream.h" 0062 #include "ui_videoSettingsWidget.h" 0063 #include "videoWindow.h" 0064 0065 namespace Dragon 0066 { 0067 0068 MainWindow *MainWindow::s_instance = nullptr; 0069 /// @see codeine.h 0070 QWidget *mainWindow() 0071 { 0072 return MainWindow::s_instance; 0073 } 0074 0075 MainWindow::MainWindow() 0076 : KXmlGuiWindow() 0077 , m_mainView(nullptr) 0078 , m_audioView(nullptr) 0079 , m_loadView(new LoadView(this)) 0080 , m_currentWidget(nullptr) 0081 , m_leftDock(nullptr) 0082 , m_positionSlider(nullptr) 0083 , m_volumeSlider(nullptr) 0084 , m_timeLabel(nullptr) 0085 , m_titleLabel(new QLabel(this)) 0086 , m_menuToggleAction(nullptr) 0087 , m_stopSleepCookie(-1) 0088 , m_stopScreenPowerMgmtCookie(-1) 0089 , m_profileMaxDays(30) 0090 , m_toolbarIsHidden(false) 0091 , m_statusbarIsHidden(false) 0092 , m_menuBarIsHidden(true) 0093 , m_FullScreenHandler(nullptr) 0094 { 0095 s_instance = this; 0096 setMouseTracking(true); 0097 0098 m_mainView = new QStackedWidget(this); 0099 m_mainView->setMouseTracking(true); 0100 0101 new VideoWindow(this); 0102 videoWindow()->setMouseTracking(true); 0103 0104 m_positionSlider = videoWindow()->newPositionSlider(); 0105 0106 m_mainView->addWidget(m_loadView); 0107 m_audioView = new AudioView2(this); 0108 m_mainView->addWidget(m_audioView); 0109 m_mainView->addWidget(videoWindow()); 0110 m_mainView->setCurrentWidget(m_loadView); 0111 0112 setCentralWidget(m_mainView); 0113 0114 setFocusProxy(videoWindow()); // essential! See VideoWindow::event(), QEvent::FocusOut 0115 0116 m_titleLabel->setContentsMargins(2, 2, 2, 2); 0117 m_titleLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); 0118 0119 // FIXME work around a bug in KStatusBar 0120 // sizeHint width of statusbar seems to get stupidly large quickly 0121 statusBar()->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Maximum); 0122 0123 setupActions(); 0124 0125 // setStandardToolBarMenuEnabled( false ); //bah to setupGUI()! 0126 // toolBar()->show(); //it's possible it would be hidden, but we don't want that as no UI way to show it! 0127 0128 { 0129 KActionCollection *ac = actionCollection(); 0130 0131 const auto make_menu = [this, ac](const QString &name, const QString &text) { 0132 auto menuAction = new KActionMenu(text, this); 0133 menuAction->setObjectName(name); 0134 menuAction->setEnabled(false); 0135 connect(menuAction->menu(), &QMenu::aboutToShow, this, &MainWindow::aboutToShowMenu); 0136 ac->addAction(menuAction->objectName(), menuAction); 0137 }; 0138 make_menu(QLatin1String("aspect_ratio_menu"), i18nc("@title:menu", "Aspect &Ratio")); 0139 make_menu(QLatin1String("audio_channels_menu"), i18nc("@title:menu", "&Audio Channels")); 0140 make_menu(QLatin1String("subtitle_channels_menu"), i18nc("@title:menu", "&Subtitles")); 0141 0142 m_aspectRatios = new QActionGroup(this); 0143 m_aspectRatios->setExclusive(true); 0144 const auto make_ratio_action = [this, ac](const QString &text, const QString &objectName, int aspectEnum) { 0145 auto ratioAction = new QAction(this); 0146 ratioAction->setText(text); 0147 ratioAction->setCheckable(true); 0148 m_aspectRatios->addAction(ratioAction); 0149 TheStream::addRatio(aspectEnum, ratioAction); 0150 ac->addAction(objectName, ratioAction); 0151 connect(ratioAction, &QAction::triggered, this, &MainWindow::streamSettingChange); 0152 }; 0153 make_ratio_action(i18nc("@option:radio aspect ratio", "Determine &Automatically"), QLatin1String("ratio_auto"), Phonon::VideoWidget::AspectRatioAuto); 0154 make_ratio_action(i18nc("@option:radio aspect ratio", "&4:3"), QLatin1String("ratio_golden"), Phonon::VideoWidget::AspectRatio4_3); 0155 make_ratio_action(i18nc("@option:radio aspect ratio", "Ana&morphic (16:9)"), QLatin1String("ratio_anamorphic"), Phonon::VideoWidget::AspectRatio16_9); 0156 make_ratio_action(i18nc("@option:radio aspect ratio", "&Window Size"), QLatin1String("ratio_window"), Phonon::VideoWidget::AspectRatioWidget); 0157 0158 ac->action(QLatin1String("ratio_auto"))->setChecked(true); 0159 ac->action(QLatin1String("aspect_ratio_menu"))->menu()->addActions(m_aspectRatios->actions()); 0160 } 0161 0162 setupGUI(); // load xml dragonplayerui.rc file 0163 // must be done after setupGUI: 0164 { 0165 toolBar()->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); 0166 toolBar()->setFloatable(false); 0167 } 0168 KXMLGUIClient::stateChanged(QLatin1String("empty")); 0169 0170 auto hamburgerMenu = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection()); 0171 hamburgerMenu->setObjectName(QStringLiteral("hamburger_menu")); 0172 toolBar()->addAction(hamburgerMenu); 0173 hamburgerMenu->setMenuBar(menuBar()); 0174 if (hamburgerMenu->menu()) { 0175 hamburgerMenu->menu()->clear(); 0176 } 0177 connect(hamburgerMenu, &KHamburgerMenu::aboutToShowMenu, this, [this, hamburgerMenu] { 0178 if (!hamburgerMenu->menu()) { 0179 hamburgerMenu->setMenu(new QMenu); 0180 } 0181 0182 auto ac = actionCollection(); 0183 0184 auto menu = hamburgerMenu->menu(); 0185 if (!menu->isEmpty()) { 0186 return; 0187 } 0188 0189 auto originalSettingsMenu = qobject_cast<QMenu *>(guiFactory()->container(QStringLiteral("settings"), this)); 0190 auto settingsMenu = new QMenu(originalSettingsMenu->title()); 0191 settingsMenu->addActions(originalSettingsMenu->actions()); 0192 0193 menu->addMenu(qobject_cast<QMenu *>(guiFactory()->container(QStringLiteral("play_media_menu"), this))); 0194 menu->addAction(ac->action(QStringLiteral("file_open_recent"))); 0195 menu->addAction(ac->action(QStringLiteral("play"))); 0196 menu->addAction(ac->action(QStringLiteral("stop"))); 0197 menu->addAction(ac->action(QStringLiteral("prev_chapter"))); 0198 menu->addAction(ac->action(QStringLiteral("next_chapter"))); 0199 menu->addSeparator(); 0200 menu->addAction(ac->action(QStringLiteral("fullscreen"))); 0201 menu->addSeparator(); 0202 // Extract certain actions out of the settingsmenu and place them in the top level. They don't deserve to be hidden 0203 for (const auto &id : 0204 {"volume", "fullscreen", "aspect_ratio_menu", "subtitle_channels_menu", "audio_channels_menu", "toggle_dvd_menu", "video_settings"}) { 0205 auto action = ac->action(QLatin1String(id)); 0206 menu->addAction(action); 0207 settingsMenu->removeAction(action); 0208 } 0209 0210 menu->addMenu(settingsMenu); 0211 hamburgerMenu->setMenuBarAdvertised(false); // hide whatever remains (i.e. the quit action) 0212 }); 0213 m_menuBarIsHidden = actionCollection()->action(QStringLiteral("options_show_menubar"))->isChecked(); 0214 0215 //"faster" startup 0216 // TODO if we have a size stored for this video, do the "faster" route 0217 QTimer::singleShot(0, this, &MainWindow::init); 0218 QApplication::setOverrideCursor(Qt::WaitCursor); 0219 } 0220 0221 void MainWindow::init() 0222 { 0223 // connect the video player 0224 connect(engine(), &VideoWindow::stateUpdated, this, &MainWindow::engineStateChanged); 0225 connect(engine(), &VideoWindow::currentSourceChanged, this, &MainWindow::engineMediaChanged); 0226 connect(engine(), &VideoWindow::seekableChanged, this, &MainWindow::engineSeekableChanged); 0227 connect(engine(), &VideoWindow::metaDataChanged, this, &MainWindow::engineMetaDataChanged); 0228 connect(engine(), &VideoWindow::hasVideoChanged, this, &MainWindow::engineHasVideoChanged); 0229 0230 connect(engine(), &VideoWindow::subChannelsChanged, this, &MainWindow::subChannelsChanged); 0231 connect(engine(), &VideoWindow::audioChannelsChanged, this, &MainWindow::audioChannelsChanged); 0232 connect(engine(), &VideoWindow::mutedChanged, this, &MainWindow::mutedChanged); 0233 0234 connect(engine(), &VideoWindow::finished, this, &MainWindow::toggleLoadView); 0235 0236 if (!engine()->init()) { 0237 KMessageBox::error(this, i18n("<qt>Phonon could not be successfully initialized. Dragon Player will now exit.</qt>")); 0238 QApplication::exit(2); 0239 } 0240 0241 // would be dangerous for these to happen before the videoWindow() is initialised 0242 setAcceptDrops(true); 0243 connect(statusBar(), &QStatusBar::messageChanged, engine(), &VideoWindow::showOSD); 0244 // statusBar()->insertPermanentItem( "hello world", 0, 0 ); 0245 m_timeLabel = new TimeLabel(statusBar()); 0246 connect(videoWindow(), &VideoWindow::tick, m_timeLabel, &TimeLabel::setCurrentTime); 0247 connect(videoWindow(), &VideoWindow::totalTimeChanged, m_timeLabel, &TimeLabel::setTotalTime); 0248 statusBar()->addPermanentWidget(m_titleLabel, 100); 0249 statusBar()->addPermanentWidget(m_timeLabel); 0250 0251 new Mpris2(this); 0252 0253 QApplication::restoreOverrideCursor(); 0254 engineStateChanged(Phonon::StoppedState); // set everything as it would be in stopped state 0255 engineSeekableChanged(false); 0256 } 0257 0258 MainWindow::~MainWindow() 0259 { 0260 hide(); // so we appear to have quit, and then sound fades out below 0261 releasePowerSave(); 0262 qobject_cast<KRecentFilesAction *>(action(QStringLiteral("file_open_recent"))) 0263 ->saveEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("General"))); 0264 delete videoWindow(); // fades out sound in dtor 0265 } 0266 0267 void MainWindow::closeEvent(QCloseEvent *event) 0268 { 0269 mainWindow()->setWindowState(Qt::WindowNoState); 0270 // restore the following user ui settings which are changed in full screen mode 0271 if (mainWindow()->isFullScreen()) { 0272 statusBar()->setHidden(m_statusbarIsHidden); 0273 toolBar()->setHidden(m_toolbarIsHidden); 0274 menuBar()->setHidden(m_menuBarIsHidden); 0275 } 0276 0277 KMainWindow::closeEvent(event); 0278 } 0279 0280 void MainWindow::wheelEvent(QWheelEvent *event) 0281 { 0282 // do not allow to change volume in load view 0283 // it can be frustrating in recently played list 0284 if (m_mainView->currentWidget() == m_loadView) { 0285 return; 0286 } 0287 0288 if (event->angleDelta().y() > 0) 0289 engine()->increaseVolume(); 0290 else 0291 engine()->decreaseVolume(); 0292 event->accept(); 0293 } 0294 0295 void MainWindow::setupActions() 0296 { 0297 KActionCollection *const ac = actionCollection(); 0298 0299 auto open = KStandardAction::open(this, &MainWindow::openFileDialog, ac); 0300 open->setText(i18nc("@action", "Play File…")); 0301 open->setToolTip(i18nc("@info:tooltip", "Open a media file for playback")); 0302 auto recent = KStandardAction::openRecent(this, &MainWindow::open, ac); 0303 recent->loadEntries(KConfigGroup(KSharedConfig::openConfig(), QStringLiteral("General"))); 0304 KStandardAction::quit(qApp, &QApplication::closeAllWindows, ac); 0305 0306 const auto addToAc = [ac](QAction *action) { 0307 ac->addAction(action->objectName(), action); 0308 }; 0309 0310 auto playStreamAction = new QAction(i18nc("@action", "Play Stream…"), ac); 0311 playStreamAction->setObjectName(QStringLiteral("play_stream")); 0312 playStreamAction->setIcon(QIcon::fromTheme(QStringLiteral("document-open-remote"))); 0313 connect(playStreamAction, &QAction::triggered, this, &MainWindow::openStreamDialog); 0314 addToAc(playStreamAction); 0315 0316 auto playDiscAction = new QAction(i18nc("@action", "Play Disc"), ac); 0317 playDiscAction->setObjectName(QStringLiteral("play_disc")); 0318 playDiscAction->setIcon(QIcon::fromTheme(QStringLiteral("media-optical"))); 0319 connect(playDiscAction, &QAction::triggered, this, &MainWindow::playDisc); 0320 if (Solid::Device::listFromType(Solid::DeviceInterface::OpticalDrive).isEmpty()) { 0321 playDiscAction->setVisible(false); 0322 } 0323 addToAc(playDiscAction); 0324 0325 m_loadView->setToolbarActions({open, playStreamAction, playDiscAction}); 0326 0327 KToggleFullScreenAction *toggleFullScreen = new KToggleFullScreenAction(this, ac); 0328 toggleFullScreen->setObjectName(QLatin1String("fullscreen")); 0329 ac->setDefaultShortcuts(toggleFullScreen, QList<QKeySequence>() << Qt::Key_F << KStandardShortcut::fullScreen()); 0330 toggleFullScreen->setAutoRepeat(false); 0331 connect(toggleFullScreen, SIGNAL(toggled(bool)), Dragon::mainWindow(), SLOT(setFullScreen(bool))); 0332 0333 addToAc(toggleFullScreen); 0334 0335 new PlayAction(this, &MainWindow::play, ac); 0336 new VolumeAction(this, &MainWindow::toggleVolumeSlider, ac); 0337 0338 m_menuToggleAction = static_cast<KToggleAction *>(ac->addAction(KStandardAction::ShowMenubar, menuBar(), SLOT(setVisible(bool)))); 0339 0340 auto action = new QAction(i18nc("@action", "Increase Volume"), ac); 0341 action->setObjectName(QLatin1String("volume_inc")); 0342 connect(action, &QAction::triggered, engine(), &VideoWindow::increaseVolume); 0343 addToAc(action); 0344 0345 action = new QAction(i18nc("@action", "Decrease Volume"), ac); 0346 action->setObjectName(QLatin1String("volume_dec")); 0347 connect(action, &QAction::triggered, engine(), &VideoWindow::decreaseVolume); 0348 addToAc(action); 0349 0350 auto playerStop = new QAction(QIcon::fromTheme(QLatin1String("media-playback-stop")), i18nc("@action", "Stop"), ac); 0351 playerStop->setObjectName(QLatin1String("stop")); 0352 ac->setDefaultShortcuts(playerStop, QList<QKeySequence>() << Qt::Key_S << Qt::Key_MediaStop); 0353 connect(playerStop, &QAction::triggered, this, &MainWindow::stop); 0354 addToAc(playerStop); 0355 0356 auto mute = new KToggleAction(QIcon::fromTheme(QLatin1String("player-volume-muted")), i18nc("@action Mute the sound output", "Mute"), ac); 0357 mute->setObjectName(QLatin1String("mute")); 0358 ac->setDefaultShortcut(mute, Qt::Key_M); 0359 connect(mute, &QAction::toggled, videoWindow(), &VideoWindow::mute); 0360 addToAc(mute); 0361 0362 auto resetZoom = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18nc("@action", "Reset Video Scale"), ac); 0363 resetZoom->setObjectName(QLatin1String("reset_zoom")); 0364 ac->setDefaultShortcut(resetZoom, Qt::Key_Equal); 0365 connect(resetZoom, &QAction::triggered, videoWindow(), &VideoWindow::resetZoom); 0366 addToAc(resetZoom); 0367 0368 auto dvdMenu = new QAction(QIcon::fromTheme(QLatin1String("media-optical-video")), i18nc("@action", "Menu Toggle"), ac); 0369 dvdMenu->setObjectName(QLatin1String("toggle_dvd_menu")); 0370 ac->setDefaultShortcut(dvdMenu, Qt::Key_R); 0371 connect(dvdMenu, &QAction::triggered, engine(), &VideoWindow::toggleDVDMenu); 0372 addToAc(dvdMenu); 0373 0374 auto positionSlider = new QWidgetAction(ac); 0375 positionSlider->setObjectName(QLatin1String("position_slider")); 0376 positionSlider->setText(i18n("Position Slider")); 0377 positionSlider->setDefaultWidget(m_positionSlider); 0378 addToAc(positionSlider); 0379 0380 auto videoSettings = new QAction(i18nc("@option:check", "Video Settings"), ac); 0381 videoSettings->setObjectName(QLatin1String("video_settings")); 0382 videoSettings->setCheckable(true); 0383 connect(videoSettings, &QAction::toggled, this, &MainWindow::toggleVideoSettings); 0384 addToAc(videoSettings); 0385 0386 auto uniqueToggle = new QAction(i18nc("@option:check Whether only one instance of dragon can be started" 0387 " and will be reused when the user tries to play another file.", 0388 "One Instance Only"), 0389 ac); 0390 uniqueToggle->setObjectName(QLatin1String("unique")); 0391 uniqueToggle->setCheckable(true); 0392 uniqueToggle->setChecked(!KSharedConfig::openConfig()->group(QStringLiteral("KDE")).readEntry("MultipleInstances", QVariant(false)).toBool()); 0393 connect(uniqueToggle, &QAction::toggled, this, &MainWindow::toggleUnique); 0394 addToAc(uniqueToggle); 0395 0396 auto prev_chapter = new QAction(QIcon::fromTheme(QLatin1String("media-skip-backward")), i18nc("@action previous chapter", "Previous"), ac); 0397 prev_chapter->setObjectName(QLatin1String("prev_chapter")); 0398 ac->setDefaultShortcuts(prev_chapter, QList<QKeySequence>() << Qt::Key_Comma << Qt::Key_MediaPrevious); 0399 connect(prev_chapter, &QAction::triggered, engine(), &VideoWindow::prevChapter); 0400 addToAc(prev_chapter); 0401 0402 auto next_chapter = new QAction(QIcon::fromTheme(QLatin1String("media-skip-forward")), i18nc("@action next chapter", "Next"), ac); 0403 next_chapter->setObjectName(QLatin1String("next_chapter")); 0404 ac->setDefaultShortcuts(next_chapter, QList<QKeySequence>() << Qt::Key_Period << Qt::Key_MediaNext); 0405 connect(next_chapter, &QAction::triggered, engine(), &VideoWindow::nextChapter); 0406 addToAc(next_chapter); 0407 0408 // xgettext: no-c-format 0409 auto tenPercentBack = new QAction(QIcon::fromTheme(QLatin1String("media-seek-backward")), i18nc("@action", "Return 10% Back"), ac); 0410 tenPercentBack->setObjectName(QLatin1String("ten_percent_back")); 0411 ac->setDefaultShortcut(tenPercentBack, Qt::Key_PageUp); 0412 connect(tenPercentBack, &QAction::triggered, engine(), &VideoWindow::tenPercentBack); 0413 addToAc(tenPercentBack); 0414 0415 // xgettext: no-c-format 0416 auto tenPercentForward = new QAction(QIcon::fromTheme(QLatin1String("media-seek-forward")), i18nc("@action", "Go 10% Forward"), ac); 0417 tenPercentForward->setObjectName(QLatin1String("ten_percent_forward")); 0418 ac->setDefaultShortcut(tenPercentForward, Qt::Key_PageDown); 0419 connect(tenPercentForward, &QAction::triggered, engine(), &VideoWindow::tenPercentForward); 0420 addToAc(tenPercentForward); 0421 0422 auto tenSecondsBack = new QAction(QIcon::fromTheme(QLatin1String("media-seek-backward")), i18nc("@action", "Return 10 Seconds Back"), ac); 0423 tenSecondsBack->setObjectName(QLatin1String("ten_seconds_back")); 0424 ac->setDefaultShortcut(tenSecondsBack, Qt::Key_Minus); 0425 connect(tenSecondsBack, &QAction::triggered, engine(), &VideoWindow::tenSecondsBack); 0426 addToAc(tenSecondsBack); 0427 0428 auto tenSecondsForward = new QAction(QIcon::fromTheme(QLatin1String("media-seek-forward")), i18nc("@action", "Go 10 Seconds Forward"), ac); 0429 tenSecondsForward->setObjectName(QLatin1String("ten_seconds_forward")); 0430 ac->setDefaultShortcut(tenSecondsForward, Qt::Key_Plus); 0431 connect(tenSecondsForward, &QAction::triggered, engine(), &VideoWindow::tenSecondsForward); 0432 addToAc(tenSecondsForward); 0433 } 0434 0435 void MainWindow::toggleUnique(bool unique) 0436 { 0437 KSharedConfig::Ptr cfg = KSharedConfig::openConfig(); // kf5 FIXME? this might not work w/o KUniqueApplication 0438 cfg->group(QStringLiteral("KDE")).writeEntry("MultipleInstances", !unique); 0439 cfg->sync(); 0440 } 0441 0442 void MainWindow::toggleVideoSettings(bool show) 0443 { 0444 if (show) { 0445 m_leftDock = new QDockWidget(this); 0446 m_leftDock->setObjectName(QLatin1String("left_dock")); 0447 m_leftDock->setFeatures(QDockWidget::NoDockWidgetFeatures); 0448 QWidget *videoSettingsWidget = new QWidget(m_leftDock); 0449 m_leftDock->setWidget(videoSettingsWidget); 0450 Ui::VideoSettingsWidget ui; 0451 ui.setupUi(videoSettingsWidget); 0452 KGuiItem::assign(ui.defaultsButton, KStandardGuiItem::defaults()); 0453 KGuiItem::assign(ui.closeButton, KStandardGuiItem::closeWindow()); 0454 videoSettingsWidget->adjustSize(); 0455 addDockWidget(Qt::LeftDockWidgetArea, m_leftDock); 0456 m_sliders.clear(); 0457 m_sliders << ui.brightnessSlider << ui.contrastSlider << ui.hueSlider << ui.saturationSlider; 0458 updateSliders(); 0459 for (QSlider *slider : std::as_const(m_sliders)) { 0460 connect(slider, &QAbstractSlider::valueChanged, engine(), &VideoWindow::settingChanged); 0461 } 0462 0463 connect(ui.defaultsButton, &QAbstractButton::clicked, this, &MainWindow::restoreDefaultVideoSettings); 0464 connect(ui.closeButton, &QAbstractButton::clicked, action(QStringLiteral("video_settings")), &QAction::setChecked); 0465 connect(ui.closeButton, &QAbstractButton::clicked, m_leftDock, &QObject::deleteLater); 0466 } else { 0467 m_sliders.clear(); 0468 delete m_leftDock; 0469 } 0470 } 0471 0472 void MainWindow::restoreDefaultVideoSettings() 0473 { 0474 for (QSlider *slider : std::as_const(m_sliders)) { 0475 slider->setValue(0); 0476 } 0477 } 0478 0479 void MainWindow::toggleLoadView() 0480 { 0481 if (m_mainView->currentWidget() == m_loadView) { 0482 if (m_currentWidget && engine()->state() != Phonon::StoppedState) { 0483 if (m_mainView->indexOf(m_currentWidget) == -1) { 0484 m_mainView->addWidget(m_currentWidget); 0485 } 0486 m_mainView->setCurrentWidget(m_currentWidget); 0487 } 0488 engine()->isPreview(false); 0489 } else if (m_currentWidget != m_audioView) { 0490 m_mainView->setCurrentWidget(m_loadView); 0491 if (m_currentWidget && engine()->state() != Phonon::StoppedState) { 0492 m_mainView->removeWidget(m_currentWidget); 0493 engine()->isPreview(true); 0494 } 0495 } else { 0496 m_mainView->setCurrentWidget(m_loadView); 0497 } 0498 } 0499 0500 void MainWindow::toggleVolumeSlider(bool show) 0501 { 0502 if (show) { 0503 m_volumeSlider = engine()->newVolumeSlider(); 0504 m_volumeSlider->setDisabled(engine()->isMuted()); 0505 m_volumeSlider->setFocus(Qt::PopupFocusReason); 0506 0507 m_muteCheckBox = new QCheckBox(); 0508 m_muteCheckBox->setText(i18nc("@option:check Mute the sound output", "Mute")); 0509 m_muteCheckBox->setChecked(engine()->isMuted()); 0510 connect(m_muteCheckBox, &QCheckBox::toggled, videoWindow(), &VideoWindow::mute); 0511 0512 QVBoxLayout *layout = new QVBoxLayout(); 0513 layout->addWidget(m_volumeSlider); 0514 layout->addWidget(m_muteCheckBox); 0515 0516 QWidget *dock = new QWidget; 0517 dock->setLayout(layout); 0518 0519 m_rightDock = new QDockWidget(this); 0520 m_rightDock->setFeatures(QDockWidget::NoDockWidgetFeatures); 0521 m_rightDock->setObjectName(QStringLiteral("volume_dock")); 0522 dock->setParent(m_rightDock); 0523 m_rightDock->setWidget(dock); 0524 addDockWidget(Qt::RightDockWidgetArea, m_rightDock); 0525 } else { 0526 disconnect(m_muteCheckBox, &QCheckBox::toggled, videoWindow(), &VideoWindow::mute); 0527 delete m_rightDock; // it's a QPointer, it will 0 itself 0528 } 0529 } 0530 0531 void MainWindow::mutedChanged(bool mute) 0532 { 0533 if (m_rightDock) { 0534 m_volumeSlider->setDisabled(mute); 0535 m_muteCheckBox->setChecked(mute); 0536 } 0537 } 0538 0539 void MainWindow::stop() 0540 { 0541 engine()->stop(); 0542 toggleLoadView(); 0543 } 0544 0545 void MainWindow::updateSliders() 0546 { 0547 for (QSlider *slider : std::as_const(m_sliders)) { 0548 slider->setValue(engine()->videoSetting(slider->objectName())); 0549 } 0550 } 0551 0552 void MainWindow::engineMessage(const QString &message) 0553 { 0554 statusBar()->showMessage(message, 3500); 0555 } 0556 0557 bool MainWindow::open(const QUrl &url) 0558 { 0559 qDebug() << "Opening" << url; 0560 0561 if (load(url)) { 0562 const int offset = (TheStream::hasProfile() && isFresh()) 0563 // adjust offset if we have session history for this video 0564 ? TheStream::profile().readEntry<int>("Position", 0) 0565 : 0; 0566 qDebug() << "Initial offset is " << offset; 0567 engine()->loadSettings(); 0568 updateSliders(); 0569 return engine()->play(offset); 0570 } 0571 0572 return false; 0573 } 0574 0575 bool MainWindow::load(const QUrl &url) 0576 { 0577 // FileWatch the file that is opened 0578 0579 if (url.isEmpty()) { 0580 MessageBox::error(i18n("Dragon Player was asked to open an empty URL; it cannot.")); 0581 return false; 0582 } 0583 0584 bool ret = false; 0585 0586 PlaylistFile playlist(url); 0587 if (playlist.isPlaylist()) { 0588 // TODO: problem is we return out of the function 0589 // statusBar()->message( i18n("Parsing playlist file...") ); 0590 0591 if (playlist.isValid()) 0592 ret = engine()->load(playlist.contents()); 0593 else { 0594 MessageBox::error(playlist.error()); 0595 return false; 0596 } 0597 } 0598 0599 // local protocols like nepomuksearch:/ are not supported by xine 0600 // check if an UDS_LOCAL_PATH is defined. 0601 if (!ret && KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) { 0602 // #define UDS_LOCAL_PATH (7 | KIO::UDS_STRING) 0603 KIO::StatJob *job = KIO::stat(url, KIO::StatJob::SourceSide, KIO::StatBasic); 0604 KJobWidgets::setWindow(job, this); 0605 if (job->exec()) { 0606 KIO::UDSEntry e = job->statResult(); 0607 const QString path = e.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); 0608 if (!path.isEmpty()) 0609 ret = engine()->load(QUrl::fromLocalFile(path)); 0610 } 0611 job->deleteLater(); 0612 } 0613 0614 // let xine handle invalid, etc, QUrlS 0615 // TODO it handles non-existing files with bad error message 0616 if (!ret) 0617 ret = engine()->load(url); 0618 0619 if (ret) { 0620 m_currentWidget = nullptr; 0621 } 0622 return ret; 0623 } 0624 0625 void MainWindow::play() 0626 { 0627 switch (engine()->state()) { 0628 case Phonon::PlayingState: 0629 engine()->pause(); 0630 break; 0631 case Phonon::PausedState: 0632 engine()->resume(); 0633 if (m_mainView->currentWidget() == m_loadView) 0634 toggleLoadView(); 0635 break; 0636 case Phonon::StoppedState: 0637 engine()->play(); 0638 m_currentWidget = nullptr; 0639 break; 0640 default: 0641 break; 0642 } 0643 } 0644 0645 void MainWindow::openFileDialog() 0646 { 0647 QStringList mimeFilter = Phonon::BackendCapabilities::availableMimeTypes(); 0648 // temporary fixes for MimeTypes that Xine does support but it doesn't return - this is a Xine bug. 0649 mimeFilter << QLatin1String("audio/x-flac"); 0650 mimeFilter << QLatin1String("video/mp4"); 0651 mimeFilter << QLatin1String("application/x-cd-image"); // added for *.iso images 0652 // everything. Must be here or the native Qt dialog doesn't want to default to it! https://bugs.kde.org/show_bug.cgi?id=459326 0653 mimeFilter << QLatin1String("application/octet-stream"); 0654 0655 static QUrl lastDirectory; 0656 0657 QFileDialog dlg(this, i18nc("@title:window", "Select File to Play")); 0658 dlg.setAcceptMode(QFileDialog::AcceptOpen); 0659 dlg.setFileMode(QFileDialog::ExistingFile); 0660 dlg.setMimeTypeFilters(mimeFilter); 0661 dlg.selectMimeTypeFilter(QStringLiteral("application/octet-stream")); // by default don't restrict 0662 0663 if (lastDirectory.isValid()) { 0664 dlg.setDirectoryUrl(lastDirectory); 0665 } else { 0666 dlg.setDirectory(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)); 0667 } 0668 0669 dlg.exec(); 0670 0671 lastDirectory = dlg.directoryUrl(); 0672 const QList<QUrl> urls = dlg.selectedUrls(); 0673 0674 if (urls.isEmpty()) { 0675 qDebug() << Q_FUNC_INFO << "URL empty"; 0676 return; 0677 } else { 0678 open(urls.first()); 0679 } 0680 } 0681 0682 void MainWindow::openStreamDialog() 0683 { 0684 QUrl url = QUrl::fromUserInput(QInputDialog::getText(this, i18nc("@title:window", "Stream to Play"), i18nc("@label:textbox", "Stream:"))); 0685 0686 if (url.isEmpty()) { 0687 qDebug() << "URL empty in MainWindow::openStreamDialog()"; 0688 return; 0689 } else { 0690 open(url); 0691 } 0692 } 0693 0694 void MainWindow::playDisc() 0695 { 0696 QList<Solid::Device> playableDiscs; 0697 { 0698 const QList<Solid::Device> deviceList = Solid::Device::listFromType(Solid::DeviceInterface::OpticalDisc); 0699 0700 for (const Solid::Device &device : deviceList) { 0701 const Solid::OpticalDisc *disc = device.as<const Solid::OpticalDisc>(); 0702 if (disc) { 0703 if (disc->availableContent() 0704 & (Solid::OpticalDisc::VideoDvd | Solid::OpticalDisc::VideoCd | Solid::OpticalDisc::SuperVideoCd | Solid::OpticalDisc::Audio)) 0705 playableDiscs << device; 0706 } 0707 } 0708 } 0709 if (!playableDiscs.isEmpty()) { 0710 if (playableDiscs.size() > 1) { // more than one disc, show user a selection box 0711 qDebug() << "> 1 possible discs, showing dialog"; 0712 new DiscSelectionDialog(this, playableDiscs); 0713 } else { // only one optical disc inserted, play whatever it is 0714 bool status = engine()->playDisc(playableDiscs.first()); 0715 qDebug() << "playing disc" << status; 0716 } 0717 } else { 0718 engine()->playDvd(); 0719 toggleLoadView(); 0720 qDebug() << "no disc in drive or Solid isn't working"; 0721 } 0722 } 0723 0724 void MainWindow::openRecentFile(const QUrl &url) 0725 { 0726 this->open(url); 0727 } 0728 0729 void MainWindow::setFullScreen(bool isFullScreen) 0730 { 0731 qDebug() << "Setting full screen to " << isFullScreen; 0732 mainWindow()->setWindowState((isFullScreen ? Qt::WindowFullScreen : Qt::WindowNoState)); 0733 0734 if (isFullScreen) { 0735 m_statusbarIsHidden = statusBar()->isHidden(); 0736 m_toolbarIsHidden = toolBar()->isHidden(); 0737 m_menuBarIsHidden = menuBar()->isHidden(); 0738 toolBar()->setHidden(false); 0739 statusBar()->setHidden(true); 0740 menuBar()->setHidden(true); 0741 } else { 0742 statusBar()->setHidden(m_statusbarIsHidden); 0743 toolBar()->setHidden(m_toolbarIsHidden); 0744 menuBar()->setHidden(m_menuBarIsHidden); 0745 // In case someone hit the shortcut while being in fullscreen, the action 0746 // would be out of sync. 0747 m_menuToggleAction->setChecked(!m_menuBarIsHidden); 0748 } 0749 if (m_leftDock) 0750 m_leftDock->setHidden(isFullScreen); 0751 // the right dock is handled by the tool bar handler 0752 0753 if (isFullScreen) { 0754 if (!m_FullScreenHandler) 0755 m_FullScreenHandler = new FullScreenToolBarHandler(this); 0756 } else { 0757 action(QStringLiteral("fullscreen"))->setEnabled(videoWindow()->state() == Phonon::PlayingState || videoWindow()->state() == Phonon::PausedState); 0758 delete m_FullScreenHandler; 0759 m_FullScreenHandler = nullptr; 0760 } 0761 } 0762 0763 void MainWindow::showVolume(bool visible) 0764 { 0765 if (m_rightDock) 0766 m_rightDock->setVisible(visible); 0767 } 0768 0769 bool MainWindow::volumeContains(const QPoint &mousePos) 0770 { 0771 if (m_rightDock) 0772 return m_rightDock->geometry().contains(mousePos); 0773 return false; 0774 } 0775 0776 void MainWindow::aboutToShowMenu() 0777 { 0778 TheStream::aspectRatioAction()->setChecked(true); 0779 { 0780 const int subId = TheStream::subtitleChannel(); 0781 const QList<QAction *> subs = action(QStringLiteral("subtitle_channels_menu"))->menu()->actions(); 0782 qDebug() << "subtitle #" << subId << " is going to be checked"; 0783 for (QAction *subAction : subs) { 0784 if (subAction->property(TheStream::CHANNEL_PROPERTY).toInt() == subId) { 0785 subAction->setChecked(true); 0786 break; 0787 } 0788 qDebug() << subAction->property(TheStream::CHANNEL_PROPERTY).toInt() << " not checked."; 0789 } 0790 } 0791 { 0792 const int audioId = TheStream::audioChannel(); 0793 const QList<QAction *> audios = action(QStringLiteral("audio_channels_menu"))->menu()->actions(); 0794 qDebug() << "audio #" << audioId << " is going to be checked"; 0795 for (QAction *audioAction : audios) { 0796 if (audioAction->property(TheStream::CHANNEL_PROPERTY).toInt() == audioId) { 0797 audioAction->setChecked(true); 0798 break; 0799 } 0800 } 0801 } 0802 } 0803 0804 void MainWindow::dragEnterEvent(QDragEnterEvent *e) 0805 { 0806 e->setAccepted(e->mimeData()->hasUrls()); 0807 } 0808 0809 void MainWindow::dropEvent(QDropEvent *e) 0810 { 0811 if (e->mimeData()->hasUrls()) 0812 this->open(e->mimeData()->urls().first()); 0813 else 0814 engineMessage(i18n("Sorry, no media was found in the drop")); 0815 } 0816 0817 void MainWindow::keyPressEvent(QKeyEvent *e) 0818 { 0819 switch (e->key()) { 0820 case Qt::Key_Left: 0821 engine()->relativeSeek(-5000); 0822 break; 0823 case Qt::Key_Right: 0824 engine()->relativeSeek(5000); 0825 break; 0826 case Qt::Key_Escape: 0827 action(QStringLiteral("fullscreen"))->setChecked(false); 0828 default:; 0829 } 0830 } 0831 0832 void MainWindow::inhibitPowerSave() 0833 { 0834 if (m_stopSleepCookie == -1) { 0835 QDBusInterface iface(QStringLiteral("org.freedesktop.login1"), 0836 QStringLiteral("/org/freedesktop/login1"), 0837 QStringLiteral("org.freedesktop.login1.Manager"), 0838 QDBusConnection::systemBus()); 0839 if (iface.isValid()) { 0840 QDBusReply<QDBusUnixFileDescriptor> reply; 0841 if (TheStream::hasVideo()) { 0842 reply = iface.call(QStringLiteral("Inhibit"), 0843 QStringLiteral("sleep:idle"), 0844 KAboutData::applicationData().componentName(), 0845 QStringLiteral("playing a video"), 0846 QStringLiteral("block")); 0847 } else { 0848 reply = iface.call(QStringLiteral("Inhibit"), 0849 QStringLiteral("sleep"), 0850 KAboutData::applicationData().componentName(), 0851 QStringLiteral("playing an audio"), 0852 QStringLiteral("block")); 0853 } 0854 if (reply.isValid()) { 0855 m_stopSleepCookie = reply.value().fileDescriptor(); 0856 } 0857 } 0858 } 0859 // TODO: inhibit screen sleep. No viable API found. 0860 // https://git.reviewboard.kde.org/r/129651 0861 if (TheStream::hasVideo()) { 0862 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"), 0863 QStringLiteral("/ScreenSaver"), 0864 QStringLiteral("org.freedesktop.ScreenSaver"), 0865 QStringLiteral("Inhibit")); 0866 message << QGuiApplication::desktopFileName(); 0867 message << i18nc("Notification inhibition reason", "Playing a video"); 0868 QDBusReply<uint> reply = QDBusConnection::sessionBus().call(message); 0869 if (reply.isValid()) { 0870 m_screensaverDisableCookie = reply.value(); 0871 return; 0872 } 0873 } 0874 } 0875 0876 void MainWindow::releasePowerSave() 0877 { 0878 // stop suppressing sleep 0879 if (m_stopSleepCookie != -1) { 0880 ::close(m_stopSleepCookie); 0881 m_stopSleepCookie = -1; 0882 } 0883 0884 // stop disabling screensaver 0885 if (m_screensaverDisableCookie.has_value()) { 0886 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"), 0887 QStringLiteral("/ScreenSaver"), 0888 QStringLiteral("org.freedesktop.ScreenSaver"), 0889 QStringLiteral("UnInhibit")); 0890 message << static_cast<uint>(m_screensaverDisableCookie.value()); 0891 m_screensaverDisableCookie = {}; 0892 if (QDBusConnection::sessionBus().send(message)) { 0893 return; 0894 } 0895 } 0896 } 0897 0898 QMenu *MainWindow::menu(const char *name) 0899 { 0900 // KXMLGUI is "really good". 0901 return static_cast<QMenu *>(factory()->container(QLatin1String(name), this)); 0902 } 0903 0904 void MainWindow::streamSettingChange() 0905 { 0906 if (sender()->objectName().left(5) == QLatin1String("ratio")) { 0907 TheStream::setRatio(dynamic_cast<QAction *>(sender())); 0908 } 0909 } 0910 0911 void MainWindow::updateTitleBarText() 0912 { 0913 if (!TheStream::hasMedia()) { 0914 setWindowTitle(i18n("No media loaded")); 0915 } else if (engine()->state() == Phonon::PausedState) { 0916 setWindowTitle(i18n("Paused")); 0917 } else { 0918 setWindowTitle(TheStream::prettyTitle()); 0919 } 0920 qDebug() << "set titles "; 0921 } 0922 0923 #define CHANNELS_CHANGED(function, actionName) \ 0924 void MainWindow::function(QList<QAction *> subActions) \ 0925 { \ 0926 if (subActions.size() <= 2) \ 0927 action(actionName)->setEnabled(false); \ 0928 else { \ 0929 action(actionName)->menu()->addActions(subActions); \ 0930 action(actionName)->setEnabled(true); \ 0931 } \ 0932 } 0933 0934 CHANNELS_CHANGED(subChannelsChanged, QStringLiteral("subtitle_channels_menu")) 0935 CHANNELS_CHANGED(audioChannelsChanged, QStringLiteral("audio_channels_menu")) 0936 #undef CHANNELS_CHANGED 0937 0938 /// Convenience class for other classes that need access to the actionCollection 0939 KActionCollection *actionCollection() 0940 { 0941 return static_cast<MainWindow *>(mainWindow())->actionCollection(); 0942 } 0943 0944 /// Convenience class for other classes that need access to the actions 0945 QAction *action(const char *name) 0946 { 0947 KActionCollection *actionCollection = nullptr; 0948 QAction *action = nullptr; 0949 0950 if (mainWindow()) 0951 if ((actionCollection = ((MainWindow *)mainWindow())->actionCollection())) 0952 action = actionCollection->action(QLatin1String(name)); 0953 if (!action) 0954 qDebug() << name; 0955 Q_ASSERT(mainWindow()); 0956 Q_ASSERT(actionCollection); 0957 Q_ASSERT(action); 0958 0959 return action; 0960 } 0961 0962 bool MainWindow::isFresh() 0963 { 0964 QDate date = TheStream::profile().readEntry<QDate>("Date", QDate::currentDate()); 0965 0966 return (date.daysTo(QDate::currentDate()) < m_profileMaxDays) ? true : false; 0967 } 0968 0969 void MainWindow::contextMenuEvent(QContextMenuEvent *event) 0970 { 0971 QMenu menu; 0972 qobject_cast<KHamburgerMenu *>(action(QStringLiteral("hamburger_menu")))->addToMenu(&menu); 0973 if (menu.isEmpty()) { 0974 return; 0975 } 0976 menu.exec(event->globalPos()); 0977 } 0978 } // namespace Dragon 0979 0980 #include "moc_mainWindow.cpp"