1 /*
2  * Strawberry Music Player
3  * This file was part of Clementine.
4  * Copyright 2010, David Sansome <me@davidsansome.com>
5  *
6  * Strawberry is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Strawberry is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Strawberry.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "config.h"
22 
23 #include <QtGlobal>
24 #include <QWidget>
25 #include <QList>
26 #include <QString>
27 #include <QImage>
28 #include <QFont>
29 #include <QFontMetrics>
30 #include <QPainter>
31 #include <QPainterPath>
32 #include <QPalette>
33 #include <QColor>
34 #include <QBrush>
35 #include <QPen>
36 #include <QPoint>
37 #include <QRect>
38 #include <QSize>
39 #include <QTransform>
40 #include <QLinearGradient>
41 
42 #include "core/utilities.h"
43 #include "freespacebar.h"
44 
45 class QPaintEvent;
46 
47 const int FreeSpaceBar::kBarHeight = 20;
48 const int FreeSpaceBar::kBarBorderRadius = 8;
49 const int FreeSpaceBar::kMarkerSpacing = 32;
50 const int FreeSpaceBar::kLabelBoxSize = 12;
51 const int FreeSpaceBar::kLabelBoxPadding = 4;
52 const int FreeSpaceBar::kLabelSpacing = 16;
53 
54 const QRgb FreeSpaceBar::kColorBg1 = qRgb(214, 207, 200);
55 const QRgb FreeSpaceBar::kColorBg2 = qRgb(234, 226, 218);
56 const QRgb FreeSpaceBar::kColorAdd1 = qRgb(136, 180, 229);
57 const QRgb FreeSpaceBar::kColorAdd2 = qRgb(72, 146, 229);
58 const QRgb FreeSpaceBar::kColorBar1 = qRgb(250, 148, 76);
59 const QRgb FreeSpaceBar::kColorBar2 = qRgb(214, 102, 24);
60 const QRgb FreeSpaceBar::kColorBorder = qRgb(174, 168, 162);
61 
FreeSpaceBar(QWidget * parent)62 FreeSpaceBar::FreeSpaceBar(QWidget *parent)
63     : QWidget(parent),
64       free_(100),
65       additional_(0),
66       total_(100),
67       free_text_(tr("Available")),
68       additional_text_(tr("New songs")),
69       used_text_(tr("Used")) {
70 
71   setMinimumHeight(FreeSpaceBar::sizeHint().height());
72 
73 }
74 
sizeHint() const75 QSize FreeSpaceBar::sizeHint() const {
76   return QSize(150, kBarHeight + kLabelBoxPadding + fontMetrics().height());
77 }
78 
paintEvent(QPaintEvent *)79 void FreeSpaceBar::paintEvent(QPaintEvent*) {
80 
81   // Geometry
82   QRect bar_rect(rect());
83   bar_rect.setHeight(kBarHeight);
84 
85   QRect reflection_rect(bar_rect);
86   reflection_rect.moveTop(reflection_rect.bottom());
87 
88   QRect labels_rect(rect());
89   labels_rect.setTop(labels_rect.top() + kBarHeight + kLabelBoxPadding);
90 
91   // Draw the reflection
92   // Create the reflected pixmap
93   QImage reflection(reflection_rect.size(), QImage::Format_ARGB32_Premultiplied);
94   reflection.fill(palette().color(QPalette::Window).rgba());
95   QPainter p(&reflection);
96 
97   // Set up the transformation
98   QTransform transform;
99   transform.scale(1.0, -1.0);
100   transform.translate(0.0, -reflection.height());
101   p.setTransform(transform);
102 
103   // Draw the bar
104   DrawBar(&p, QRect(QPoint(0, 0), reflection.size()));
105 
106   // Make it fade out towards the bottom
107   QLinearGradient fade_gradient(reflection.rect().topLeft(), reflection.rect().bottomLeft());
108   fade_gradient.setColorAt(0.0, QColor(0, 0, 0, 0));
109   fade_gradient.setColorAt(1.0, QColor(0, 0, 0, 128));
110 
111   p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
112   p.fillRect(reflection.rect(), fade_gradient);
113 
114   p.end();
115 
116   // Draw on the widget
117   p.begin(this);
118   DrawBar(&p, bar_rect);
119   p.drawImage(reflection_rect, reflection);
120   DrawText(&p, labels_rect);
121 
122 }
123 
DrawBar(QPainter * p,const QRect r)124 void FreeSpaceBar::DrawBar(QPainter *p, const QRect r) {
125 
126   p->setRenderHint(QPainter::Antialiasing, true);
127 
128   QRect bar_rect(r);
129   bar_rect.setWidth(static_cast<int>(static_cast<float>(bar_rect.width()) * (static_cast<float>(total_ - free_) / static_cast<float>(total_))));
130 
131   QLinearGradient background_gradient(r.topLeft(), r.bottomLeft());
132   background_gradient.setColorAt(0, kColorBg1);
133   background_gradient.setColorAt(1, kColorBg2);
134 
135   QLinearGradient bar_gradient(bar_rect.topLeft(), bar_rect.bottomLeft());
136   bar_gradient.setColorAt(0, kColorBar1);
137   bar_gradient.setColorAt(1, kColorBar2);
138 
139   // Draw the background
140   p->setPen(Qt::NoPen);
141   p->setBrush(background_gradient);
142   p->drawRoundedRect(r, kBarBorderRadius, kBarBorderRadius);
143 
144   // Create a path to use for clipping the bars
145   QPainterPath clip_path;
146   clip_path.addRoundedRect(r, kBarBorderRadius, kBarBorderRadius);
147   p->setClipPath(clip_path);
148 
149   // Draw any additional space
150   if (additional_ > 0) {
151     QRect additional_rect(bar_rect);
152     additional_rect.setLeft(bar_rect.right());
153     additional_rect.setWidth(static_cast<int>(static_cast<float>(r.width()) * (static_cast<float>(qMin(free_, additional_)) / total_) + 1));
154 
155     QLinearGradient additional_gradient(additional_rect.topLeft(), additional_rect.bottomLeft());
156     additional_gradient.setColorAt(0, kColorAdd1);
157     additional_gradient.setColorAt(1, kColorAdd2);
158 
159     p->fillRect(additional_rect, additional_gradient);
160   }
161 
162   // Draw the bar foreground
163   p->fillRect(bar_rect, bar_gradient);
164 
165   // Draw a border
166   p->setClipping(false);
167   p->setPen(kColorBorder);
168   p->setBrush(Qt::NoBrush);
169   p->drawRoundedRect(r, kBarBorderRadius, kBarBorderRadius);
170 
171   // Draw marker lines over the top every few pixels
172   p->setOpacity(0.35);
173   p->setRenderHint(QPainter::Antialiasing, false);
174   p->setPen(QPen(palette().color(QPalette::Light), 1.0));
175   for (int x = r.left() + kMarkerSpacing; x < r.right(); x += kMarkerSpacing) {
176     p->drawLine(x, r.top() + 2, x, r.bottom() - 2);
177   }
178 
179   p->setOpacity(1.0);
180 
181 }
182 
DrawText(QPainter * p,const QRect r)183 void FreeSpaceBar::DrawText(QPainter *p, const QRect r) {
184 
185   QFont small_font(font());
186   small_font.setPointSize(small_font.pointSize() - 1);
187   small_font.setBold(true);
188   QFontMetrics small_metrics(small_font);
189   p->setFont(small_font);
190 
191   // Work out the geometry for the text
192   QList<Label> labels;
193   labels << Label(TextForSize(used_text_, total_ - free_), kColorBar1);
194   if (additional_ > 0) {
195     labels << Label(TextForSize(additional_text_, additional_), kColorAdd1);
196   }
197   labels << Label(TextForSize(free_text_, free_ - additional_), kColorBg2);
198 
199   int text_width = 0;
200   for (const Label &label : labels) {
201 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
202     text_width += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.horizontalAdvance(label.text);
203 #else
204     text_width += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.width(label.text);
205 #endif
206   }
207 
208   // Draw the text
209   int x = (r.width() - text_width) / 2;
210 
211   p->setRenderHint(QPainter::Antialiasing, false);
212   for (const Label &label : labels) {
213     const bool light = palette().color(QPalette::Base).value() > 128;
214 
215     QRect box(x, r.top() + (r.height() - kLabelBoxSize)/2, kLabelBoxSize, kLabelBoxSize);
216     p->setPen(label.color.darker());
217     p->setBrush(label.color);
218     p->drawRect(box);
219 
220 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
221     QRect text(x + kLabelBoxSize + kLabelBoxPadding, r.top(), small_metrics.horizontalAdvance(label.text), r.height());
222 #else
223     QRect text(x + kLabelBoxSize + kLabelBoxPadding, r.top(), small_metrics.width(label.text), r.height());
224 #endif
225     p->setPen(light ? label.color.darker() : label.color);
226     p->drawText(text, Qt::AlignCenter, label.text);
227 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
228     x += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.horizontalAdvance(label.text);
229 #else
230     x += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.width(label.text);
231 #endif
232   }
233 
234 }
235 
TextForSize(const QString & prefix,const qint64 size)236 QString FreeSpaceBar::TextForSize(const QString &prefix, const qint64 size) {
237 
238   QString ret;
239   if (size > 0) {
240     ret = Utilities::PrettySize(size);
241   }
242   else if (size < 0) {
243     ret = "-" + Utilities::PrettySize(-size);
244   }
245   else {
246     ret = "0 MB";
247   }
248 
249   if (!prefix.isEmpty()) ret.prepend(prefix + " ");
250 
251   return ret;
252 
253 }
254