systemtray.cpp 10.1 KB
Newer Older
1 2 3
/***************************************************************************
                          systray.cpp  -  description
                             -------------------
Scott Wheeler's avatar
Scott Wheeler committed
4 5
    copyright            : (C) 2002 by Daniel Molkentin <molkentin@kde.org>
                           (C) 2002, 2003 Scott Wheeler <wheeler@kde.org>
6 7 8 9 10 11 12 13 14 15 16
 ***************************************************************************/

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

17
#include <klocale.h>
18
#include <kiconloader.h>
19
#include <kpassivepopup.h>
20
#include <kiconeffect.h>
21 22
#include <kaction.h>
#include <kpopupmenu.h>
23
#include <kdebug.h>
Nadeem Hasan's avatar
Nadeem Hasan committed
24

25
#include <qhbox.h>
26
#include <qvbox.h>
27
#include <qpushbutton.h>
28
#include <qtooltip.h>
29 30
#include <qlayout.h>
#include <qfileinfo.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
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////

45
SystemTray::SystemTray(QWidget *parent, const char *name) : KSystemTray(parent, name),
46
                                                            m_popup(0)
47

48
{
49
    m_appPix = loadIcon("juk_dock");
50

51 52
    m_playPix = createPixmap("player_play");
    m_pausePix = createPixmap("player_pause");
53

54 55
    m_backPix = loadIcon("player_start");
    m_forwardPix = loadIcon("player_end");
56

57
    setPixmap(m_appPix);
58

Nadeem Hasan's avatar
Nadeem Hasan committed
59 60
    setToolTip();

61
    KPopupMenu *cm = contextMenu();
62

63 64 65
    connect(PlayerManager::instance(), SIGNAL(signalPlay()), this, SLOT(slotPlay()));
    connect(PlayerManager::instance(), SIGNAL(signalPause()), this, SLOT(slotPause()));
    connect(PlayerManager::instance(), SIGNAL(signalStop()), this, SLOT(slotStop()));
66

67 68 69 70 71
    action("play")->plug(cm);
    action("pause")->plug(cm);
    action("stop")->plug(cm);
    action("back")->plug(cm);
    action("forward")->plug(cm);
72

73 74
    cm->insertSeparator();

75
    action("randomPlay")->plug(cm);
76
    action("togglePopups")->plug(cm);
77 78

    if(PlayerManager::instance()->playing())
79
        slotPlay();
80
    else if(PlayerManager::instance()->paused())
81
        slotPause();
82 83 84 85 86 87 88 89 90 91 92
}

SystemTray::~SystemTray()
{

}

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

93 94 95 96
void SystemTray::slotPlay()
{
    setPixmap(m_playPix);
    setToolTip(PlayerManager::instance()->playingString());
97
    createPopup();
98 99
}

100 101
void SystemTray::slotStop()
{
102
    setPixmap(m_appPix);
Nadeem Hasan's avatar
Nadeem Hasan committed
103
    setToolTip();
104 105 106 107 108 109 110 111 112

    delete m_popup;
    m_popup = 0;
}

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

113
void SystemTray::createPopup(bool addButtons)
114
{
115 116 117
    FileHandle playingFile = PlayerManager::instance()->playingFile();
    Tag *playingInfo = playingFile.tag();
    
118
    // If the action exists and it's checked, do our stuff
119
    if(action<KToggleAction>("togglePopups")->isChecked()) {
120

Scott Wheeler's avatar
Scott Wheeler committed
121
        delete m_popup;
122 123 124
        m_popup = new KPassivePopup(this);

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

127
        QVBox *infoBox = new QVBox(box);
128 129 130 131 132 133 134 135 136 137 138 139 140 141

        infoBox->setSpacing(3);
        infoBox->setMargin(3);
        
        // We need to add QLabels to replace any missing labels.
        int numSpacers = 0;
        
        QString titleStr = playingInfo->title();
        QLabel *title = new QLabel(titleStr, infoBox);
        title->setAlignment(AlignRight | AlignVCenter);

        // Give the title a bold, bigger font to make it look better.
        QFont f = title->font();
        f.setBold(true);
142
        f.setPointSize(f.pointSize() + 4);
143 144 145 146 147
        title->setFont(f);
        
        // Artist info
        QString artistStr = playingInfo->artist();
        QLabel *artist = 0;
148
        if(!artistStr.isEmpty())
149 150
        {
            artist = new QLabel(infoBox);
151
            artist->setAlignment(AlignRight | AlignVCenter);
152 153 154 155 156 157 158
        }
        else
            ++numSpacers;
                
        // Album info
        QString albumStr = playingInfo->album();
        QLabel *album = 0;
159
        if(!albumStr.isEmpty())
160 161 162 163
        {
            int year = playingInfo->year();
            
            // See if there is a year.
164
            if(year > 0)
165 166 167
                albumStr = i18n("%1 (%2)").arg(albumStr).arg(year);
            
            album = new QLabel(infoBox);
168
            album->setAlignment(AlignRight | AlignVCenter);
169 170 171 172
        }
        else
            ++numSpacers;

173
        for( ; numSpacers != 0; --numSpacers)
174 175 176 177
        {
            // Add an empty label.  QSpacerItems weren't working for
            // me for some reason.  QBoxLayout->addSpacing() wasn't
            // either. mpyne :-(
178
            (void) new QLabel(" ", infoBox);
179
        }
180 181

        if(addButtons) {
182
            // Add separator line
183 184
            QFrame *line = new QFrame(box);
            line->setFrameShape(QFrame::VLine);
185

186 187
            QVBox *buttonBox = new QVBox(box);
            buttonBox->setSpacing(3);
188 189
            
            QPushButton *backButton = new QPushButton(m_backPix, 0, buttonBox, "popup_back");
190
            backButton->setFlat(true);
191
            connect(backButton, SIGNAL(clicked()), action("back"), SLOT(activate()));
192

193
            QPushButton *forwardButton = new QPushButton(m_forwardPix, 0, buttonBox, "popup_forward");
194
            forwardButton->setFlat(true);
195
            connect(forwardButton, SIGNAL(clicked()), action("forward"), SLOT(activate()));
196 197
        }

198 199
        // We don't want an autodelete popup.  There are times when it will need
        // to be hidden before the timeout.
200
        m_popup->setAutoDelete(false);
201 202 203 204 205

        // 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.
        title->setText(playingInfo->title());
206 207 208 209
        if(artist)
            artist->setText(artistStr);
        if(album)
            album->setText(albumStr);
210
        
211
        m_popup->setView(box);
212 213
        m_popup->show();
    }
214 215
}

216
QPixmap SystemTray::createPixmap(const QString &pixName)
217
{
218
    QPixmap buffer(m_appPix.width(), m_appPix.height());
219
    buffer.fill(this, 0, 0);
220 221

    QPixmap bgPix = m_appPix;
222
    QPixmap fgPix = SmallIcon(pixName);
223

224 225
    QImage bgImage = bgPix.convertToImage(); // Probably 22x22
    QImage fgImage = fgPix.convertToImage(); // Should be 16x16
226

227 228
    KIconEffect::semiTransparent(bgImage);
    KIconEffect::semiTransparent(bgImage);
229 230
    copyImage(bgImage, fgImage, (bgImage.width() - fgImage.width()) / 2,
              (bgImage.height() - fgImage.height()) / 2);
231

232
    bgPix.convertFromImage(bgImage);
233
    return bgPix;
234 235
}

236
void SystemTray::setToolTip(const QString &tip)
Nadeem Hasan's avatar
Nadeem Hasan committed
237
{
238
    QToolTip::remove(this);
Nadeem Hasan's avatar
Nadeem Hasan committed
239

240 241
    if(tip.isNull())
        QToolTip::add(this, "JuK");
Nadeem Hasan's avatar
Nadeem Hasan committed
242
    else
243
        QToolTip::add(this, tip);
Nadeem Hasan's avatar
Nadeem Hasan committed
244 245
}

246 247
void SystemTray::wheelEvent(QWheelEvent *e)
{
248
    if(e->orientation() == Horizontal)
Scott Wheeler's avatar
Scott Wheeler committed
249
        return;
250 251 252 253

    // 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
254
    // inheritance.  (This is why I don't check the result.)
255

256 257
    switch(e->state()) {
    case ShiftButton:
258 259 260 261
        if(e->delta() > 0)
            action("volumeUp")->activate();
        else
            action("volumeDown")->activate();
Scott Wheeler's avatar
Scott Wheeler committed
262
        break;
263
    default:
Scott Wheeler's avatar
Scott Wheeler committed
264
        if(e->delta() > 0)
265
            action("forward")->activate();
266
        else
267
            action("back")->activate();
Scott Wheeler's avatar
Scott Wheeler committed
268
        break;
269
    }
270 271 272
    e->accept();
}

273 274 275
/*
 * Reimplemented this in order to use the middle mouse button
 */
Scott Wheeler's avatar
Scott Wheeler committed
276
void SystemTray::mousePressEvent(QMouseEvent *e)
277
{
Scott Wheeler's avatar
Scott Wheeler committed
278
    switch(e->button()) {
279 280
    case LeftButton:
    case RightButton:
281
    default:
Scott Wheeler's avatar
Scott Wheeler committed
282
        KSystemTray::mousePressEvent(e);
283 284
        break;
    case MidButton:
Scott Wheeler's avatar
Scott Wheeler committed
285 286
        if(!rect().contains(e->pos()))
            return;
287 288
        if(action("pause")->isEnabled())
            action("pause")->activate();
289
        else
290
            action("play")->activate();
Scott Wheeler's avatar
Scott Wheeler committed
291
        break;
292 293 294
    }
}

295 296 297 298 299 300 301 302 303
/*
 * 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())
304
        return false;
305 306 307 308
    if((x + src.width()) >= dest.width())
        return false;
    if((y + src.height()) >= dest.height())
        return false;
309

310 311 312 313 314 315 316 317 318 319
    // 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();
320

321 322 323
    // However, we do have to specifically ensure that setAlphaBuffer is set
    // to false

324
    large_src.setAlphaBuffer(false);
325
    large_src.fill(0); // All transparent pixels
326
    large_src.setAlphaBuffer(true);
327

328 329 330 331 332
    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));
333

334 335
    // Apply effect to image

336
    KIconEffect::overlay(dest, large_src);
337

338 339 340
    return true;
}

341
#include "systemtray.moc"
Nadeem Hasan's avatar
Nadeem Hasan committed
342 343

// vim: ts=8