You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/noatun/modules/systray/systray.cpp

468 lines
12 KiB

// systray.h
//
// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
// Copyright (C) 1999 Charles Samuels <charles@kde.org>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Except as contained in this notice, the name(s) of the author(s) shall not be
// used in advertising or otherwise to promote the sale, use or other dealings
// in this Software without prior written authorization from the author(s).
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "systray.h"
#include "kitsystemtray.h"
#include "cmodule.h"
#include "yhconfig.h"
#include <noatun/app.h>
#include <noatun/pref.h>
#include <noatun/player.h>
#include <noatun/stdaction.h>
#include <tdeaction.h>
#include <tdeconfig.h>
#include <tqfile.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <kpassivepopup.h>
#include <kpixmapeffect.h>
#include <kstdaction.h>
#include <tqbitmap.h>
#include <tqhbox.h>
#include <tqpainter.h>
#include <tqpushbutton.h>
#include <tqtooltip.h>
#include <tqvbox.h>
#include <tqimage.h>
#include <kurl.h>
#include <tdeio/netaccess.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <netwm.h>
#include <tdeglobalsettings.h>
// TODO: Maybe make this value configurable?
const int COVER_MAXW = 128;
const int COVER_MAXH = 128;
#define BASEICON "noatun"
// From JuK
class PassivePopup : public KPassivePopup
{
public:
PassivePopup(TQWidget *parent = 0, const char *name = 0) : KPassivePopup(parent, name) {}
protected:
virtual void enterEvent(TQEvent *)
{
setTimeout(3000000); // Make timeout damn near infinite
}
virtual void leaveEvent(TQEvent *)
{
setTimeout(250); // Close quickly
}
};
//NoatunSystray *NoatunSystray::self = 0;
NoatunSystray::NoatunSystray() : TDEMainWindow(0, "NoatunSystray"), Plugin(),
mTray(0), trayStatus(0), trayBase(0), mPassivePopup(0L)
{
//self = this;
hide();
tmpCoverPath = locateLocal("tmp", "youngHickoryCover.png");
removeCover(); // make sure any old temp cover is gone
KStdAction::quit(TQT_TQOBJECT(napp), TQT_SLOT(quit()), actionCollection());
KStdAction::open(TQT_TQOBJECT(napp), TQT_SLOT(fileOpen()), actionCollection());
KStdAction::preferences(TQT_TQOBJECT(napp), TQT_SLOT(preferences()), actionCollection());
NoatunStdAction::back(actionCollection(), "back");
NoatunStdAction::stop(actionCollection(), "stop");
NoatunStdAction::playpause(actionCollection(), "play");
NoatunStdAction::forward(actionCollection(), "forward");
NoatunStdAction::playlist(actionCollection(), "show_playlist");
NoatunStdAction::loop(actionCollection(), "loop_style");
NoatunStdAction::effects(actionCollection(), "effects");
NoatunStdAction::equalizer(actionCollection(), "equalizer");
createGUI("systrayui.rc");
mTray = new KitSystemTray("tray", this);
mTray->show();
trayBase = renderIcon(BASEICON, TQString());
trayStatus = renderIcon(BASEICON, "media-playback-stop");
mTray->changeTitle(*trayBase, i18n("Noatun"));
showingTrayStatus = false;
mBlinkTimer = new TQTimer(this);
connect(mBlinkTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotBlinkTimer()));
connect(napp->player(), TQT_SIGNAL(playing()), this, TQT_SLOT(slotPlayPause()));
connect(napp->player(), TQT_SIGNAL(paused()), this, TQT_SLOT(slotPlayPause()));
connect(napp->player(), TQT_SIGNAL(stopped()), this, TQT_SLOT(slotStopped()));
//napp->player()->handleButtons();
}
NoatunSystray::~NoatunSystray()
{
//kdDebug(66666) << k_funcinfo << "Called." << endl;
removeCover();
delete trayBase;
delete trayStatus;
napp->showInterfaces();
}
void NoatunSystray::init()
{
YHModule *cmod = new YHModule(TQT_TQOBJECT(this));
connect(cmod, TQT_SIGNAL(saved()), this, TQT_SLOT(slotLoadSettings()));
slotLoadSettings();
}
void NoatunSystray::slotLoadSettings()
{
kdDebug(66666) << k_funcinfo << endl;
YHConfig *c = YHConfig::self();
if(c->stateIconDisplay() == YHConfig::FlashingIcon)
mBlinkTimer->start(1000);
else
mBlinkTimer->stop();
slotBlinkTimer();
if(c->tip())
TQToolTip::add(mTray, tipText);
else
TQToolTip::remove(mTray);
if (!c->passivePopupCovers())
removeCover();
if(c->passivePopup())
{
mPassivePopup = new PassivePopup(mTray, "NoatunPassivePopup");
}
else
{
delete mPassivePopup;
mPassivePopup = 0L;
}
}
void NoatunSystray::closeEvent(TQCloseEvent*)
{
//kdDebug(66666) << k_funcinfo << "Called." << endl;
disconnect(napp->player(), 0, 0, 0);
unload();
}
void NoatunSystray::slotPlayPause()
{
TQString status;
if(napp->player()->isPaused())
{
changeTray("media-playback-pause");
status = i18n("Noatun - Paused");
}
else
{
changeTray("media-playback-start");
status = i18n("Noatun - Playing");
}
const PlaylistItem item = napp->player()->current();
TQString s;
if(!item.isProperty("title"))
{
// No metadata
s = TQString("<nobr>%1</nobr>").arg(item.title());
}
else
{
s = TQString("<h2><nobr>%1</nobr></h2>").arg(item.property("title"));
if(item.isProperty("author"))
s += TQString("<nobr>%1</nobr><br>").arg(item.property("author"));
if(item.isProperty("album"))
{
if(item.isProperty("date"))
s += TQString("<nobr>%1 (%2)</nobr><br>").arg(item.property("album")).arg(item.property("date"));
else
s += TQString("<nobr>%1</nobr><br>").arg(item.property("album"));
}
}
// prepare cover image for display
if (YHConfig::self()->passivePopupCovers())
updateCover();
if(YHConfig::self()->passivePopupCovers() && TQFile::exists(tmpCoverPath))
{
// QT always adds an empty line after the table so we add en empty line before the
// table to get equal spacing on top and bottom
setTipText(TQString("<qt><br><table cellspacing=0 cellpadding=0><tr>" \
"<td align=center valign=center><h4><nobr>%1</nobr></h4>%2</td>" \
"<td valign=center><img src='%3'></td>" \
"</qt></tr></table>").arg(status).arg(s).arg(tmpCoverPath));
}
else
{
setTipText(TQString("<qt><center><h4><nobr>%1</nobr></h4>%2</center></qt>").arg(status).arg(s));
}
}
void NoatunSystray::slotStopped()
{
if(!napp->player()->current())
return;
changeTray("media-playback-stop");
setTipText(TQString("<qt><nobr><h4>%1</h4></nobr></qt>").arg(i18n("Noatun - Stopped")));
}
void NoatunSystray::changeTray(const TQString &pm)
{
delete trayStatus;
trayStatus = renderIcon(BASEICON, pm);
if(showingTrayStatus)
slotBlinkTimer();
}
void NoatunSystray::slotBlinkTimer()
{
switch(YHConfig::self()->stateIconDisplay())
{
case (YHConfig::FlashingIcon):
showingTrayStatus ^= true;
break;
case (YHConfig::StaticIcon):
showingTrayStatus = true;
break;
case (YHConfig::NoIcon):
showingTrayStatus = false;
break;
}
if(showingTrayStatus)
mTray->setPixmap(*trayStatus);
else
mTray->setPixmap(*trayBase);
}
// taken from patched karamba xmmssensor
// modified heavily to work in this place
void NoatunSystray::updateCover()
{
//kdDebug(66666) << k_funcinfo << endl;
TQString dir = napp->player()->current().url().directory();
TQString cover;
// TODO: Maybe make these filenames configurable?
if(TQFile::exists(dir + "/folder.png"))
cover = dir + "/folder.png";
else if(TQFile::exists(dir + "/.folder.png"))
cover = dir + "/.folder.png";
else if(TQFile::exists(dir + "/cover.png"))
cover = dir + "/cover.png";
else if(TQFile::exists(dir + "/cover.jpg"))
cover = dir + "/cover.jpg";
else if(TQFile::exists(dir + "/cover.jpeg"))
cover = dir + "/cover.jpeg";
else // no cover
{
//kdDebug(66666) << k_funcinfo << "NO COVER" << endl;
removeCover();
return;
}
TQString title = napp->player()->current().title();
TQImage previmg;
previmg.load(tmpCoverPath);
if(previmg.text("Title") != title)
{ //Verify song change to limit CPU usage
/*kdDebug(66666) << k_funcinfo << "Creating new temp cover for '" <<
cover << "'" << endl;*/
TQImage src;
TQImage tmpimg;
if(src.load(cover))
{
if(src.width() >= COVER_MAXW || src.height() >= COVER_MAXH)
tmpimg = src.scale(COVER_MAXW, COVER_MAXH, TQ_ScaleMin);
else
tmpimg = src;
tmpimg.setText("Title", 0, title); //add Title in the image text for cache usage
tmpimg.save(tmpCoverPath, "PNG", 0);
}
else
{
removeCover();
}
}
}
void NoatunSystray::removeCover()
{
if(TQFile::exists(tmpCoverPath))
TDEIO::NetAccess::del(KURL(tmpCoverPath), this);
}
void NoatunSystray::setTipText(const TQString& text)
{
if(text == tipText) // save the planet, save cpu cycles ;)
return;
tipText = text;
YHConfig *c = YHConfig::self();
if(c->passivePopup())
TQTimer::singleShot(0, this, TQT_SLOT(showPassivePopup()));
if(c->tip())
TQToolTip::add(mTray, tipText);
}
void NoatunSystray::showPassivePopup()
{
if (!mPassivePopup)
{
kdDebug(66666) << k_funcinfo << "Called but no KPassivePopup created yet!" << endl;
return;
}
mPassivePopup->reparent(0L, TQPoint(0,0));
if (YHConfig::self()->passivePopupButtons() && !napp->player()->isStopped())
{
TQVBox *widget = mPassivePopup->standardView(TQString(), tipText, TQPixmap());
TQHBox *box = new TQHBox(mPassivePopup, "popupbox");
box->setSpacing(8);
// Algorithm for determining popup location from kpassivepopup.cpp via JuK
NETWinInfo ni(tqt_xdisplay(), mTray->winId(), tqt_xrootwin(),
NET::WMIconGeometry | NET::WMKDESystemTrayWinFor);
NETRect frame, win;
ni.kdeGeometry(frame, win);
TQRect bounds = TDEGlobalSettings::desktopGeometry(TQPoint(win.pos.x, win.pos.y));
if(win.pos.x < bounds.center().x())
{
// Buttons to the left
TQVBox *buttonBox = new TQVBox(box);
buttonBox->setSpacing(3);
TQPushButton *forwardButton = new TQPushButton(action("forward")->iconSet(), 0, buttonBox, "popup_forward");
forwardButton->setFlat(true);
connect(forwardButton, TQT_SIGNAL(clicked()), action("forward"), TQT_SLOT(activate()));
TQPushButton *backButton = new TQPushButton(action("back")->iconSet(), 0, buttonBox, "popup_back");
backButton->setFlat(true);
connect(backButton, TQT_SIGNAL(clicked()), action("back"), TQT_SLOT(activate()));
TQFrame *line = new TQFrame(box);
line->setFrameShape(TQFrame::VLine);
widget->reparent(box, TQPoint(0, 0));
}
else
{
// Buttons to the right
widget->reparent(box, TQPoint(0, 0));
TQFrame *line = new TQFrame(box);
line->setFrameShape(TQFrame::VLine);
TQVBox *buttonBox = new TQVBox(box);
buttonBox->setSpacing(3);
TQPushButton *forwardButton = new TQPushButton(action("forward")->iconSet(), 0, buttonBox, "popup_forward");
forwardButton->setFlat(true);
connect(forwardButton, TQT_SIGNAL(clicked()), action("forward"), TQT_SLOT(activate()));
TQPushButton *backButton = new TQPushButton(action("back")->iconSet(), 0, buttonBox, "popup_back");
backButton->setFlat(true);
connect(backButton, TQT_SIGNAL(clicked()), action("back"), TQT_SLOT(activate()));
}
mPassivePopup->setView(box);
}
else
{
mPassivePopup->setView(TQString(), tipText);
}
mPassivePopup->setTimeout(YHConfig::self()->passivePopupTimeout()*1000);
mPassivePopup->show();
}
TQPixmap *NoatunSystray::renderIcon(const TQString& baseIcon, const TQString &overlayIcon) const
{
TQPixmap *base = new TQPixmap(KSystemTray::loadIcon(baseIcon));
if(!(overlayIcon.isNull())) // otherwise leave the base as-is
{
TQPixmap overlay = KSystemTray::loadIcon(overlayIcon);
if(!overlay.isNull())
{
// draw the overlay on top of it
TQPainter p(base);
p.drawPixmap(0, 0, overlay);
}
}
return base;
}
#include "systray.moc"