diff --git a/src/dvb/dvbtab.cpp b/src/dvb/dvbtab.cpp index f48ca6b..03d0528 100644 --- a/src/dvb/dvbtab.cpp +++ b/src/dvb/dvbtab.cpp @@ -1,497 +1,521 @@ /* * dvbtab.cpp * * Copyright (C) 2007-2011 Christoph Pfister * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "../log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../osdwidget.h" #include "dvbchanneldialog.h" #include "dvbconfigdialog.h" #include "dvbepg.h" #include "dvbepgdialog.h" #include "dvbliveview.h" #include "dvbmanager.h" #include "dvbrecordingdialog.h" #include "dvbscandialog.h" #include "dvbtab.h" class DvbTimeShiftCleaner : public QThread { public: explicit DvbTimeShiftCleaner(QObject *parent) : QThread(parent) { } ~DvbTimeShiftCleaner() { wait(); } void remove(const QString &path_, const QStringList &files_); private: void run(); QString path; QStringList files; }; void DvbTimeShiftCleaner::remove(const QString &path_, const QStringList &files_) { path = path_; files = files_; start(); } void DvbTimeShiftCleaner::run() { // delete files asynchronously because it may block for several seconds foreach (const QString &file, files) { QFile::remove(path + QLatin1Char('/') + file); } } DvbTab::DvbTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_) : mediaWidget(mediaWidget_) { manager = new DvbManager(mediaWidget, this); QAction *channelsAction = new QAction(QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")), i18n("Channels"), this); channelsAction->setShortcut(Qt::Key_C); connect(channelsAction, SIGNAL(triggered(bool)), this, SLOT(showChannelDialog())); menu->addAction(collection->addAction(QLatin1String("dvb_channels"), channelsAction)); QAction *epgAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-details"), QIcon(":view-list-details")), i18n("Program Guide"), this); epgAction->setShortcut(Qt::Key_G); connect(epgAction, SIGNAL(triggered(bool)), this, SLOT(toggleEpgDialog())); menu->addAction(collection->addAction(QLatin1String("dvb_epg"), epgAction)); QAction *osdAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-information"), QIcon(":dialog-information")), i18n("OSD"), this); osdAction->setShortcut(Qt::Key_O); connect(osdAction, SIGNAL(triggered(bool)), manager->getLiveView(), SLOT(toggleOsd())); menu->addAction(collection->addAction(QLatin1String("dvb_osd"), osdAction)); QAction *recordingsAction = new QAction(QIcon::fromTheme(QLatin1String("view-pim-calendar"), QIcon(":view-pim-calendar")), i18nc("dialog", "Recording Schedule"), this); recordingsAction->setShortcut(Qt::Key_R); connect(recordingsAction, SIGNAL(triggered(bool)), this, SLOT(showRecordingDialog())); menu->addAction(collection->addAction(QLatin1String("dvb_recordings"), recordingsAction)); menu->addSeparator(); instantRecordAction = new QAction(QIcon::fromTheme(QLatin1String("document-save"), QIcon(":document-save")), i18n("Instant Record"), this); instantRecordAction->setCheckable(true); connect(instantRecordAction, SIGNAL(triggered(bool)), this, SLOT(instantRecord(bool))); menu->addAction(collection->addAction(QLatin1String("dvb_instant_record"), instantRecordAction)); menu->addSeparator(); QAction *configureAction = new QAction(QIcon::fromTheme(QLatin1String("configure"), QIcon(":configure")), i18nc("@action:inmenu", "Configure Television..."), this); connect(configureAction, SIGNAL(triggered()), this, SLOT(configureDvb())); menu->addAction(collection->addAction(QLatin1String("settings_dvb"), configureAction)); connect(manager->getLiveView(), SIGNAL(previous()), this, SLOT(previousChannel())); connect(manager->getLiveView(), SIGNAL(next()), this, SLOT(nextChannel())); connect(manager->getRecordingModel(), SIGNAL(recordingRemoved(DvbSharedRecording)), this, SLOT(recordingRemoved(DvbSharedRecording))); QBoxLayout *boxLayout = new QHBoxLayout(this); boxLayout->setMargin(0); splitter = new QSplitter(this); boxLayout->addWidget(splitter); leftWidget = new QWidget(splitter); QBoxLayout *leftLayout = new QVBoxLayout(leftWidget); boxLayout = new QHBoxLayout(); boxLayout->addWidget(new QLabel(i18n("Search:"))); QLineEdit *lineEdit = new QLineEdit(leftWidget); lineEdit->setClearButtonEnabled(true); boxLayout->addWidget(lineEdit); leftLayout->addLayout(boxLayout); channelView = new DvbChannelView(leftWidget); channelView->setContextMenuPolicy(Qt::ActionsContextMenu); channelProxyModel = new DvbChannelTableModel(this); channelView->setModel(channelProxyModel); channelView->setRootIsDecorated(false); if (!channelView->header()->restoreState(QByteArray::fromBase64( KSharedConfig::openConfig()->group("DVB").readEntry("ChannelViewState", QByteArray())))) { channelView->sortByColumn(0, Qt::AscendingOrder); } channelView->setSortingEnabled(true); channelView->addEditAction(); connect(channelView, SIGNAL(activated(QModelIndex)), this, SLOT(playChannel(QModelIndex))); channelProxyModel->setChannelModel(manager->getChannelModel()); connect(lineEdit, SIGNAL(textChanged(QString)), channelProxyModel, SLOT(setFilter(QString))); manager->setChannelView(channelView); leftLayout->addWidget(channelView); boxLayout = new QHBoxLayout(); const QSize BUTTON_SIZE = QSize(22, 22); QToolButton *toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(configureAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(channelsAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(epgAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(recordingsAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); toolButton = new QToolButton(leftWidget); toolButton->setDefaultAction(instantRecordAction); toolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); toolButton->setFixedSize(BUTTON_SIZE); boxLayout->addWidget(toolButton); leftLayout->addLayout(boxLayout); QWidget *mediaContainer = new QWidget(splitter); mediaLayout = new QHBoxLayout(mediaContainer); mediaLayout->setMargin(0); splitter->setStretchFactor(1, 1); connect(mediaWidget, SIGNAL(osdKeyPressed(int)), this, SLOT(osdKeyPressed(int))); connect(&osdChannelTimer, SIGNAL(timeout()), this, SLOT(tuneOsdChannel())); lastChannel = KSharedConfig::openConfig()->group("DVB").readEntry("LastChannel"); splitter->restoreState(QByteArray::fromBase64( KSharedConfig::openConfig()->group("DVB").readEntry("TabSplitterState", QByteArray()))); timeShiftCleaner = new DvbTimeShiftCleaner(this); QTimer *timer = new QTimer(this); timer->start(30000); connect(timer, SIGNAL(timeout()), this, SLOT(cleanTimeShiftFiles())); + + autoHideMenu = false; + cursorHideTimer = new QTimer(this); + cursorHideTimer->setInterval(1500); + cursorHideTimer->setSingleShot(true); + connect(cursorHideTimer, SIGNAL(timeout()), this, SLOT(hideCursor())); + } DvbTab::~DvbTab() { KSharedConfig::openConfig()->group("DVB").writeEntry("TabSplitterState", splitter->saveState().toBase64()); KSharedConfig::openConfig()->group("DVB").writeEntry("ChannelViewState", channelView->header()->saveState().toBase64()); if (!currentChannel.isEmpty()) { lastChannel = currentChannel; } KSharedConfig::openConfig()->group("DVB").writeEntry("LastChannel", lastChannel); } void DvbTab::playChannel(const QString &nameOrNumber) { DvbChannelModel *channelModel = manager->getChannelModel(); DvbSharedChannel channel; int number = nameOrNumber.toInt(); if (number > 0) { channel = channelModel->findChannelByNumber(number); } if (!channel.isValid()) { channel = channelModel->findChannelByName(nameOrNumber); } if (channel.isValid()) { playChannel(channel, channelProxyModel->find(channel)); } } void DvbTab::playLastChannel() { if (!manager->getLiveView()->getChannel().isValid() && !currentChannel.isEmpty()) { lastChannel = currentChannel; } DvbSharedChannel channel = manager->getChannelModel()->findChannelByName(lastChannel); if (channel.isValid()) { playChannel(channel, channelProxyModel->find(channel)); } } void DvbTab::toggleOsd() { manager->getLiveView()->toggleOsd(); } void DvbTab::toggleInstantRecord() { instantRecordAction->trigger(); } void DvbTab::enableDvbDump() { manager->enableDvbDump(); } +void DvbTab::mouse_move(int x, int) +{ + cursorHideTimer->stop(); + unsetCursor(); + + leftWidget->setVisible(x < 60); + + if (leftWidget->isHidden()) { + cursorHideTimer->start(); + } + +} + void DvbTab::toggleDisplayMode(MediaWidget::DisplayMode displayMode) { switch (displayMode) { case MediaWidget::FullScreenMode: case MediaWidget::FullScreenReturnToMinimalMode: case MediaWidget::MinimalMode: leftWidget->hide(); + autoHideMenu = true; + cursorHideTimer->start(); break; case MediaWidget::NormalMode: leftWidget->show(); + autoHideMenu = false; + cursorHideTimer->stop(); break; } } void DvbTab::osdKeyPressed(int key) { if ((key >= Qt::Key_0) && (key <= Qt::Key_9)) { osdChannel += QString::number(key - Qt::Key_0); osdChannelTimer.start(1500); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Channel: %1_", osdChannel), 1500); } } void DvbTab::mayCloseApplication(bool *ok, QWidget *parent) { if (*ok) { DvbRecordingModel *recordingModel = manager->getRecordingModel(); if (recordingModel->hasActiveRecordings()) { if (KMessageBox::warningYesNo(parent, i18nc("message box", "Kaffeine is currently recording programs.\n" "Do you really want to close the application?")) != KMessageBox::Yes) { *ok = false; } return; } if (recordingModel->hasRecordings()) { if (KMessageBox::questionYesNo(parent, i18nc("message box", "Kaffeine has scheduled recordings.\n" "Do you really want to close the application?"), QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QLatin1String("ScheduledRecordings")) != KMessageBox::Yes) { *ok = false; } return; } } } void DvbTab::showChannelDialog() { QDialog *dialog = new DvbScanDialog(manager, this); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(true); dialog->show(); } void DvbTab::showRecordingDialog() { DvbRecordingDialog::showDialog(manager, this); } void DvbTab::toggleEpgDialog() { if (epgDialog.isNull()) { epgDialog = new DvbEpgDialog(manager, this); epgDialog->setAttribute(Qt::WA_DeleteOnClose, true); epgDialog->setCurrentChannel(manager->getLiveView()->getChannel()); epgDialog->setModal(false); epgDialog->show(); } else { epgDialog->deleteLater(); epgDialog = NULL; } } void DvbTab::instantRecord(bool checked) { if (checked) { const DvbSharedChannel &channel = manager->getLiveView()->getChannel(); if (!channel.isValid()) { instantRecordAction->setChecked(false); return; } DvbRecording recording; QList epgEntries = manager->getEpgModel()->getCurrentNext(channel); if (!epgEntries.isEmpty()) { recording.name = epgEntries.at(0)->title(); } if (recording.name.isEmpty()) { recording.name = (channel->name + QTime::currentTime().toString(QLatin1String("-hhmmss"))); } recording.channel = channel; recording.begin = QDateTime::currentDateTime().toUTC(); recording.duration = QTime(12, 0); instantRecording = manager->getRecordingModel()->addRecording(recording); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Started"), 1500); } else { manager->getRecordingModel()->removeRecording(instantRecording); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Stopped"), 1500); } } void DvbTab::recordingRemoved(const DvbSharedRecording &recording) { if (instantRecording == recording) { instantRecording = DvbSharedRecording(); instantRecordAction->setChecked(false); mediaWidget->getOsdWidget()->showText(i18nc("osd", "Instant Record Stopped"), 1500); } } void DvbTab::configureDvb() { QDialog *dialog = new DvbConfigDialog(manager, this); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(true); dialog->show(); } void DvbTab::tuneOsdChannel() { int number = osdChannel.toInt(); osdChannel.clear(); osdChannelTimer.stop(); DvbSharedChannel channel = manager->getChannelModel()->findChannelByNumber(number); if (channel.isValid()) { playChannel(channel, channelProxyModel->find(channel)); } } void DvbTab::playChannel(const QModelIndex &index) { if (index.isValid()) { playChannel(channelProxyModel->value(index), index); } } void DvbTab::previousChannel() { QModelIndex index = channelView->currentIndex(); if (index.isValid()) { playChannel(index.sibling(index.row() - 1, index.column())); } } void DvbTab::nextChannel() { QModelIndex index = channelView->currentIndex(); if (index.isValid()) { playChannel(index.sibling(index.row() + 1, index.column())); } } void DvbTab::cleanTimeShiftFiles() { if (timeShiftCleaner->isRunning()) { return; } QDir dir(manager->getTimeShiftFolder()); QStringList entries = dir.entryList(QStringList(QLatin1String("TimeShift-*.m2t")), QDir::Files, QDir::Name); if (entries.count() < 2) { return; } entries.removeLast(); timeShiftCleaner->remove(dir.path(), entries); } void DvbTab::activate() { mediaLayout->addWidget(mediaWidget); mediaWidget->setFocus(); } void DvbTab::playChannel(const DvbSharedChannel &channel, const QModelIndex &index) { if (!channel.isValid()) { qCWarning(logDvb, "Channel is invalid"); return; } if (!currentChannel.isEmpty()) { lastChannel = currentChannel; } channelView->setCurrentIndex(index); currentChannel = channel->name; manager->getLiveView()->playChannel(channel); if (!epgDialog.isNull()) { epgDialog->setCurrentChannel(manager->getLiveView()->getChannel()); } } diff --git a/src/dvb/dvbtab.h b/src/dvb/dvbtab.h index 195803d..76c1353 100644 --- a/src/dvb/dvbtab.h +++ b/src/dvb/dvbtab.h @@ -1,114 +1,117 @@ /* * dvbtab.h * * Copyright (C) 2007-2011 Christoph Pfister * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DVBTAB_H #define DVBTAB_H #include #include #include #include "../tabbase.h" #include "../mediawidget.h" #include "dvbrecording.h" class QModelIndex; class QSplitter; class QAction; class KActionCollection; class QMenu; class DvbChannelTableModel; class DvbChannelView; class DvbEpgDialog; class DvbTimeShiftCleaner; class MediaWidget; class DvbTab : public TabBase { Q_OBJECT public: DvbTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_); ~DvbTab(); void playChannel(const QString &nameOrNumber); void playLastChannel(); void toggleOsd(); void toggleInstantRecord(); void toggleDisplayMode(MediaWidget::DisplayMode displayMode); + void mouse_move(int x, int y); DvbManager *getManager() const { return manager; } void enableDvbDump(); public slots: void osdKeyPressed(int key); void mayCloseApplication(bool *ok, QWidget *parent); private slots: void showChannelDialog(); void toggleEpgDialog(); void showRecordingDialog(); void instantRecord(bool checked); void recordingRemoved(const DvbSharedRecording &recording); void configureDvb(); void tuneOsdChannel(); void playChannel(const QModelIndex &index); void previousChannel(); void nextChannel(); void cleanTimeShiftFiles(); private: void activate(); void playChannel(const DvbSharedChannel &channel, const QModelIndex &index); MediaWidget *mediaWidget; DvbManager *manager; QAction *instantRecordAction; DvbSharedRecording instantRecording; QSplitter *splitter; QWidget *leftWidget; DvbChannelTableModel *channelProxyModel; DvbChannelView *channelView; QPointer epgDialog; QLayout *mediaLayout; QString osdChannel; QTimer osdChannelTimer; QString currentChannel; QString lastChannel; + bool autoHideMenu; + QTimer *cursorHideTimer; DvbTimeShiftCleaner *timeShiftCleaner; }; #ifndef HAVE_DVB #error HAVE_DVB must be defined #endif /* HAVE_DVB */ #if HAVE_DVB == 0 inline void DvbTab::playChannel(QString const &) { } inline void DvbTab::playLastChannel() { } inline void DvbTab::toggleOsd() { } inline void DvbTab::toggleInstantRecord() { } inline void DvbTab::osdKeyPressed(int) { } #endif /* HAVE_DVB == 0 */ #endif /* DVBTAB_H */ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ad665c1..4d97c0d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,844 +1,848 @@ /* * mainwindow.cpp * * Copyright (C) 2007-2011 Christoph Pfister * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configuration.h" #include "configurationdialog.h" #include "dbusobjects.h" #include "dvb/dvbtab.h" #include "mainwindow.h" #include "playlist/playlisttab.h" // log categories. Should match log.h Q_LOGGING_CATEGORY(logCam, "kaffeine.cam") Q_LOGGING_CATEGORY(logDev, "kaffeine.dev") Q_LOGGING_CATEGORY(logDvb, "kaffeine.dvb") Q_LOGGING_CATEGORY(logDvbSi, "kaffeine.dvbsi") Q_LOGGING_CATEGORY(logEpg, "kaffeine.epg") Q_LOGGING_CATEGORY(logConfig, "kaffeine.config") Q_LOGGING_CATEGORY(logMediaWidget, "kaffeine.mediawidget") Q_LOGGING_CATEGORY(logPlaylist, "kaffeine.playlist") Q_LOGGING_CATEGORY(logSql, "kaffeine.sql") Q_LOGGING_CATEGORY(logVlc, "kaffeine.vlc") #define FILTER_RULE "kaffeine.*.debug=true" #define CATEGORIES "cam, dev, dvb, dvbsi, epg, config, mediawidget, playlist, sql, vlc" class StackedLayout : public QStackedLayout { public: explicit StackedLayout(QWidget *parent) : QStackedLayout(parent) { } ~StackedLayout() { } QSize minimumSize() const { QWidget *widget = currentWidget(); if (widget != NULL) { return widget->minimumSizeHint(); } return QSize(); } }; class StartTab : public TabBase { public: explicit StartTab(MainWindow *mainWindow); ~StartTab() { } private: void activate() { } QAbstractButton *addShortcut(const QString &name, const QIcon &icon, QWidget *parent); }; StartTab::StartTab(MainWindow *mainWindow) { setBackgroundRole(QPalette::Base); setAutoFillBackground(true); QGridLayout *gridLayout = new QGridLayout(this); gridLayout->setAlignment(Qt::AlignCenter); gridLayout->setMargin(10); gridLayout->setSpacing(15); QAbstractButton *button = addShortcut(i18n("&1 Play File"), QIcon::fromTheme(QLatin1String("video-x-generic"), QIcon(":video-x-generic")), this); button->setShortcut(Qt::Key_1); button->setWhatsThis(i18n("Open dialog to play a file")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(open())); gridLayout->addWidget(button, 0, 0); button = addShortcut(i18n("&2 Play Audio CD"), QIcon::fromTheme(QLatin1String("media-optical-audio"), QIcon(":media-optical-audio")), this); button->setShortcut(Qt::Key_2); button->setWhatsThis(i18n("Start playing an audio CD. It assumes that the CD is already there at the CD drive")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(openAudioCd())); gridLayout->addWidget(button, 0, 1); button = addShortcut(i18n("&3 Play Video CD"), QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), this); button->setShortcut(Qt::Key_3); button->setWhatsThis(i18n("Start playing a Video CD. It assumes that the CD is already there at the CD drive")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(openVideoCd())); gridLayout->addWidget(button, 0, 2); button = addShortcut(i18n("&4 Play DVD"), QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), this); button->setShortcut(Qt::Key_4); button->setWhatsThis(i18n("Start playing a DVD. It assumes that the DVD is already there at the DVD drive")); connect(button, SIGNAL(clicked()), mainWindow, SLOT(openDvd())); gridLayout->addWidget(button, 1, 0); #if HAVE_DVB == 1 button = addShortcut(i18n("&5 Digital TV"), QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")), this); button->setShortcut(Qt::Key_5); button->setWhatsThis("Open the Digital TV live view window. If the TV channels are already scanned, it will start playing the last channel"); connect(button, SIGNAL(clicked()), mainWindow, SLOT(playDvb())); gridLayout->addWidget(button, 1, 1); #endif /* HAVE_DVB == 1 */ } QAbstractButton *StartTab::addShortcut(const QString &name, const QIcon &icon, QWidget *parent) { // QPushButton has visual problems with big icons QToolButton *button = new QToolButton(parent); button->setText(name); button->setIcon(icon); button->setFocusPolicy(Qt::NoFocus); button->setIconSize(QSize(48, 48)); button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); return button; } class PlayerTab : public TabBase { public: explicit PlayerTab(MediaWidget *mediaWidget_); ~PlayerTab() { } void activate() { layout()->addWidget(mediaWidget); mediaWidget->setFocus(); } private: MediaWidget *mediaWidget; }; PlayerTab::PlayerTab(MediaWidget *mediaWidget_) : mediaWidget(mediaWidget_) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setMargin(0); } void MainWindow::run() { // Allow the user to enable or disable debugging // We handle this before the other parameters, as it may affect some // early debug messages // // --debug enables all debugging categories. It is possible to enable // each category individually by calling Kaffeine with: // QT_LOGGING_RULES="epg.debug=true" kaffeine if (parser->isSet("debug")) { QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true); QLoggingCategory::setFilterRules(QStringLiteral(FILTER_RULE)); } else { QLoggingCategory::setFilterRules(QStringLiteral("kaffeine.*.debug=false")); } readSettings(); setAttribute(Qt::WA_DeleteOnClose, true); // menu structure QMenuBar *menuBar = QMainWindow::menuBar(); collection = new KActionCollection(this); QMenu *menu = new QMenu(i18n("&File"), this); menuBar->addMenu(menu); QAction *action = KStandardAction::open(this, SLOT(open()), collection); menu->addAction(collection->addAction(QLatin1String("file_open"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("text-html"), QIcon(":text-html")), i18nc("@action:inmenu", "Open URL..."), collection); action->setShortcut(Qt::CTRL | Qt::Key_U); connect(action, SIGNAL(triggered(bool)), this, SLOT(openUrl())); menu->addAction(collection->addAction(QLatin1String("file_open_url"), action)); actionOpenRecent = KStandardAction::openRecent(this, SLOT(openUrl(QUrl)), collection); actionOpenRecent->loadEntries(KSharedConfig::openConfig()->group("Recent Files")); menu->addAction(collection->addAction(QLatin1String("file_open_recent"), actionOpenRecent)); menu->addSeparator(); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical-audio"), QIcon(":media-optical-audio")), i18n("Play Audio CD"), collection); connect(action, SIGNAL(triggered(bool)), this, SLOT(openAudioCd())); menu->addAction(collection->addAction(QLatin1String("file_play_audiocd"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), i18n("Play Video CD"), collection); connect(action, SIGNAL(triggered(bool)), this, SLOT(openVideoCd())); menu->addAction(collection->addAction(QLatin1String("file_play_videocd"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), i18n("Play DVD"), collection); connect(action, SIGNAL(triggered(bool)), this, SLOT(openDvd())); menu->addAction(collection->addAction(QLatin1String("file_play_dvd"), action)); action = new QAction(QIcon::fromTheme(QLatin1String("media-optical"), QIcon(":media-optical")), i18nc("@action:inmenu", "Play DVD Folder"), collection); connect(action, SIGNAL(triggered()), this, SLOT(playDvdFolder())); menu->addAction(collection->addAction(QLatin1String("file_play_dvd_folder"), action)); menu->addSeparator(); action = KStandardAction::quit(this, SLOT(close()), collection); menu->addAction(collection->addAction(QLatin1String("file_quit"), action)); QMenu *playerMenu = new QMenu(i18n("&Playback"), this); menuBar->addMenu(playerMenu); QMenu *playlistMenu = new QMenu(i18nc("menu bar", "Play&list"), this); menuBar->addMenu(playlistMenu); #if HAVE_DVB == 1 QMenu *dvbMenu = new QMenu(i18n("&Television"), this); menuBar->addMenu(dvbMenu); #endif /* HAVE_DVB == 1 */ menu = new QMenu(i18n("&Settings"), this); menuBar->addMenu(menu); action = KStandardAction::keyBindings(this, SLOT(configureKeys()), collection); menu->addAction(collection->addAction(QLatin1String("settings_keys"), action)); action = KStandardAction::preferences(this, SLOT(configureKaffeine()), collection); menu->addAction(collection->addAction(QLatin1String("settings_kaffeine"), action)); menuBar->addSeparator(); KHelpMenu *helpMenu = new KHelpMenu(this, *aboutData); menuBar->addMenu(helpMenu->menu()); // navigation bar - keep in sync with TabIndex enum! navigationBar = new QToolBar(QLatin1String("navigation_bar")); this->addToolBar(Qt::LeftToolBarArea, navigationBar); connect(navigationBar, SIGNAL(orientationChanged(Qt::Orientation)), this, SLOT(navigationBarOrientationChanged(Qt::Orientation))); tabBar = new QTabBar(navigationBar); tabBar->addTab(QIcon::fromTheme(QLatin1String("start-here-kde"), QIcon(":start-here-kde")), i18n("Start")); tabBar->addTab(QIcon::fromTheme(QLatin1String("kaffeine"), QIcon(":kaffeine")), i18n("Playback")); tabBar->addTab(QIcon::fromTheme(QLatin1String("view-media-playlist"), QIcon(":view-media-playlist")), i18n("Playlist")); #if HAVE_DVB == 1 tabBar->addTab(QIcon::fromTheme(QLatin1String("video-television"), QIcon(":video-television")), i18n("Television")); #endif /* HAVE_DVB == 1 */ tabBar->setShape(QTabBar::RoundedWest); tabBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); connect(tabBar, SIGNAL(currentChanged(int)), this, SLOT(activateTab(int))); navigationBar->addWidget(tabBar); // control bar controlBar = new QToolBar(QLatin1String("control_bar")); this->addToolBar(Qt::BottomToolBarArea, controlBar); controlBar->setToolButtonStyle(Qt::ToolButtonIconOnly); autoHideControlBar = false; cursorHideTimer = new QTimer(this); cursorHideTimer->setInterval(1500); cursorHideTimer->setSingleShot(true); connect(cursorHideTimer, SIGNAL(timeout()), this, SLOT(hideCursor())); // main area QWidget *widget = new QWidget(this); stackedLayout = new StackedLayout(widget); setCentralWidget(widget); mediaWidget = new MediaWidget(playerMenu, controlBar, collection, widget); connect(mediaWidget, SIGNAL(displayModeChanged()), this, SLOT(displayModeChanged())); connect(mediaWidget, SIGNAL(changeCaption(QString)), this, SLOT(setWindowTitle(QString))); // tabs - keep in sync with TabIndex enum! TabBase *startTab = new StartTab(this); tabs.append(startTab); stackedLayout->addWidget(startTab); playerTab = new PlayerTab(mediaWidget); playerTab->activate(); tabs.append(playerTab); stackedLayout->addWidget(playerTab); playlistTab = new PlaylistTab(playlistMenu, collection, mediaWidget); tabs.append(playlistTab); stackedLayout->addWidget(playlistTab); #if HAVE_DVB == 1 dvbTab = new DvbTab(dvbMenu, collection, mediaWidget); connect(this, SIGNAL(mayCloseApplication(bool*,QWidget*)), dvbTab, SLOT(mayCloseApplication(bool*,QWidget*))); tabs.append(dvbTab); stackedLayout->addWidget(dvbTab); #endif /* HAVE_DVB == 1 */ currentTabIndex = StartTabId; // actions also have to work if the menu bar is hidden (fullscreen) collection->addAssociatedWidget(this); // restore custom key bindings collection->readSettings(); // Tray menu menu = new QMenu(i18n("Kaffeine"), this); action = new QAction(i18n("Play &File"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(open())); menu->addAction(action); action = new QAction(i18n("Play &Audio CD"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(openAudioCd())); menu->addAction(action); action = new QAction(i18n("Play &Video CD"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(openVideoCd())); menu->addAction(action); action = new QAction(i18n("Play &DVD"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(openDvd())); menu->addAction(action); #if HAVE_DVB == 1 action = new QAction(i18n("&Watch Digital TV"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(playDvb())); menu->addAction(action); #endif action = new QAction(i18n("&Quit"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(close())); menu->addAction(action); // Tray Icon and its menu QMenu *trayMenu = new QMenu(this); trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayMenu); trayIcon->setIcon(QIcon::fromTheme(QLatin1String("kaffeine"), QIcon(":kaffeine"))); trayIcon->setToolTip(i18n("Kaffeine")); trayIcon->setContextMenu(menu); connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayShowHide(QSystemTrayIcon::ActivationReason)) ); // make sure that the bars are visible (fullscreen -> quit -> restore -> hidden) menuBar->show(); navigationBar->show(); controlBar->show(); trayIcon->show(); // workaround setAutoSaveSettings() which doesn't accept "IconOnly" as initial state controlBar->setToolButtonStyle(Qt::ToolButtonIconOnly); // initialize random number generator qsrand(QTime(0, 0, 0).msecsTo(QTime::currentTime())); // initialize dbus objects QDBusConnection::sessionBus().registerObject(QLatin1String("/"), new MprisRootObject(this), QDBusConnection::ExportAllContents); QDBusConnection::sessionBus().registerObject(QLatin1String("/Player"), new MprisPlayerObject(this, mediaWidget, playlistTab, this), QDBusConnection::ExportAllContents); QDBusConnection::sessionBus().registerObject(QLatin1String("/TrackList"), new MprisTrackListObject(playlistTab, this), QDBusConnection::ExportAllContents); #if HAVE_DVB == 1 QDBusConnection::sessionBus().registerObject(QLatin1String("/Television"), new DBusTelevisionObject(dvbTab, this), QDBusConnection::ExportAllContents); #endif /* HAVE_DVB == 1 */ QDBusConnection::sessionBus().registerService(QLatin1String("org.mpris.kaffeine")); show(); // set display mode switch (Configuration::instance()->getStartupDisplayMode()) { case Configuration::StartupNormalMode: // nothing to do break; case Configuration::StartupMinimalMode: mediaWidget->setDisplayMode(MediaWidget::MinimalMode); break; case Configuration::StartupFullScreenMode: mediaWidget->setDisplayMode(MediaWidget::FullScreenMode); break; case Configuration::StartupRememberLastSetting: { int value = KSharedConfig::openConfig()->group("MainWindow").readEntry("DisplayMode", 0); switch (value) { case 0: // nothing to do break; case 1: mediaWidget->setDisplayMode(MediaWidget::MinimalMode); break; case 2: mediaWidget->setDisplayMode(MediaWidget::FullScreenMode); break; } break; } } parseArgs(); } MainWindow::~MainWindow() { actionOpenRecent->saveEntries(KSharedConfig::openConfig()->group("Recent Files")); if (!temporaryUrls.isEmpty()) { KIO::del(temporaryUrls); } int value = 0; switch (mediaWidget->getDisplayMode()) { case MediaWidget::NormalMode: value = 0; break; case MediaWidget::MinimalMode: value = 1; break; case MediaWidget::FullScreenMode: value = 2; break; case MediaWidget::FullScreenReturnToMinimalMode: value = 2; break; } KSharedConfig::openConfig()->group("MainWindow").writeEntry("DisplayMode", value); } void MainWindow::close() { bool ok = true; #if HAVE_DVB == 1 dvbTab->mayCloseApplication(&ok, mediaWidget); #endif /* HAVE_DVB == 1 */ if (ok) QCoreApplication::exit(0); } void MainWindow::readSettings() { QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray(); if (geometry.isEmpty()) { const QRect availableGeometry = QApplication::desktop()->availableGeometry(this); resize(availableGeometry.width() / 3, availableGeometry.height() / 2); move((availableGeometry.width() - width()) / 2, (availableGeometry.height() - height()) / 2); } else { restoreGeometry(geometry); } } void MainWindow::writeSettings() { QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName()); settings.setValue("geometry", saveGeometry()); } MainWindow::MainWindow(KAboutData *aboutData, QCommandLineParser *parser) { this->aboutData = aboutData; this->parser = parser; parser->addOption(QCommandLineOption(QStringList() << QLatin1String("d") << QLatin1String("debug"), i18n("Enable all debug messages. Please notice that Kaffeine also allows enabling debug messages per category, by using the environment var:\nQT_LOGGING_RULES=kaffeine.category.debug=true\nwhere 'category' can be:\n" CATEGORIES))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("tempfile"), i18n("The files/URLs opened by the application will be deleted after use"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("f") << QLatin1String("fullscreen"), i18n("Start in full screen mode"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("audiocd"), i18n("Play Audio CD"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("videocd"), i18n("Play Video CD"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("dvd"), i18n("Play DVD"))); #if HAVE_DVB == 1 parser->addOption(QCommandLineOption(QStringList() << QLatin1String("dumpdvb"), i18nc("command line option", "Dump dvb data (debug option)"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("channel"), i18nc("command line option", "Play TV channel"), QLatin1String("name / number"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("tv"), i18nc("command line option", "(deprecated option)"), QLatin1String("channel"))); parser->addOption(QCommandLineOption(QStringList() << QLatin1String("lastchannel"), i18nc("command line option", "Play last tuned TV channel"))); #endif parser->addPositionalArgument(QLatin1String("[file]"), i18n("Files or URLs to play")); } void MainWindow::parseArgs(const QString workingDirectory) { if (parser->isSet("fullscreen")) { mediaWidget->setDisplayMode(MediaWidget::FullScreenMode); } if (parser->isSet("audiocd")) { if (parser->positionalArguments().count() > 0) { openAudioCd(parser->positionalArguments().first()); } else { openAudioCd(); } return; } if (parser->isSet("videocd")) { if (parser->positionalArguments().count() > 0) { openVideoCd(parser->positionalArguments().first()); } else { openVideoCd(); } return; } if (parser->isSet("dvd")) { if (parser->positionalArguments().count() > 0) { openDvd(parser->positionalArguments().first()); } else { openDvd(); } return; } #if HAVE_DVB == 1 QString channel; if (parser->isSet("dumpdvb")) { dvbTab->enableDvbDump(); } channel = parser->value("channel"); if (!channel.isEmpty()) { activateTab(DvbTabId); dvbTab->playChannel(channel); return; } channel = parser->value("tv"); if (!channel.isEmpty()) { activateTab(DvbTabId); dvbTab->playChannel(channel); return; } if (parser->isSet("lastchannel")) { activateTab(DvbTabId); dvbTab->playLastChannel(); return; } #endif /* HAVE_DVB == 1 */ if (parser->positionalArguments().count() > 0) { QList urls; for (int i = 0; i < parser->positionalArguments().count(); ++i) { QUrl url = QUrl::fromUserInput(parser->positionalArguments().at(i), workingDirectory); if (url.isValid()) { urls.append(url); } } if (parser->isSet("tempfile")) { temporaryUrls.append(urls); } if (urls.size() >= 2) { activateTab(PlaylistTabId); playlistTab->appendToVisiblePlaylist(urls, true); } else if (!urls.isEmpty()) { openUrl(urls.at(0)); } } } void MainWindow::displayModeChanged() { MediaWidget::DisplayMode displayMode = mediaWidget->getDisplayMode(); switch (displayMode) { case MediaWidget::FullScreenMode: case MediaWidget::FullScreenReturnToMinimalMode: setWindowState(windowState() | Qt::WindowFullScreen); break; case MediaWidget::MinimalMode: case MediaWidget::NormalMode: setWindowState(windowState() & (~Qt::WindowFullScreen)); break; } switch (displayMode) { case MediaWidget::FullScreenMode: case MediaWidget::FullScreenReturnToMinimalMode: case MediaWidget::MinimalMode: menuBar()->hide(); navigationBar->hide(); controlBar->hide(); autoHideControlBar = true; cursorHideTimer->start(); break; case MediaWidget::NormalMode: menuBar()->show(); navigationBar->show(); controlBar->show(); autoHideControlBar = false; cursorHideTimer->stop(); unsetCursor(); break; } tabs.at(currentTabIndex)->toggleDisplayMode(displayMode); } void MainWindow::trayShowHide(QSystemTrayIcon::ActivationReason reason) { if (reason != QSystemTrayIcon::DoubleClick) return; if (isVisible()) hide(); else { show(); raise(); setFocus(); } } void MainWindow::open() { if (isMinimized()) showNormal(); QList urls = QFileDialog::getOpenFileUrls(this, i18nc("@title:window", "Open files"), QUrl(), MediaWidget::extensionFilter()); // trayIcon->showMessage("Open", "Opening file(s)"); if (urls.size() >= 2) { activateTab(PlaylistTabId); playlistTab->appendToVisiblePlaylist(urls, true); } else if (!urls.isEmpty()) { openUrl(urls.at(0)); } } void MainWindow::openUrl() { openUrl(QInputDialog::getText(this, i18nc("@title:window", "Open URL"), i18n("Enter a URL:"))); } void MainWindow::openUrl(const QUrl &url) { if (!url.isValid()) { return; } // we need to copy "url" because addUrl() may invalidate it QUrl copy(url); actionOpenRecent->addUrl(copy); // moves the url to the top of the list if (currentTabIndex != PlaylistTabId) { activateTab(PlayerTabId); } playlistTab->appendToVisiblePlaylist(QList() << copy, true); } void MainWindow::openAudioCd(const QString &device) { if (isMinimized()) showNormal(); activateTab(PlayerTabId); mediaWidget->playAudioCd(device); } void MainWindow::openVideoCd(const QString &device) { if (isMinimized()) showNormal(); activateTab(PlayerTabId); mediaWidget->playVideoCd(device); } void MainWindow::openDvd(const QString &device) { if (isMinimized()) showNormal(); activateTab(PlayerTabId); mediaWidget->playDvd(device); } void MainWindow::playDvdFolder() { QString folder = QFileDialog::getExistingDirectory(this, QString()); if (!folder.isEmpty()) { openDvd(folder); } } void MainWindow::playDvb() { if (isMinimized()) showNormal(); activateTab(DvbTabId); dvbTab->playLastChannel(); } void MainWindow::configureKeys() { KShortcutsDialog::configure(collection); } void MainWindow::configureKaffeine() { QDialog *dialog = new ConfigurationDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose, true); dialog->setModal(true); dialog->show(); } void MainWindow::navigationBarOrientationChanged(Qt::Orientation orientation) { if (orientation == Qt::Horizontal) { tabBar->setShape(QTabBar::RoundedNorth); } else { tabBar->setShape(QTabBar::RoundedWest); } } void MainWindow::activateTab(int tabIndex) { currentTabIndex = tabIndex; tabBar->setCurrentIndex(tabIndex); if (!autoHideControlBar) { stackedLayout->setCurrentIndex(currentTabIndex); tabs.at(currentTabIndex)->activate(); } } void MainWindow::hideCursor() { setCursor(Qt::BlankCursor); } void MainWindow::closeEvent(QCloseEvent *event) { bool ok = true; emit mayCloseApplication(&ok, this); if (!ok) { event->ignore(); } else { writeSettings(); QMainWindow::closeEvent(event); } } bool MainWindow::event(QEvent *event) { bool retVal = QMainWindow::event(event); // this has to be done before calling setVisible() // FIXME we depend on QEvent::HoverMove (instead of QEvent::MouseMove) // but the latter depends on mouse tracking being enabled on this widget // and all its children (especially the video widget) ... if ((event->type() == QEvent::HoverMove) && autoHideControlBar) { + int x = reinterpret_cast (event)->pos().x(); int y = reinterpret_cast (event)->pos().y(); - if ((y < 0) || (y >= height())) { + if ((y < 0) || (y >= height()) || + (x < 0) || (x >= width())) { // QHoverEvent sometimes reports quite strange coordinates - ignore them return retVal; } cursorHideTimer->stop(); unsetCursor(); switch (toolBarArea(controlBar)) { case Qt::TopToolBarArea: controlBar->setVisible(y < 60); break; case Qt::BottomToolBarArea: controlBar->setVisible(y >= (height() - 60)); break; default: break; } if (controlBar->isHidden()) { cursorHideTimer->start(); } + + tabs.at(currentTabIndex)->mouse_move(x, y); } return retVal; } void MainWindow::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { mediaWidget->setDisplayMode(MediaWidget::NormalMode); } QMainWindow::keyPressEvent(event); } void MainWindow::leaveEvent(QEvent *event) { if (autoHideControlBar) { controlBar->setVisible(false); } QMainWindow::leaveEvent(event); } diff --git a/src/tabbase.h b/src/tabbase.h index b10af72..bf0dbf4 100644 --- a/src/tabbase.h +++ b/src/tabbase.h @@ -1,38 +1,38 @@ /* * tabbase.h * * Copyright (C) 2009-2011 Christoph Pfister * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TABBASE_H #define TABBASE_H #include #include "mediawidget.h" class TabBase : public QWidget { public: TabBase() { } ~TabBase() { } virtual void activate() = 0; virtual void toggleDisplayMode(MediaWidget::DisplayMode) {}; - + virtual void mouse_move(int, int) {}; }; #endif /* TABBASE_H */