|
|
|
// 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());
|
Bring mail_forward, mail_new, mail_replyall, mail_reply, mail_send, player_pause, player_play, player_stop, player_rew, player_fwd, player_start, player_end, rotate_ccw, rotate_cw, window_fullscreen, window_nofullscreen, window_new, viewmagfit, viewmag+, viewmag1, and viewmag- icons into XDG compliance
10 years ago
|
|
|
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())
|
|
|
|
{
|
Bring mail_forward, mail_new, mail_replyall, mail_reply, mail_send, player_pause, player_play, player_stop, player_rew, player_fwd, player_start, player_end, rotate_ccw, rotate_cw, window_fullscreen, window_nofullscreen, window_new, viewmagfit, viewmag+, viewmag1, and viewmag- icons into XDG compliance
10 years ago
|
|
|
changeTray("media-playback-pause");
|
|
|
|
status = i18n("Noatun - Paused");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Bring mail_forward, mail_new, mail_replyall, mail_reply, mail_send, player_pause, player_play, player_stop, player_rew, player_fwd, player_start, player_end, rotate_ccw, rotate_cw, window_fullscreen, window_nofullscreen, window_new, viewmagfit, viewmag+, viewmag1, and viewmag- icons into XDG compliance
10 years ago
|
|
|
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;
|
Bring mail_forward, mail_new, mail_replyall, mail_reply, mail_send, player_pause, player_play, player_stop, player_rew, player_fwd, player_start, player_end, rotate_ccw, rotate_cw, window_fullscreen, window_nofullscreen, window_new, viewmagfit, viewmag+, viewmag1, and viewmag- icons into XDG compliance
10 years ago
|
|
|
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, TQImage::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"
|