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