diff --git a/playermanager.cpp b/playermanager.cpp index a1f9d943..abc4f670 100644 --- a/playermanager.cpp +++ b/playermanager.cpp @@ -1,515 +1,517 @@ /** * Copyright (C) 2004 Scott Wheeler * Copyright (C) 2007 Matthias Kretz * Copyright (C) 2008, 2009, 2018 Michael Pyne * * 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, see . */ #include "playermanager.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "playlistinterface.h" #include "playeradaptor.h" #include "slideraction.h" #include "statuslabel.h" #include "actioncollection.h" #include "collectionlist.h" #include "coverinfo.h" #include "tag.h" #include "scrobbler.h" #include "juk.h" #include "juk_debug.h" using namespace ActionCollection; enum PlayerManagerStatus { StatusStopped = -1, StatusPaused = 1, StatusPlaying = 2 }; //////////////////////////////////////////////////////////////////////////////// // static functions //////////////////////////////////////////////////////////////////////////////// static void updateWindowTitle(const FileHandle &file) { JuK::JuKInstance()->setWindowTitle(i18nc( "%1 is the artist and %2 is the title of the currently playing track.", "%1 - %2 :: JuK", file.tag()->artist(), file.tag()->title())); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// PlayerManager::PlayerManager() : QObject(), m_playlistInterface(nullptr), m_output(new Phonon::AudioOutput(Phonon::MusicCategory, this)), m_media( new Phonon::MediaObject(this)), m_audioPath(Phonon::createPath(m_media, m_output)) { setupAudio(); new PlayerAdaptor(this); QDBusConnection::sessionBus().registerObject("/Player", this); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// bool PlayerManager::playing() const { Phonon::State state = m_media->state(); return (state == Phonon::PlayingState || state == Phonon::BufferingState); } bool PlayerManager::paused() const { return m_media->state() == Phonon::PausedState; } bool PlayerManager::muted() const { return m_output->isMuted(); } float PlayerManager::volume() const { return m_output->volume(); } int PlayerManager::status() const { if(paused()) return StatusPaused; if(playing()) return StatusPlaying; return StatusStopped; } int PlayerManager::totalTime() const { return totalTimeMSecs() / 1000; } int PlayerManager::currentTime() const { return currentTimeMSecs() / 1000; } int PlayerManager::totalTimeMSecs() const { return m_media->totalTime(); } int PlayerManager::currentTimeMSecs() const { return m_media->currentTime(); } bool PlayerManager::seekable() const { return m_media->isSeekable(); } QStringList PlayerManager::trackProperties() { return FileHandle::properties(); } QString PlayerManager::trackProperty(const QString &property) const { if(!playing() && !paused()) return QString(); return m_file.property(property); } QPixmap PlayerManager::trackCover(const QString &size) const { if(!playing() && !paused()) return QPixmap(); if(size.toLower() == "small") return m_file.coverInfo()->pixmap(CoverInfo::Thumbnail); if(size.toLower() == "large") return m_file.coverInfo()->pixmap(CoverInfo::FullSize); return QPixmap(); } FileHandle PlayerManager::playingFile() const { return m_file; } QString PlayerManager::playingString() const { if(!playing() || m_file.isNull()) return QString(); return m_file.tag()->playingString(); } void PlayerManager::setPlaylistInterface(PlaylistInterface *interface) { m_playlistInterface = interface; } //////////////////////////////////////////////////////////////////////////////// // public slots //////////////////////////////////////////////////////////////////////////////// void PlayerManager::play(const FileHandle &file) { if(!m_media || !m_playlistInterface || file.isNull()) return; m_media->setCurrentSource(QUrl::fromLocalFile(file.absFilePath())); m_media->play(); if(m_file != file) emit signalItemChanged(file); m_file = file; // Our state changed handler will perform the follow up actions necessary // once we actually start playing. } void PlayerManager::play(const QString &file) { CollectionListItem *item = CollectionList::instance()->lookup(file); if(item) { Playlist::setPlaying(item); play(item->file()); } } void PlayerManager::play() { if(paused()) m_media->play(); else if(playing()) { m_media->seek(0); emit seeked(0); } else { m_playlistInterface->playNext(); const auto file = m_playlistInterface->currentFile(); play(file); } } void PlayerManager::pause() { if(paused()) return; action("pause")->setEnabled(false); m_media->pause(); } void PlayerManager::stop() { if(!m_playlistInterface) return; action("pause")->setEnabled(false); action("stop")->setEnabled(false); action("back")->setEnabled(false); action("forward")->setEnabled(false); action("forwardAlbum")->setEnabled(false); if(!m_file.isNull()) { m_file = FileHandle(); emit signalItemChanged(m_file); } m_media->stop(); } void PlayerManager::setVolume(float volume) { m_output->setVolume(volume); } void PlayerManager::seek(int seekTime) { if(m_media->currentTime() == seekTime) return; m_media->seek(seekTime); emit seeked(seekTime); } void PlayerManager::seekForward() { const qint64 total = m_media->totalTime(); const qint64 newtime = m_media->currentTime() + total / 100; const qint64 seekTo = qMin(total, newtime); m_media->seek(seekTo); emit seeked(seekTo); } void PlayerManager::seekBack() { const qint64 total = m_media->totalTime(); const qint64 newtime = m_media->currentTime() - total / 100; const qint64 seekTo = qMax(qint64(0), newtime); m_media->seek(seekTo); emit seeked(seekTo); } void PlayerManager::playPause() { playing() ? action("pause")->trigger() : action("play")->trigger(); } void PlayerManager::forward() { m_playlistInterface->playNext(); FileHandle file = m_playlistInterface->currentFile(); if(!file.isNull()) play(file); else stop(); } void PlayerManager::back() { m_playlistInterface->playPrevious(); FileHandle file = m_playlistInterface->currentFile(); if(!file.isNull()) play(file); else stop(); } void PlayerManager::volumeUp() { - setVolume(volume() + 0.04); // 4% up + const auto newVolume = std::min(m_output->volume() + 0.04, 1.0); + m_output->setVolume(newVolume); // 4% up } void PlayerManager::volumeDown() { - setVolume(volume() - 0.04); // 4% down + const auto newVolume = std::max(m_output->volume() - 0.04, 0.0); + m_output->setVolume(newVolume); // 4% down } void PlayerManager::setMuted(bool m) { m_output->setMuted(m); } bool PlayerManager::mute() { bool newState = !muted(); setMuted(newState); return newState; } //////////////////////////////////////////////////////////////////////////////// // private slots //////////////////////////////////////////////////////////////////////////////// void PlayerManager::slotFinished() { // It is possible to end up in this function if a file simply fails to play or if the // user moves the slider all the way to the end, therefore see if we can keep playing // and if we can, do so. Otherwise, stop. m_playlistInterface->playNext(); play(m_playlistInterface->currentFile()); } void PlayerManager::slotLength(qint64 msec) { emit totalTimeChanged(msec); } void PlayerManager::slotTick(qint64 msec) { emit tick(msec); } void PlayerManager::slotStateChanged(Phonon::State newstate, Phonon::State) { if(newstate == Phonon::ErrorState) { QString errorMessage = i18nc( "%1 will be the /path/to/file, %2 will be some string from Phonon describing the error", "JuK is unable to play the audio file%1" "for the following reason:%2", m_file.absFilePath(), m_media->errorString() ); qCWarning(JUK_LOG) << "Phonon is in error state" << m_media->errorString() << "while playing" << m_file.absFilePath(); switch(m_media->errorType()) { case Phonon::NoError: qCDebug(JUK_LOG) << "received a state change to ErrorState but errorType is NoError!?"; break; case Phonon::NormalError: KMessageBox::information(0, errorMessage); break; case Phonon::FatalError: KMessageBox::sorry(0, errorMessage); break; } stop(); return; } // "normal" path if(newstate == Phonon::StoppedState && m_file.isNull()) { JuK::JuKInstance()->setWindowTitle(i18n("JuK")); emit signalStop(); } else if(newstate == Phonon::PausedState) { emit signalPause(); } else { // PlayingState or BufferingState action("pause")->setEnabled(true); action("stop")->setEnabled(true); action("forward")->setEnabled(true); if(action("albumRandomPlay")->isChecked()) action("forwardAlbum")->setEnabled(true); action("back")->setEnabled(true); updateWindowTitle(m_file); emit signalPlay(); } } void PlayerManager::slotSeekableChanged(bool isSeekable) { emit seekableChanged(isSeekable); } void PlayerManager::slotMutedChanged(bool muted) { emit mutedChanged(muted); } void PlayerManager::slotVolumeChanged(qreal volume) { if(qFuzzyCompare(m_output->volume(), volume)) { return; } emit volumeChanged(volume); } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void PlayerManager::setupAudio() { using namespace Phonon; connect(m_output, &AudioOutput::mutedChanged, this, &PlayerManager::slotMutedChanged); connect(m_output, &AudioOutput::volumeChanged, this, &PlayerManager::slotVolumeChanged); connect(m_media, &MediaObject::stateChanged, this, &PlayerManager::slotStateChanged); connect(m_media, &MediaObject::currentSourceChanged, this, &PlayerManager::trackHasChanged); connect(m_media, &MediaObject::totalTimeChanged, this, &PlayerManager::slotLength); connect(m_media, &MediaObject::tick, this, &PlayerManager::slotTick); connect(m_media, &MediaObject::aboutToFinish, this, &PlayerManager::trackAboutToFinish); connect(m_media, &MediaObject::finished, this, &PlayerManager::slotFinished); connect(m_media, &MediaObject::seekableChanged, this, &PlayerManager::slotSeekableChanged); m_media->setTickInterval(100); } QString PlayerManager::randomPlayMode() const { if(action("randomPlay")->isChecked()) return "Random"; if(action("albumRandomPlay")->isChecked()) return "AlbumRandom"; return "NoRandom"; } void PlayerManager::setRandomPlayMode(const QString &randomMode) { if(randomMode.toLower() == "random") action("randomPlay")->setChecked(true); if(randomMode.toLower() == "albumrandom") action("albumRandomPlay")->setChecked(true); if(randomMode.toLower() == "norandom") action("disableRandomPlay")->setChecked(true); } void PlayerManager::trackHasChanged(const Phonon::MediaSource &newSource) { if(newSource.type() == Phonon::MediaSource::Url) { const auto item = CollectionList::instance()->lookup(newSource.url().path()); if(item) { const auto newFile = item->file(); if(m_file != newFile) emit signalItemChanged(newFile); m_file = newFile; updateWindowTitle(m_file); emit seeked(0); } } else { qCWarning(JUK_LOG) << "Track has changed so something we didn't set???"; return; } } void PlayerManager::trackAboutToFinish() { // Called when playback is in progress and a track is about to finish, gives us a // chance to keep audio playback going without Phonon entering StoppedState if(!m_playlistInterface) return; m_playlistInterface->playNext(); const auto file = m_playlistInterface->currentFile(); if(!file.isNull()) m_media->enqueue(QUrl::fromLocalFile(file.absFilePath())); } // vim: set et sw=4 tw=0 sta: diff --git a/systemtray.cpp b/systemtray.cpp index 0a66d373..2572b556 100644 --- a/systemtray.cpp +++ b/systemtray.cpp @@ -1,528 +1,528 @@ /** * Copyright (C) 2002 Daniel Molkentin * Copyright (C) 2002-2004 Scott Wheeler * Copyright (C) 2004-2009 Michael Pyne * * 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, see . */ #include "systemtray.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tag.h" #include "actioncollection.h" #include "playermanager.h" #include "coverinfo.h" #include "juk_debug.h" using namespace ActionCollection; PassiveInfo::PassiveInfo() : QFrame(nullptr, Qt::ToolTip | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint ), m_timer(new QTimer(this)), m_layout(new QVBoxLayout(this)), m_justDie(false) { connect(m_timer, SIGNAL(timeout()), SLOT(timerExpired())); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); // Workaround transparent background in Oxygen when (ab-)using Qt::ToolTip setAutoFillBackground(true); setFrameStyle(StyledPanel | Plain); setLineWidth(2); } void PassiveInfo::startTimer(int delay) { m_timer->start(delay); } void PassiveInfo::show() { m_timer->start(3500); setWindowOpacity(1.0); QFrame::show(); } void PassiveInfo::setView(QWidget *view) { m_layout->addWidget(view); view->show(); // We are still hidden though. adjustSize(); positionSelf(); } void PassiveInfo::timerExpired() { // If m_justDie is set, we should just go, otherwise we should emit the // signal and wait for the system tray to delete us. if(m_justDie) hide(); else emit timeExpired(); } void PassiveInfo::enterEvent(QEvent *) { m_timer->stop(); emit mouseEntered(); } void PassiveInfo::leaveEvent(QEvent *) { m_justDie = true; m_timer->start(50); } void PassiveInfo::hideEvent(QHideEvent *) { } void PassiveInfo::wheelEvent(QWheelEvent *e) { if(e->delta() >= 0) { emit nextSong(); } else { emit previousSong(); } e->accept(); } void PassiveInfo::positionSelf() { // Start with a QRect of our size, move it to the right spot. QRect r(rect()); QRect curScreen(KWindowSystem::workArea()); // Try to position in lower right of the screen QPoint anchor(curScreen.right() * 7 / 8, curScreen.bottom()); // Now make our rect hit that anchor. r.moveBottomRight(anchor); move(r.topLeft()); } //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// SystemTray::SystemTray(PlayerManager *player, QWidget *parent) : KStatusNotifierItem(parent), m_popup(0), m_player(player), m_fadeTimer(0), m_fade(true), m_hasCompositionManager(false) { using ActionCollection::action; // Override the KSNI::action call introduced in KF5 // This should be initialized to the number of labels that are used. m_labels.fill(0, 3); setIconByName("juk"); setCategory(ApplicationStatus); setStatus(Active); // We were told to dock in systray by user, force us visible m_forwardPix = SmallIcon("media-skip-forward"); m_backPix = SmallIcon("media-skip-backward"); // Just create this here so that it show up in the DBus interface and the // key bindings dialog. QAction *rpaction = new QAction(i18n("Redisplay Popup"), this); ActionCollection::actions()->addAction("showPopup", rpaction); connect(rpaction, SIGNAL(triggered(bool)), SLOT(slotPlay())); QMenu *cm = contextMenu(); connect(m_player, SIGNAL(signalPlay()), this, SLOT(slotPlay())); connect(m_player, SIGNAL(signalPause()), this, SLOT(slotPause())); connect(m_player, SIGNAL(signalStop()), this, SLOT(slotStop())); cm->addAction( action("play") ); cm->addAction( action("pause") ); cm->addAction( action("stop") ); cm->addAction( action("forward") ); cm->addAction( action("back") ); cm->addSeparator(); // Pity the actionCollection doesn't keep track of what sub-menus it has. KActionMenu *menu = new KActionMenu(i18n("&Random Play"), this); // FIXME //actionCollection()->addAction("randomplay", menu); menu->addAction(action("disableRandomPlay")); menu->addAction(action("randomPlay")); menu->addAction(action("albumRandomPlay")); cm->addAction( menu ); cm->addAction( action("togglePopups") ); m_fadeTimer = new QTimer(this); m_fadeTimer->setObjectName( QLatin1String("systrayFadeTimer" )); connect(m_fadeTimer, SIGNAL(timeout()), SLOT(slotNextStep())); // Handle wheel events connect(this, SIGNAL(scrollRequested(int,Qt::Orientation)), SLOT(scrollEvent(int,Qt::Orientation))); // Add a quick hook for play/pause toggle connect(this, SIGNAL(secondaryActivateRequested(QPoint)), action("playPause"), SLOT(trigger())); if(m_player->playing()) slotPlay(); else if(m_player->paused()) slotPause(); } //////////////////////////////////////////////////////////////////////////////// // public slots //////////////////////////////////////////////////////////////////////////////// void SystemTray::slotPlay() { if(!m_player->playing()) return; QPixmap cover = m_player->playingFile().coverInfo()->pixmap(CoverInfo::FullSize); setOverlayIconByName("media-playback-start"); setToolTip(m_player->playingString(), cover); createPopup(); } void SystemTray::slotPause() { setOverlayIconByName("media-playback-pause"); } void SystemTray::slotPopupLargeCover() { if(!m_player->playing()) return; FileHandle playingFile = m_player->playingFile(); playingFile.coverInfo()->popup(); } void SystemTray::slotStop() { setToolTip(); setOverlayIconByName(QString()); delete m_popup; m_popup = 0; m_fadeTimer->stop(); } void SystemTray::slotPopupDestroyed() { for(int i = 0; i < m_labels.size(); ++i) m_labels[i] = 0; } void SystemTray::slotNextStep() { // Could happen I guess if the timeout event were queued while we're deleting m_popup if(!m_popup) return; ++m_step; // If we're not fading, immediately stop the fadeout if(!m_fade || m_step == STEPS) { m_step = 0; m_fadeTimer->stop(); emit fadeDone(); return; } if(m_hasCompositionManager) { m_popup->setWindowOpacity((1.0 * STEPS - m_step) / STEPS); } else { QColor result = interpolateColor(m_step); for(int i = 0; i < m_labels.size() && m_labels[i]; ++i) { QPalette palette; palette.setColor(m_labels[i]->foregroundRole(), result); m_labels[i]->setPalette(palette); } } } void SystemTray::slotFadeOut() { m_startColor = m_labels[0]->palette().color( QPalette::Text ); //textColor(); m_endColor = m_labels[0]->palette().color( QPalette::Window ); //backgroundColor(); m_hasCompositionManager = KWindowSystem::compositingActive(); connect(this, SIGNAL(fadeDone()), m_popup, SLOT(hide())); connect(m_popup, SIGNAL(mouseEntered()), this, SLOT(slotMouseInPopup())); m_fadeTimer->start(1500 / STEPS); } // If we receive this signal, it's because we were called during fade out. // That means there is a single shot timer about to call slotNextStep, so we // don't have to do it ourselves. void SystemTray::slotMouseInPopup() { m_endColor = m_labels[0]->palette().color( QPalette::Text ); //textColor(); disconnect(SIGNAL(fadeDone())); if(m_hasCompositionManager) m_popup->setWindowOpacity(1.0); m_step = STEPS - 1; // Simulate end of fade to solid text slotNextStep(); } //////////////////////////////////////////////////////////////////////////////// // private methods //////////////////////////////////////////////////////////////////////////////// QWidget *SystemTray::createInfoBox(QBoxLayout *parentLayout, const FileHandle &file) { // We always show the popup on the right side of the current screen, so // this logic assumes that. Earlier revisions had logic for popup being // wherever the systray icon is, so if it's decided to go that route again, // dig into the source control history. --mpyne if(file.coverInfo()->hasCover()) { addCoverButton(parentLayout, file.coverInfo()->pixmap(CoverInfo::Thumbnail)); addSeparatorLine(parentLayout); } auto infoBox = new QWidget; auto infoBoxVLayout = new QVBoxLayout(infoBox); infoBoxVLayout->setSpacing(3); infoBoxVLayout->setMargin(3); parentLayout->addWidget(infoBox); addSeparatorLine(parentLayout); createButtonBox(parentLayout); return infoBox; } void SystemTray::createPopup() { FileHandle playingFile = m_player->playingFile(); Tag *playingInfo = playingFile.tag(); // If the action exists and it's checked, do our stuff if(!ActionCollection::action("togglePopups")->isChecked()) return; delete m_popup; m_popup = 0; m_fadeTimer->stop(); // This will be reset after this function call by slot(Forward|Back) // so it's safe to set it true here. m_fade = true; m_step = 0; m_popup = new PassiveInfo; connect(m_popup, SIGNAL(destroyed()), SLOT(slotPopupDestroyed())); connect(m_popup, SIGNAL(timeExpired()), SLOT(slotFadeOut())); connect(m_popup, SIGNAL(nextSong()), SLOT(slotForward())); connect(m_popup, SIGNAL(previousSong()), SLOT(slotBack())); auto box = new QWidget; auto boxHLayout = new QHBoxLayout(box); boxHLayout->setSpacing(15); // Add space between text and buttons QWidget *infoBox = createInfoBox(boxHLayout, playingFile); QLayout *infoBoxLayout = infoBox->layout(); for(int i = 0; i < m_labels.size(); ++i) { QLabel *l = new QLabel(" "); l->setAlignment(Qt::AlignRight | Qt::AlignVCenter); m_labels[i] = l; infoBoxLayout->addWidget(l); } // We have to set the text of the labels after all of the // widgets have been added in order for the width to be calculated // correctly. int labelCount = 0; QString title = playingInfo->title().toHtmlEscaped(); m_labels[labelCount++]->setText(QString("

%1

").arg(title)); if(!playingInfo->artist().isEmpty()) m_labels[labelCount++]->setText(playingInfo->artist()); if(!playingInfo->album().isEmpty()) { QString album = playingInfo->album().toHtmlEscaped(); QString s = playingInfo->year() > 0 ? QString("%1 (%2)").arg(album).arg(playingInfo->year()) : QString("%1").arg(album); m_labels[labelCount++]->setText(s); } m_popup->setView(box); m_popup->show(); } void SystemTray::createButtonBox(QBoxLayout *parentLayout) { auto buttonBox = new QWidget; auto buttonBoxVLayout = new QVBoxLayout(buttonBox); buttonBoxVLayout->setSpacing(3); QPushButton *forwardButton = new QPushButton(m_forwardPix, QString()); forwardButton->setObjectName(QLatin1String("popup_forward")); connect(forwardButton, SIGNAL(clicked()), SLOT(slotForward())); QPushButton *backButton = new QPushButton(m_backPix, QString()); backButton->setObjectName(QLatin1String("popup_back")); connect(backButton, SIGNAL(clicked()), SLOT(slotBack())); buttonBoxVLayout->addWidget(forwardButton); buttonBoxVLayout->addWidget(backButton); parentLayout->addWidget(buttonBox); } /** * What happens here is that the action->trigger() call will end up invoking * createPopup(), which sets m_fade to true. Before the text starts fading * control returns to this function, which resets m_fade to false. */ void SystemTray::slotBack() { ActionCollection::action("back")->trigger(); m_fade = false; } void SystemTray::slotForward() { ActionCollection::action("forward")->trigger(); m_fade = false; } void SystemTray::addSeparatorLine(QBoxLayout *parentLayout) { QFrame *line = new QFrame; line->setFrameShape(QFrame::VLine); // Cover art takes up 80 pixels, make sure we take up at least 80 pixels // even if we don't show the cover art for consistency. line->setMinimumHeight(80); parentLayout->addWidget(line); } void SystemTray::addCoverButton(QBoxLayout *parentLayout, const QPixmap &cover) { QPushButton *coverButton = new QPushButton; coverButton->setIconSize(cover.size()); coverButton->setIcon(cover); coverButton->setFixedSize(cover.size()); coverButton->setFlat(true); connect(coverButton, SIGNAL(clicked()), this, SLOT(slotPopupLargeCover())); parentLayout->addWidget(coverButton); } QColor SystemTray::interpolateColor(int step, int steps) { if(step < 0) return m_startColor; if(step >= steps) return m_endColor; // TODO: Perhaps the algorithm here could be better? For example, it might // make sense to go rather quickly from start to end and then slow down // the progression. return QColor( (step * m_endColor.red() + (steps - step) * m_startColor.red()) / steps, (step * m_endColor.green() + (steps - step) * m_startColor.green()) / steps, (step * m_endColor.blue() + (steps - step) * m_startColor.blue()) / steps ); } void SystemTray::setToolTip(const QString &tip, const QPixmap &cover) { if(tip.isEmpty()) KStatusNotifierItem::setToolTip("juk", i18n("JuK"), QString()); else { QPixmap myCover; if(cover.isNull()) { myCover = DesktopIcon("juk"); } else { //Scale to proper icon size, otherwise KStatusNotifierItem will show an unknown icon int iconSize = KIconLoader::global()->currentSize(KIconLoader::Desktop); myCover = cover.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } KStatusNotifierItem::setToolTip(QIcon(myCover), i18n("JuK"), tip); } } void SystemTray::scrollEvent(int delta, Qt::Orientation orientation) { if(orientation == Qt::Horizontal) return; - switch(QApplication::keyboardModifiers()) { + switch(QApplication::queryKeyboardModifiers()) { case Qt::ShiftModifier: if(delta > 0) ActionCollection::action("volumeUp")->trigger(); else ActionCollection::action("volumeDown")->trigger(); break; default: if(delta > 0) ActionCollection::action("forward")->trigger(); else ActionCollection::action("back")->trigger(); break; } } // vim: set et sw=4 tw=0 sta: