systemtray.cpp 10.4 KB
Newer Older
1
/***************************************************************************
2 3 4 5 6
    copyright            : (C) 2002 by Daniel Molkentin
    email                : molkentin@kde.org

    copyright            : (C) 2002 - 2004 by Scott Wheeler
    email                : wheeler@kde.org
7 8 9 10 11 12 13 14 15 16 17
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

18
#include <klocale.h>
19
#include <kiconloader.h>
20
#include <kpassivepopup.h>
21
#include <kiconeffect.h>
22 23
#include <kaction.h>
#include <kpopupmenu.h>
24
#include <kglobalsettings.h>
25
#include <kdebug.h>
26

27
#include <qvbox.h>
28
#include <qpushbutton.h>
29
#include <qtooltip.h>
30
#include <qvaluevector.h>
31

32
#include "tag.h"
33
#include "systemtray.h"
34
#include "actioncollection.h"
35
#include "playermanager.h"
36

37 38
using namespace ActionCollection;

39 40
static bool copyImage(QImage &dest, QImage &src, int x, int y);

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
class PassiveInfo : public KPassivePopup
{
public:
    PassiveInfo(QWidget *parent = 0, const char *name = 0) :
        KPassivePopup(parent, name) {}

protected:
    virtual void enterEvent(QEvent *)
    {
        setTimeout(3000000); // Make timeout damn near infinite
    }
    
    virtual void leaveEvent(QEvent *)
    {
        setTimeout(250); // Close quickly
    }
};

59 60 61 62
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////

63
SystemTray::SystemTray(QWidget *parent, const char *name) : KSystemTray(parent, name),
64
                                                            m_popup(0)
65

66
{
67
    m_appPix = loadIcon("juk_dock");
68

69 70
    m_playPix = createPixmap("player_play");
    m_pausePix = createPixmap("player_pause");
71

72
    m_forwardPix = loadIcon("player_end");
73
    m_backPix = loadIcon("player_start");
74

75
    setPixmap(m_appPix);
76

77 78
    setToolTip();

79 80 81 82 83
    // Just create this here so that it show up in the DCOP interface and the key
    // bindings dialog.

    new KAction(i18n("Redisplay Popup"), KShortcut(), this, SLOT(slotPlay()), actions(), "showPopup");
    
84
    KPopupMenu *cm = contextMenu();
85

86 87 88
    connect(PlayerManager::instance(), SIGNAL(signalPlay()), this, SLOT(slotPlay()));
    connect(PlayerManager::instance(), SIGNAL(signalPause()), this, SLOT(slotPause()));
    connect(PlayerManager::instance(), SIGNAL(signalStop()), this, SLOT(slotStop()));
89

90 91 92 93
    action("play")->plug(cm);
    action("pause")->plug(cm);
    action("stop")->plug(cm);
    action("forward")->plug(cm);
94
    action("back")->plug(cm);
95

96 97
    cm->insertSeparator();

98
    action("randomPlay")->plug(cm);
99
    action("togglePopups")->plug(cm);
100 101

    if(PlayerManager::instance()->playing())
102
        slotPlay();
103
    else if(PlayerManager::instance()->paused())
104
        slotPause();
105 106 107 108 109 110 111 112 113 114 115
}

SystemTray::~SystemTray()
{

}

////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

116 117
void SystemTray::slotPlay()
{
118 119 120
    if(!PlayerManager::instance()->playing())
        return;

121 122
    setPixmap(m_playPix);
    setToolTip(PlayerManager::instance()->playingString());
123
    createPopup();
124 125
}

126 127
void SystemTray::slotStop()
{
128
    setPixmap(m_appPix);
129
    setToolTip();
130 131 132 133 134 135 136 137 138

    delete m_popup;
    m_popup = 0;
}

////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////

139
void SystemTray::createPopup()
140
{
141 142 143
    FileHandle playingFile = PlayerManager::instance()->playingFile();
    Tag *playingInfo = playingFile.tag();
    
144
    // If the action exists and it's checked, do our stuff
145

146
    if(action<KToggleAction>("togglePopups")->isChecked()) {
147

148
        delete m_popup;
149
        m_popup = new PassiveInfo(this);
150 151

        QHBox *box = new QHBox(m_popup);
152
        box->setSpacing(15); // Add space between text and buttons
153

154
        // See where to put the buttons
155

156 157
        bool onLeft = buttonsToLeft();
        QVBox *buttonBox;
158
        QVBox *infoBox;
159 160

        if(onLeft) {
161

162
            // They go to the left because JuK is on that side
163

164 165 166
            buttonBox = new QVBox(box);

            // Separator line
167

168 169
            QFrame *line = new QFrame(box);
            line->setFrameShape(QFrame::VLine);
170 171

            infoBox = new QVBox(box);
172
        }
173 174 175 176 177
        else {

            // Buttons go on right because JuK is there

            infoBox = new QVBox(box);
178

179 180 181 182 183
            QFrame *line = new QFrame(box);
            line->setFrameShape(QFrame::VLine);

            buttonBox = new QVBox(box);
        }
184 185 186

        infoBox->setSpacing(3);
        infoBox->setMargin(3);
187
        buttonBox->setSpacing(3);
188
        
189
        // This should be initialized to the number of labels that are used.
190

191 192 193 194
        QValueVector<QLabel *> labels(3, 0);
        for(QValueVector<QLabel *>::Iterator it = labels.begin(); it != labels.end(); ++it) {
            *it = new QLabel(" ", infoBox);
            (*it)->setAlignment(AlignRight | AlignVCenter);
195
        }
196

197 198 199
        QPushButton *forwardButton = new QPushButton(m_forwardPix, 0, buttonBox, "popup_forward");
        forwardButton->setFlat(true);
        connect(forwardButton, SIGNAL(clicked()), action("forward"), SLOT(activate()));
200

201 202 203
        QPushButton *backButton = new QPushButton(m_backPix, 0, buttonBox, "popup_back");
        backButton->setFlat(true);
        connect(backButton, SIGNAL(clicked()), action("back"), SLOT(activate()));
204

205 206
        // We don't want an autodelete popup.  There are times when it will need
        // to be hidden before the timeout.
207

208
        m_popup->setAutoDelete(false);
209 210 211 212

        // 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.
213

214 215 216 217 218 219 220 221 222 223 224 225 226
        int labelCount = 0;

        labels[labelCount++]->setText(QString("<nobr><h2>%1</h2></nobr>").arg(playingInfo->title()));

        if(!playingInfo->artist().isEmpty())
            labels[labelCount++]->setText(playingInfo->artist());

        if(!playingInfo->album().isEmpty()) {
            QString s = playingInfo->year() > 0
                ? QString("%1 (%2)").arg(playingInfo->album()).arg(playingInfo->year())
                : playingInfo->album();
            labels[labelCount++]->setText(s);
        }
227

228
        m_popup->setView(box);
229 230
        m_popup->show();
    }
231 232
}

233 234 235 236 237 238 239 240 241 242 243
bool SystemTray::buttonsToLeft() const
{
    QPoint center = mapToGlobal(geometry().center());
    QRect bounds = KGlobalSettings::desktopGeometry(center);
    int middle = bounds.center().x();

    // This seems to accurately guess what side of the icon that
    // KPassivePopup will popup on.
    return((center.x() - (width() / 2)) < middle);
}

244
QPixmap SystemTray::createPixmap(const QString &pixName)
245 246
{
    QPixmap bgPix = m_appPix;
247
    QPixmap fgPix = SmallIcon(pixName);
248

249 250
    QImage bgImage = bgPix.convertToImage(); // Probably 22x22
    QImage fgImage = fgPix.convertToImage(); // Should be 16x16
251

252 253
    KIconEffect::semiTransparent(bgImage);
    KIconEffect::semiTransparent(bgImage);
254 255
    copyImage(bgImage, fgImage, (bgImage.width() - fgImage.width()) / 2,
              (bgImage.height() - fgImage.height()) / 2);
256

257
    bgPix.convertFromImage(bgImage);
258
    return bgPix;
259 260
}

261
void SystemTray::setToolTip(const QString &tip)
262
{
263
    QToolTip::remove(this);
Nadeem Hasan's avatar
Nadeem Hasan committed
264

265 266
    if(tip.isNull())
        QToolTip::add(this, "JuK");
267
    else
268
        QToolTip::add(this, tip);
269 270
}

271 272
void SystemTray::wheelEvent(QWheelEvent *e)
{
273
    if(e->orientation() == Horizontal)
274
        return;
275 276 277 278

    // I already know the type here, but this file doesn't (and I don't want it
    // to) know about the JuK class, so a static_cast won't work, and I was told
    // that a reinterpret_cast isn't portable when combined with multiple
279
    // inheritance.  (This is why I don't check the result.)
280

281 282
    switch(e->state()) {
    case ShiftButton:
283 284 285 286
        if(e->delta() > 0)
            action("volumeUp")->activate();
        else
            action("volumeDown")->activate();
287
        break;
288
    default:
289
        if(e->delta() > 0)
290
            action("forward")->activate();
291
        else
292
            action("back")->activate();
293
        break;
294
    }
295 296 297
    e->accept();
}

298 299 300
/*
 * Reimplemented this in order to use the middle mouse button
 */
Scott Wheeler's avatar
Scott Wheeler committed
301
void SystemTray::mousePressEvent(QMouseEvent *e)
302
{
Scott Wheeler's avatar
Scott Wheeler committed
303
    switch(e->button()) {
304 305
    case LeftButton:
    case RightButton:
306
    default:
Scott Wheeler's avatar
Scott Wheeler committed
307
        KSystemTray::mousePressEvent(e);
308 309
        break;
    case MidButton:
Scott Wheeler's avatar
Scott Wheeler committed
310 311
        if(!rect().contains(e->pos()))
            return;
312 313
        if(action("pause")->isEnabled())
            action("pause")->activate();
314
        else
315
            action("play")->activate();
Scott Wheeler's avatar
Scott Wheeler committed
316
        break;
317 318 319
    }
}

320 321 322 323 324 325 326 327 328
/*
 * This function copies the entirety of src into dest, starting in
 * dest at x and y.  This function exists because I was unable to find
 * a function like it in either QImage or kdefx
 */

static bool copyImage(QImage &dest, QImage &src, int x, int y)
{
    if(dest.depth() != src.depth())
329
        return false;
330 331 332 333
    if((x + src.width()) >= dest.width())
        return false;
    if((y + src.height()) >= dest.height())
        return false;
334

335 336 337 338 339 340 341 342 343 344
    // We want to use KIconEffect::overlay to do this, since it handles
    // alpha, but the images need to be the same size.  We can handle that.

    QImage large_src(dest);

    // It would perhaps be better to create large_src based on a size, but
    // this is the easiest way to make a new image with the same depth, size,
    // etc.

    large_src.detach();
345

346 347 348
    // However, we do have to specifically ensure that setAlphaBuffer is set
    // to false

349
    large_src.setAlphaBuffer(false);
350
    large_src.fill(0); // All transparent pixels
351
    large_src.setAlphaBuffer(true);
352

353 354 355 356 357
    int w = src.width();
    int h = src.height();
    for(int dx = 0; dx < w; dx++)
        for(int dy = 0; dy < h; dy++)
            large_src.setPixel(dx + x, dy + y, src.pixel(dx, dy));
358

359 360
    // Apply effect to image

361
    KIconEffect::overlay(dest, large_src);
362

363 364 365
    return true;
}

366
#include "systemtray.moc"
Nadeem Hasan's avatar
Nadeem Hasan committed
367 368

// vim: ts=8