1 /*
2  * Cantata
3  *
4  * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #include "titlewidget.h"
25 #include "support/squeezedtextlabel.h"
26 #include "support/utils.h"
27 #include "support/icon.h"
28 #include "support/proxystyle.h"
29 #include "support/monoicon.h"
30 #include "gui/stdactions.h"
31 #include "toolbutton.h"
32 #include "groupedview.h"
33 #ifdef Q_OS_MAC
34 #include "support/osxstyle.h"
35 #endif
36 #include "gui/covers.h"
37 #include <QAction>
38 #include <QImage>
39 #include <QPixmap>
40 #include <QHBoxLayout>
41 #include <QVBoxLayout>
42 #include <QEvent>
43 #include <QMouseEvent>
44 #include <QApplication>
45 #include <QPainter>
46 
TitleWidget(QWidget * p)47 TitleWidget::TitleWidget(QWidget *p)
48     : QListView(p)
49     , pressed(false)
50     , controls(nullptr)
51 {
52     setProperty(ProxyStyle::constModifyFrameProp, ProxyStyle::VF_Side|ProxyStyle::VF_Top);
53     QHBoxLayout *layout=new QHBoxLayout(this);
54     QVBoxLayout *textLayout=new QVBoxLayout(nullptr);
55     image=new QLabel(this);
56     mainText=new SqueezedTextLabel(this);
57     subText=new SqueezedTextLabel(this);
58     QLabel *chevron=new QLabel(this);
59     ToolButton tb(this);
60     tb.setIcon(StdActions::self()->appendToPlayQueueAction->icon());
61     tb.ensurePolished();
62     chevron->setPixmap(MonoIcon::icon(Qt::LeftToRight==layoutDirection() ? FontAwesome::chevronleft : FontAwesome::chevronright, Utils::monoIconColor()).pixmap(tb.iconSize()));
63     chevron->setFixedSize(tb.iconSize());
64     QFont f=mainText->font();
65     subText->setFont(Utils::smallFont(f));
66     mainText->setFont(f);
67     if (f.pixelSize()>0) {
68         f.setPixelSize(f.pixelSize()*2);
69     } else {
70         f.setPointSizeF(f.pointSizeF()*2);
71     }
72     QPalette pal=mainText->palette();
73     QColor col(mainText->palette().windowText().color());
74     col.setAlphaF(0.5);
75     pal.setColor(QPalette::WindowText, col);
76     subText->setPalette(pal);
77     chevron->setFont(f);
78     mainText->ensurePolished();
79     subText->ensurePolished();
80     setToolTip(tr("Click to go back"));
81     int spacing = Utils::layoutSpacing(this);
82     layout->addItem(new QSpacerItem(spacing, 1, QSizePolicy::Fixed, QSizePolicy::Fixed));
83     layout->addWidget(chevron);
84     layout->addItem(new QSpacerItem(spacing, 1, QSizePolicy::Fixed, QSizePolicy::Fixed));
85     layout->addWidget(image);
86     layout->addItem(new QSpacerItem(spacing, 1, QSizePolicy::Fixed, QSizePolicy::Fixed));
87     textLayout->addWidget(mainText);
88     textLayout->addWidget(subText);
89     layout->addItem(textLayout);
90     mainText->setAttribute(Qt::WA_TransparentForMouseEvents);
91     subText->setAttribute(Qt::WA_TransparentForMouseEvents);
92     image->setAttribute(Qt::WA_TransparentForMouseEvents);
93     chevron->setAttribute(Qt::WA_TransparentForMouseEvents);
94     viewport()->installEventFilter(this);
95     viewport()->setAttribute(Qt::WA_Hover);
96     connect(Covers::self(), SIGNAL(cover(Song,QImage,QString)), this, SLOT(coverRetrieved(Song,QImage,QString)));
97     connect(Covers::self(), SIGNAL(coverUpdated(Song,QImage,QString)), this, SLOT(coverRetrieved(Song,QImage,QString)));
98     connect(Covers::self(), SIGNAL(artistImage(Song,QImage,QString)), this, SLOT(coverRetrieved(Song,QImage,QString)));
99     layout->setMargin(0);
100     layout->setSpacing(2);
101     textLayout->setMargin(0);
102     textLayout->setSpacing(2);
103     mainText->setAlignment(Qt::AlignBottom);
104     subText->setAlignment(Qt::AlignTop);
105     image->setAlignment(Qt::AlignCenter);
106     chevron->setAlignment(Qt::AlignCenter);
107     chevron->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
108     image->setFixedSize(GroupedView::coverSize(), GroupedView::coverSize());
109     setFixedHeight(image->height()+((frameWidth()+2)*2));
110     setFocusPolicy(Qt::NoFocus);
111 }
112 
update(const Song & sng,const QIcon & icon,const QString & text,const QString & sub,bool showControls)113 void TitleWidget::update(const Song &sng, const QIcon &icon, const QString &text, const QString &sub, bool showControls)
114 {
115     song=sng;
116     image->setVisible(true);
117     mainText->setText(text);
118     subText->setText(sub);
119     if (!showControls) {
120         if (controls) {
121             controls->setVisible(false);
122         }
123     } else {
124         if (!controls) {
125             controls=new QWidget(this);
126             QVBoxLayout *l=new QVBoxLayout(controls);
127             l->setMargin(0);
128             l->setSpacing(0);
129             ToolButton *add=new ToolButton(this);
130             ToolButton *replace=new ToolButton(this);
131             add->setIcon(StdActions::self()->appendToPlayQueueAction->icon());
132             replace->setIcon(StdActions::self()->replacePlayQueueAction->icon());
133             int size=qMin(add->iconSize().height()+6, height()/2);
134             add->setFixedSize(QSize(size, size));
135             replace->setFixedSize(add->size());
136             add->setToolTip(tr("Add All To Play Queue"));
137             replace->setToolTip(tr("Add All And Replace Play Queue"));
138             l->addWidget(replace);
139             l->addWidget(add);
140             connect(add, SIGNAL(clicked()), this, SIGNAL(addToPlayQueue()));
141             connect(replace, SIGNAL(clicked()), this, SIGNAL(replacePlayQueue()));
142             static_cast<QHBoxLayout *>(layout())->addWidget(controls);
143         }
144         controls->setVisible(true);
145     }
146     subText->setVisible(!sub.isEmpty());
147     mainText->setAlignment(sub.isEmpty() ? Qt::AlignVCenter : Qt::AlignBottom);
148     if (!sng.isEmpty()) {
149         Covers::Image cImg=Covers::self()->requestImage(sng, true);
150         if (!cImg.img.isNull()) {
151             setImage(cImg.img);
152             return;
153         }
154     }
155     if (icon.isNull()) {
156         image->setVisible(false);
157     } else {
158         int iconPad=Utils::scaleForDpi(8);
159         int iconSize=image->width()-iconPad;
160         if (iconSize<44 && iconSize>=32) {
161             iconSize=32;
162         }
163         double dpr=DEVICE_PIXEL_RATIO();
164         QPixmap pix=Icon::getScaledPixmap(icon, iconSize*dpr, iconSize*dpr, 96*dpr);
165         pix.setDevicePixelRatio(dpr);
166         image->setPixmap(pix);
167     }
168 }
169 
eventFilter(QObject * o,QEvent * event)170 bool TitleWidget::eventFilter(QObject *o, QEvent *event)
171 {
172     if (isEnabled()) {
173         switch(event->type()) {
174         case QEvent::HoverEnter: {
175             QPalette pal = qApp->palette();
176             #ifdef Q_OS_MAC
177             QColor col(OSXStyle::self()->viewPalette().color(QPalette::Highlight));
178             #else
179             QColor col(pal.color(QPalette::Highlight));
180             #endif
181             col.setAlphaF(0.2);
182             pal.setColor(QPalette::Base, col);
183             setPalette(pal);
184             break;
185         }
186         case QEvent::HoverLeave: {
187             QPalette pal = qApp->palette();
188             QColor col(pal.color(QPalette::Base));
189             pal.setColor(QPalette::Base, col);
190             setPalette(pal);
191             break;
192         }
193         case QEvent::MouseButtonPress:
194             if (Qt::LeftButton==static_cast<QMouseEvent *>(event)->button() && Qt::NoModifier==static_cast<QMouseEvent *>(event)->modifiers()) {
195                 QPalette pal = qApp->palette();
196                 #ifdef Q_OS_MAC
197                 QColor col(OSXStyle::self()->viewPalette().color(QPalette::Highlight));
198                 #else
199                 QColor col(pal.color(QPalette::Highlight));
200                 #endif
201                 col.setAlphaF(0.5);
202                 pal.setColor(QPalette::Base, col);
203                 setPalette(pal);
204                 pressed=true;
205             }
206             break;
207         case QEvent::MouseButtonRelease:
208             if (pressed && Qt::LeftButton==static_cast<QMouseEvent *>(event)->button() && !QApplication::overrideCursor()) {
209                 actions().first()->trigger();
210             }
211             pressed=false;
212             break;
213         default:
214             break;
215         }
216     }
217     return QWidget::eventFilter(o, event);
218 }
219 
coverRetrieved(const Song & s,const QImage & img,const QString & file)220 void TitleWidget::coverRetrieved(const Song &s, const QImage &img, const QString &file)
221 {
222     Q_UNUSED(file);
223     if (song.isEmpty() || img.isNull()) {
224         return;
225     }
226     if (song.albumArtistOrComposer()!=s.albumArtistOrComposer()) {
227         return;
228     }
229     if (s.isArtistImageRequest()!=song.isArtistImageRequest()) {
230         return;
231     }
232     if (s.isComposerImageRequest()!=song.isComposerImageRequest()) {
233         return;
234     }
235     if (!s.isComposerImageRequest() && !s.isArtistImageRequest() && s.album!=song.album) {
236         return;
237     }
238     setImage(img);
239 }
240 
241 
setImage(const QImage & img)242 void TitleWidget::setImage(const QImage &img)
243 {
244     double dpr=DEVICE_PIXEL_RATIO();
245     QPixmap pix=QPixmap::fromImage(img.scaled(image->width()*dpr, image->height()*dpr, Qt::KeepAspectRatio, Qt::SmoothTransformation));
246     pix.setDevicePixelRatio(dpr);
247     image->setPixmap(pix);
248 }
249 
250 #include "moc_titlewidget.cpp"
251