File indexing completed on 2024-04-14 04:49:06

0001 /*
0002     SPDX-FileCopyrightText: 2002-2003 Koos Vriezen <koos.vriezen@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include <cstdio>
0008 #include <cmath>
0009 
0010 #include "config-kmplayer.h"
0011 // include files for Qt
0012 #include <QStyle>
0013 #include <QTimer>
0014 #include <QPainter>
0015 #include <QMetaObject>
0016 #include <QLayout>
0017 #include <QPixmap>
0018 #include <QTextEdit>
0019 #include <QToolTip>
0020 #include <QApplication>
0021 #include <QCursor>
0022 #include <QKeySequence>
0023 #include <QSlider>
0024 #include <QLabel>
0025 #include <QDataStream>
0026 #include <QContextMenuEvent>
0027 #include <QMimeData>
0028 #include <QDropEvent>
0029 #include <QAction>
0030 #include <QDragEnterEvent>
0031 #include <QFontDatabase>
0032 #include <QTextDocument>
0033 #include <QTextCursor>
0034 #include <QCursor>
0035 #include <QClipboard>
0036 #include <QMainWindow>
0037 #include <QStatusBar>
0038 #include <QDockWidget>
0039 
0040 #include <KIconLoader>
0041 #include <KLocalizedString>
0042 #include <KActionCollection>
0043 
0044 #include "kmplayercommon_log.h"
0045 #include "kmplayerview.h"
0046 #include "kmplayercontrolpanel.h"
0047 #include "playlistview.h"
0048 #include "viewarea.h"
0049 
0050 /* mouse invisible: define the time (in 1/1000 seconds) before mouse goes invisible */
0051 
0052 
0053 using namespace KMPlayer;
0054 
0055 //-------------------------------------------------------------------------
0056 
0057 PictureWidget::PictureWidget (QWidget * parent, View * view)
0058  : QWidget (parent), m_view (view) {
0059     setAutoFillBackground (true);
0060 }
0061 
0062 void PictureWidget::mousePressEvent (QMouseEvent *) {
0063     m_view->emitPictureClicked ();
0064 }
0065 
0066 void PictureWidget::mouseMoveEvent (QMouseEvent *e) {
0067     if (e->buttons () == Qt::NoButton)
0068         m_view->mouseMoved (e->x (), e->y ());
0069 }
0070 
0071 //-----------------------------------------------------------------------------
0072 
0073 TextEdit::TextEdit (QWidget * parent, View * view) : QTextEdit (parent), m_view (view) {
0074     setAttribute (Qt::WA_NativeWindow);
0075     setAttribute(Qt::WA_DontCreateNativeAncestors);
0076     setReadOnly (true);
0077     QPalette p=palette();
0078     p.setColor (QPalette::Active, QPalette::Base, QColor (Qt::black));
0079     p.setColor (QPalette::Active, QPalette::Foreground, (QColor (0xB2, 0xB2, 0xB2)));
0080     setPalette (p);
0081 }
0082 
0083 void TextEdit::contextMenuEvent (QContextMenuEvent * e) {
0084     m_view->controlPanel ()->popupMenu->exec (e->globalPos ());
0085 }
0086 
0087 //-----------------------------------------------------------------------------
0088 
0089 InfoWindow::InfoWindow (QWidget *, View * view)
0090  : m_view (view) {
0091     setReadOnly (true);
0092     //setLinkUnderline (false);
0093 }
0094 
0095 void InfoWindow::contextMenuEvent (QContextMenuEvent * e) {
0096     m_view->controlPanel ()->popupMenu->exec (e->globalPos ());
0097 }
0098 
0099 //-----------------------------------------------------------------------------
0100 
0101 View::View (QWidget *parent)
0102   : KMediaPlayer::View (parent),
0103     m_control_panel (nullptr),
0104     m_status_bar (nullptr),
0105     m_controlpanel_mode (CP_Show),
0106     m_old_controlpanel_mode (CP_Show),
0107     m_statusbar_mode (SB_Hide),
0108     controlbar_timer (0),
0109     infopanel_timer (0),
0110     m_restore_state_timer(0),
0111     m_powerManagerStopSleep( -1 ),
0112     m_inhibitIface("org.freedesktop.PowerManagement.Inhibit",
0113                    "/org/freedesktop/PowerManagement/Inhibit",
0114                    QDBusConnection::sessionBus()),
0115     m_keepsizeratio (false),
0116     m_playing (false),
0117     m_tmplog_needs_eol (false),
0118     m_revert_fullscreen (false),
0119     m_no_info (false),
0120     m_edit_mode (false)
0121 {
0122     setAttribute (Qt::WA_NoSystemBackground, true);
0123     setAutoFillBackground (false);
0124     setAcceptDrops(true);
0125 }
0126 
0127 void View::dropEvent (QDropEvent * de) {
0128     QList<QUrl> uris = de->mimeData()->urls();
0129     if (uris.isEmpty() || !uris[0].isValid()) {
0130         const QUrl url = QUrl::fromUserInput(de->mimeData()->text());
0131         if (url.isValid ())
0132             uris.push_back(url);
0133     }
0134     if (uris.size () > 0) {
0135         //m_widgetstack->currentWidget ()->setFocus ();
0136         Q_EMIT urlDropped (uris);
0137         de->accept ();
0138     }
0139 }
0140 
0141 void View::dragEnterEvent (QDragEnterEvent* dee) {
0142     if (m_playlist->isDragValid (dee))
0143         dee->accept ();
0144 }
0145 
0146 void View::initDock (QWidget *central) {
0147     m_dockarea = new QMainWindow;
0148     m_dockarea->setDockNestingEnabled(true);
0149     m_dockarea->setCentralWidget (central);
0150     central->setVisible (true);
0151 
0152     m_dock_playlist = new QDockWidget (i18n ("Playlist"));
0153     if (central != m_playlist)
0154         m_dock_playlist->setWidget (m_playlist);
0155     m_dock_playlist->setObjectName ("playlist");
0156 
0157     m_dock_infopanel = new QDockWidget (i18n ("Information"));
0158     if (central != m_infopanel)
0159         m_dock_infopanel->setWidget (m_infopanel);
0160     m_dock_infopanel->setObjectName ("infopanel");
0161 
0162     m_dock_playlist->hide ();
0163     m_dock_infopanel->hide ();
0164 
0165     m_dockarea->addDockWidget (Qt::BottomDockWidgetArea, m_dock_infopanel);
0166     m_dockarea->addDockWidget (Qt::LeftDockWidgetArea, m_dock_playlist);
0167 
0168     layout ()->addWidget (m_dockarea);
0169 
0170     m_dockarea->setWindowFlags (Qt::SubWindow);
0171     m_dockarea->show ();
0172 
0173     m_view_area->resizeEvent (nullptr);
0174 }
0175 
0176 void View::init (KActionCollection *action_collection, bool transparent) {
0177     QVBoxLayout * viewbox = new QVBoxLayout;
0178     viewbox->setContentsMargins (0, 0, 0, 0);
0179     setLayout (viewbox);
0180     m_view_area = new ViewArea (nullptr, this, !transparent);
0181     m_playlist = new PlayListView (nullptr, this, action_collection);
0182 
0183     m_picture = new PictureWidget (m_view_area, this);
0184     m_picture->hide ();
0185     m_control_panel = new ControlPanel (m_view_area, this);
0186     m_control_panel->setMaximumHeight(controlPanel ()->maximumSize ().height ());
0187     m_status_bar = new StatusBar (m_view_area);
0188     m_status_bar->clearMessage();
0189     m_status_bar->setAutoFillBackground (true);
0190     QSize sbsize = m_status_bar->sizeHint ();
0191     m_status_bar->hide ();
0192     m_status_bar->setMaximumHeight(sbsize.height ());
0193     setVideoWidget (m_view_area);
0194 
0195     m_multiedit = new TextEdit (m_view_area, this);
0196     QFont fnt = QFontDatabase::systemFont(QFontDatabase::FixedFont);
0197     m_multiedit->setFont (fnt);
0198     m_multiedit->hide ();
0199 
0200     m_infopanel = new InfoWindow (nullptr, this);
0201 
0202     connect (m_control_panel->scale_slider, &QSlider::valueChanged,
0203              m_view_area, &ViewArea::scale);
0204     setFocusPolicy (Qt::ClickFocus);
0205 
0206     setAcceptDrops (true);
0207 }
0208 
0209 View::~View () {
0210     if (m_view_area->parent () != this)
0211         delete m_view_area;
0212 }
0213 
0214 void View::setEraseColor (const QColor & /*color*/) {
0215     /*KMediaPlayer::View::setEraseColor (color);
0216     if (statusBar ()) {
0217         statusBar ()->setEraseColor (color);
0218         controlPanel ()->setEraseColor (color);
0219     }*/
0220 }
0221 
0222 void View::setInfoMessage (const QString & msg) {
0223     bool ismain = m_dockarea->centralWidget () == m_infopanel;
0224     if (msg.isEmpty ()) {
0225         if (!ismain && !m_edit_mode && !infopanel_timer)
0226             infopanel_timer = startTimer (0);
0227        m_infopanel->clear ();
0228     } else if (ismain || !m_no_info) {
0229         if (!ismain && !m_edit_mode && !m_dock_infopanel->isVisible ())
0230             m_dock_infopanel->show ();
0231         if (m_edit_mode)
0232             m_infopanel->setPlainText (msg);
0233         else
0234             m_infopanel->setHtml (msg);
0235     }
0236 }
0237 
0238 void View::setStatusMessage (const QString & msg) {
0239     if (m_statusbar_mode != SB_Hide)
0240         m_status_bar->showMessage(msg);
0241 }
0242 
0243 void View::toggleShowPlaylist () {
0244     if (m_controlpanel_mode == CP_Only)
0245         return;
0246     if (m_dock_playlist->isVisible ())
0247         m_dock_playlist->hide ();
0248     else
0249         m_dock_playlist->show();
0250     /*if (m_dock_playlist->mayBeShow ()) {
0251         if (m_dock_playlist->isDockBackPossible ())
0252             m_dock_playlist->dockBack ();
0253         else {
0254             bool horz = true;
0255             QStyle *style = m_playlist->style ();
0256             int h = style->pixelMetric (QStyle::PM_ScrollBarExtent, NULL, m_playlist);
0257             h += style->pixelMetric (QStyle::PM_DockWidgetFrameWidth, NULL, m_playlist);
0258             h +=style->pixelMetric (QStyle::PM_DockWidgetHandleExtent, NULL, m_playlist);
0259             for (Q3ListViewItem *i=m_playlist->firstChild();i;i=i->itemBelow()) {
0260                 h += i->height ();
0261                 if (h > int (0.25 * height ())) {
0262                     horz = false;
0263                     break;
0264                 }
0265             }
0266             int perc = 30;
0267             if (horz && 100 * h / height () < perc)
0268                 perc = 100 * h / height ();
0269             m_dock_playlist->manualDock (m_dock_video, horz ? K3DockWidget::DockTop : K3DockWidget::DockLeft, perc);
0270         }
0271     } else
0272         m_dock_playlist->undock ();*/
0273 }
0274 
0275 void View::setViewOnly () {
0276     m_dock_playlist->hide ();
0277     m_dock_infopanel->hide ();
0278 }
0279 
0280 void View::setEditMode (TopPlayItem *ri, bool enable) {
0281     m_edit_mode = enable;
0282     m_infopanel->setReadOnly (!m_edit_mode);
0283     if (m_edit_mode && !m_dock_infopanel->isVisible ())
0284         m_dock_infopanel->show ();
0285     //if (m_edit_mode && m_dock_infopanel->mayBeShow ())
0286     //    m_dock_infopanel->manualDock(m_dock_video,K3DockWidget::DockBottom,50);
0287     m_playlist->showAllNodes (ri, m_edit_mode);
0288 }
0289 
0290 #ifndef KMPLAYER_WITH_CAIRO
0291 bool View::setPicture (const QString & path) {
0292     if (path.isEmpty ())
0293         m_image = QImage ();
0294     else {
0295         m_image = QImage (path);
0296         if (m_image.isNull ())
0297             qCDebug(LOG_KMPLAYER_COMMON) << "View::setPicture failed " << path;
0298         else if (m_image.depth () < 24)
0299             m_image = m_image.convertToFormat (QImage::Format_RGB32);
0300     }
0301     m_picture->setVisible (!m_image.isNull ());
0302     if (m_image.isNull ()) {
0303         m_view_area->setVideoWidgetVisible (true);
0304     } else {
0305         QPalette palette = m_picture->palette ();
0306         palette.setColor (m_picture->backgroundRole(), viewArea()->palette ().color (backgroundRole ()));
0307         palette.setBrush (m_picture->backgroundRole(), QBrush (m_image));
0308         m_picture->setPalette (palette);
0309         m_view_area->setVideoWidgetVisible (false);
0310         controlPanel ()->raise ();
0311         setControlPanelMode (CP_AutoHide);
0312     }
0313     return !m_image.isNull ();
0314 }
0315 #endif
0316 
0317 void View::toggleVideoConsoleWindow () {
0318     if (m_multiedit->isVisible ()) {
0319         m_multiedit->hide ();
0320         m_view_area->setVideoWidgetVisible (true);
0321         m_control_panel->videoConsoleAction->setIcon(QIcon::fromTheme(("konsole")));
0322         m_control_panel->videoConsoleAction->setText (i18n ("Con&sole"));
0323         delayedShowButtons (false);
0324     } else {
0325         m_control_panel->videoConsoleAction->setIcon(QIcon::fromTheme("video"));
0326         m_control_panel->videoConsoleAction->setText (i18n ("V&ideo"));
0327         m_multiedit->show ();
0328         m_multiedit->raise ();
0329         m_view_area->setVideoWidgetVisible (false);
0330         addText (QString (""), false);
0331         if (m_controlpanel_mode == CP_AutoHide && m_playing)
0332             m_control_panel->show();
0333     }
0334     updateLayout ();
0335     Q_EMIT windowVideoConsoleToggled (m_multiedit->isVisible ());
0336 }
0337 
0338 void View::setControlPanelMode (ControlPanelMode m) {
0339     if (controlbar_timer) {
0340         killTimer (controlbar_timer);
0341         controlbar_timer = 0L;
0342     }
0343     m_old_controlpanel_mode = m_controlpanel_mode = m;
0344     if (m_playing && isFullScreen())
0345         m_controlpanel_mode = CP_AutoHide;
0346     if ((m_controlpanel_mode == CP_Show || m_controlpanel_mode == CP_Only) &&
0347             !m_control_panel->isVisible ()) {
0348         m_control_panel->show ();
0349     } else if (m_controlpanel_mode == CP_AutoHide) {
0350         if (!m_image.isNull () || (m_playing && !m_multiedit->isVisible ()))
0351             delayedShowButtons (false);
0352         else if (!m_control_panel->isVisible ()) {
0353             m_control_panel->show ();
0354         }
0355     } else if (m_controlpanel_mode == CP_Hide) {
0356         bool vis = m_control_panel->isVisible();
0357         m_control_panel->hide ();
0358         if (vis)
0359             m_view_area->resizeEvent (nullptr);
0360     }
0361     m_view_area->resizeEvent (nullptr);
0362 }
0363 
0364 void View::setStatusBarMode (StatusBarMode m) {
0365     m_statusbar_mode = m;
0366     m_status_bar->setVisible (m != SB_Hide);
0367     m_view_area->resizeEvent (nullptr);
0368 }
0369 
0370 void View::delayedShowButtons (bool show) {
0371     if ((show && m_control_panel->isVisible ()) ||
0372             (!show && !m_control_panel->isVisible ())) {
0373         if (controlbar_timer) {
0374             killTimer (controlbar_timer);
0375             controlbar_timer = 0;
0376         }
0377         if (!show)
0378             m_control_panel->hide (); // for initial race
0379     } else if (m_controlpanel_mode == CP_AutoHide &&
0380             (m_playing || !m_image.isNull ()) &&
0381             !m_multiedit->isVisible () && !controlbar_timer) {
0382         controlbar_timer = startTimer (500);
0383     }
0384 }
0385 
0386 void View::mouseMoved (int, int y) {
0387     int h = m_view_area->height ();
0388     int vert_buttons_pos = h - statusBarHeight ();
0389     int cp_height = controlPanel ()->maximumSize ().height ();
0390     if (cp_height > int (0.25 * h))
0391         cp_height = int (0.25 * h);
0392     delayedShowButtons (y > vert_buttons_pos-cp_height && y < vert_buttons_pos);
0393 }
0394 
0395 void View::updateLayout () {
0396     if (m_controlpanel_mode == CP_Only)
0397         m_control_panel->setMaximumHeight(height());
0398     m_view_area->resizeEvent (nullptr);
0399 }
0400 
0401 void View::setKeepSizeRatio (bool b) {
0402     if (m_keepsizeratio != b) {
0403         m_keepsizeratio = b;
0404         updateLayout ();
0405         m_view_area->update ();
0406     }
0407 }
0408 
0409 void View::timerEvent (QTimerEvent * e) {
0410     if (e->timerId () == controlbar_timer) {
0411         controlbar_timer = 0;
0412         if (m_playing || !m_image.isNull ()) {
0413             int vert_buttons_pos = m_view_area->height()-statusBarHeight ();
0414             QPoint mouse_pos = m_view_area->mapFromGlobal (QCursor::pos ());
0415             int cp_height = m_control_panel->maximumSize ().height ();
0416             bool mouse_on_buttons = (//m_view_area->hasMouse () &&
0417                     mouse_pos.y () >= vert_buttons_pos-cp_height &&
0418                     mouse_pos.y ()<= vert_buttons_pos &&
0419                     mouse_pos.x () > 0 &&
0420                     mouse_pos.x () < m_control_panel->width());
0421             if (mouse_on_buttons && !m_control_panel->isVisible ()) {
0422                 m_control_panel->show ();
0423                 m_view_area->resizeEvent (nullptr);
0424             } else if (!mouse_on_buttons && m_control_panel->isVisible ()) {
0425                 m_control_panel->hide ();
0426                 m_view_area->resizeEvent (nullptr);
0427             }
0428         }
0429     } else if (e->timerId () == infopanel_timer) {
0430         if (m_infopanel->document ()->isEmpty ())
0431             m_dock_infopanel->hide ();
0432             //m_dock_infopanel->undock ();
0433         infopanel_timer  = 0;
0434     } else if (e->timerId () == m_restore_state_timer) {
0435         m_view_area->setVisible(true);
0436         setControlPanelMode (m_old_controlpanel_mode);
0437         m_dockarea->restoreState(m_dock_state);
0438         m_restore_state_timer = 0;
0439     }
0440     killTimer (e->timerId ());
0441 }
0442 
0443 void View::addText (const QString & str, bool eol) {
0444     if (m_tmplog_needs_eol)
0445         tmplog += QChar ('\n');
0446     tmplog += str;
0447     m_tmplog_needs_eol = eol;
0448     if (!m_multiedit->isVisible () && tmplog.size () < 7500)
0449         return;
0450     if (eol) {
0451         if (m_multiedit->document ()->isEmpty ())
0452             m_multiedit->setPlainText (tmplog);
0453         else
0454             m_multiedit->append (tmplog);
0455         tmplog.truncate (0);
0456         m_tmplog_needs_eol = false;
0457     } else {
0458         int pos = tmplog.lastIndexOf (QChar ('\n'));
0459         if (pos >= 0) {
0460             m_multiedit->append (tmplog.left (pos));
0461             tmplog = tmplog.mid (pos+1);
0462         }
0463     }
0464     QTextCursor cursor = m_multiedit->textCursor ();
0465     cursor.movePosition (QTextCursor::End);
0466     cursor.movePosition (QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, 5000);
0467     cursor.movePosition (QTextCursor::Start, QTextCursor::KeepAnchor);
0468     cursor.removeSelectedText ();
0469     cursor.movePosition (QTextCursor::End);
0470     m_multiedit->setTextCursor (cursor);
0471 }
0472 
0473 void View::videoStart () {
0474     if (!isFullScreen () && m_dockarea->centralWidget () != m_view_area) {
0475         // restore from an info or playlist only setting
0476         if (m_dockarea->centralWidget () == m_playlist)
0477             m_dock_playlist->setWidget (m_playlist);
0478         else if (m_dockarea->centralWidget () == m_infopanel)
0479             m_dock_infopanel->setWidget (m_infopanel);
0480         else
0481             m_status_bar->setVisible (false);
0482         m_dockarea->setCentralWidget (m_view_area);
0483     }
0484     if (m_controlpanel_mode == CP_Only) {
0485         m_control_panel->setMaximumHeight(controlPanel()->preferredHeight());
0486         setControlPanelMode (CP_Show);
0487     }
0488 }
0489 
0490 void View::playingStart () {
0491     if (m_playing)
0492         return; //FIXME: make symetric with playingStop
0493     m_playing = true;
0494     m_revert_fullscreen = !isFullScreen();
0495     setControlPanelMode (m_old_controlpanel_mode);
0496 }
0497 
0498 void View::playingStop () {
0499     if (m_controlpanel_mode == CP_AutoHide && m_image.isNull ()) {
0500         m_control_panel->show ();
0501         //m_view_area->setMouseTracking (false);
0502     }
0503     if (controlbar_timer) {
0504         killTimer (controlbar_timer);
0505         controlbar_timer = 0;
0506     }
0507     m_playing = false;
0508     m_view_area->resizeEvent (nullptr);
0509 }
0510 
0511 void View::leaveEvent (QEvent *) {
0512     delayedShowButtons (false);
0513 }
0514 
0515 void View::reset () {
0516     if (m_revert_fullscreen && isFullScreen ())
0517         m_control_panel->fullscreenAction->activate (QAction::Trigger);
0518         //m_view_area->fullScreen ();
0519     playingStop ();
0520 }
0521 
0522 bool View::isFullScreen () const {
0523     return m_view_area->isFullScreen ();
0524 }
0525 
0526 void View::fullScreen () {
0527     if (m_restore_state_timer) {
0528         killTimer (m_restore_state_timer);
0529         m_restore_state_timer = 0;
0530     }
0531     if (!m_view_area->isFullScreen()) {
0532         m_sreensaver_disabled = false;
0533         QDBusReply<uint> reply = m_inhibitIface.Inhibit(QCoreApplication::applicationName(), "KMplayer: watching a film");
0534 
0535         m_powerManagerStopSleep = reply.isValid() ? reply : -1;
0536 
0537         /*QByteArray data, replydata;
0538         QCString replyType;
0539         if (kapp->dcopClient ()->call ("kdesktop", "KScreensaverIface",
0540                     "isEnabled()", data, replyType, replydata)) {
0541             bool enabled;
0542             QDataStream replystream (replydata, IO_ReadOnly);
0543             replystream >> enabled;
0544             if (enabled)
0545                 m_sreensaver_disabled = kapp->dcopClient()->send
0546                     ("kdesktop", "KScreensaverIface", "enable(bool)", "false");
0547         }*/
0548         //if (m_keepsizeratio && m_viewer->aspect () < 0.01)
0549         //    m_viewer->setAspect (1.0 * m_viewer->width() / m_viewer->height());
0550         m_dock_state = m_dockarea->saveState();
0551         m_dock_playlist->hide();
0552         m_dock_infopanel->hide();
0553         m_view_area->fullScreen();
0554         m_control_panel->zoomAction->setVisible (false);
0555         //if (m_viewer->isVisible ())
0556         //    m_viewer->setFocus ();
0557     } else {
0558         m_inhibitIface.UnInhibit(m_powerManagerStopSleep);
0559        // if (m_sreensaver_disabled)
0560        //     m_sreensaver_disabled = !kapp->dcopClient()->send
0561        //         ("kdesktop", "KScreensaverIface", "enable(bool)", "true");
0562         m_view_area->fullScreen();
0563         m_control_panel->zoomAction->setVisible (true);
0564         m_restore_state_timer = startTimer(100); //dockArea()->restoreState(m_dock_state);
0565     }
0566     setControlPanelMode (m_old_controlpanel_mode);
0567     Q_EMIT fullScreenChanged ();
0568 }
0569 
0570 int View::statusBarHeight () const {
0571     if (statusBar()->isVisible () && !viewArea()->isFullScreen ()) {
0572         if (statusBarMode () == SB_Only)
0573             return height ();
0574         else
0575             return statusBar()->maximumSize ().height ();
0576     }
0577     return 0;
0578 }
0579 
0580 #include "moc_kmplayerview.cpp"