1 #include "widget/wdisplay.h"
2
3 #include <QPaintEvent>
4 #include <QPixmap>
5 #include <QStyleOption>
6 #include <QStylePainter>
7 #include <QtDebug>
8
9 #include "moc_wdisplay.cpp"
10 #include "widget/wpixmapstore.h"
11
WDisplay(QWidget * parent)12 WDisplay::WDisplay(QWidget * parent)
13 : WWidget(parent),
14 m_iCurrentPixmap(0),
15 m_pPixmapBack(nullptr),
16 m_bDisabledLoaded(false) {
17 setPositions(0);
18 }
19
~WDisplay()20 WDisplay::~WDisplay() {
21 resetPositions();
22 }
23
setup(const QDomNode & node,const SkinContext & context)24 void WDisplay::setup(const QDomNode& node, const SkinContext& context) {
25 // Set background pixmap if available
26
27 QDomElement backPathNode = context.selectElement(node, "BackPath");
28 if (!backPathNode.isNull()) {
29 setPixmapBackground(context.getPixmapSource(backPathNode),
30 context.selectScaleMode(backPathNode, Paintable::TILE),
31 context.getScaleFactor());
32 }
33
34 // Number of states
35 setPositions(context.selectInt(node, "NumberStates"));
36
37 // Load knob pixmaps
38 QDomElement pathNode = context.selectElement(node, "Path");
39 QString path = context.nodeToString(pathNode);
40 // The implicit default in <1.12.0 was FIXED so we keep it for
41 // backwards compatibility.
42 Paintable::DrawMode pathMode =
43 context.selectScaleMode(pathNode, Paintable::FIXED);
44 for (int i = 0; i < m_pixmaps.size(); ++i) {
45 setPixmap(&m_pixmaps, i, context.makeSkinPath(path.arg(i)),
46 pathMode, context.getScaleFactor());
47 }
48
49 // See if disabled images is defined, and load them...
50 QDomElement disabledNode = context.selectElement(node, "DisabledPath");
51 if (!disabledNode.isNull()) {
52 QString disabledPath = context.nodeToString(disabledNode);
53 // The implicit default in <1.12.0 was FIXED so we keep it for
54 // backwards compatibility.
55 Paintable::DrawMode disabledMode =
56 context.selectScaleMode(disabledNode, Paintable::FIXED);
57 for (int i = 0; i < m_disabledPixmaps.size(); ++i) {
58 setPixmap(&m_disabledPixmaps, i,
59 context.makeSkinPath(disabledPath.arg(i)),
60 disabledMode, context.getScaleFactor());
61 }
62 m_bDisabledLoaded = true;
63 }
64 }
65
setPositions(int iNoPos)66 void WDisplay::setPositions(int iNoPos) {
67 resetPositions();
68
69 if (iNoPos < 0) {
70 qWarning() << "Negative NumberStates for Display.";
71 iNoPos = 0;
72 }
73
74 // QVector inserts NULLs for the new pixmaps.
75 m_pixmaps.resize(iNoPos);
76 m_disabledPixmaps.resize(iNoPos);
77 }
78
resetPositions()79 void WDisplay::resetPositions() {
80 m_pPixmapBack.clear();
81 m_pixmaps.resize(0);
82 m_disabledPixmaps.resize(0);
83 }
84
setPixmapBackground(const PixmapSource & source,Paintable::DrawMode mode,double scaleFactor)85 void WDisplay::setPixmapBackground(const PixmapSource& source,
86 Paintable::DrawMode mode,
87 double scaleFactor) {
88 m_pPixmapBack = WPixmapStore::getPaintable(source, mode, scaleFactor);
89 if (m_pPixmapBack.isNull() || m_pPixmapBack->isNull()) {
90 qDebug() << metaObject()->className()
91 << "Error loading background pixmap:" << source.getPath();
92 }
93 }
94
setPixmap(QVector<PaintablePointer> * pPixmaps,int iPos,const QString & filename,Paintable::DrawMode mode,double scaleFactor)95 void WDisplay::setPixmap(
96 QVector<PaintablePointer>* pPixmaps,
97 int iPos,
98 const QString& filename,
99 Paintable::DrawMode mode,
100 double scaleFactor) {
101 if (iPos < 0 || iPos >= pPixmaps->size()) {
102 return;
103 }
104
105 PixmapSource source(filename);
106 PaintablePointer pPixmap = WPixmapStore::getPaintable(source, mode, scaleFactor);
107 if (pPixmap.isNull() || pPixmap->isNull()) {
108 qDebug() << metaObject()->className()
109 << "Error loading pixmap:" << filename;
110 } else {
111 (*pPixmaps)[iPos] = pPixmap;
112 if (mode == Paintable::FIXED) {
113 setFixedSize(pPixmap->size());
114 }
115 }
116 }
117
getPixmapForParameter(double dParameter) const118 int WDisplay::getPixmapForParameter(double dParameter) const {
119 // When there are an even number of pixmaps by convention we want a value of
120 // 0.5 to align to the lower of the two middle pixmaps. In Mixxx < 1.12.0 we
121 // accomplished this by the below formula:
122 // index = (m_value - 64.0/127.0) * (numPixmaps() - 1) + numPixmaps() / 2.0;
123
124 // But it's just as good to use m_value * numPixmaps() - epsilon. Using
125 // numPixmaps() instead of numPixmaps() - 1 ensures that every pixmap shares
126 // an equal slice of the value. Using m_value * (numPixmaps() - 1) gives an
127 // unequal slice of the value to the last pixmaps.
128
129 // Example:
130 // 3 pixmaps
131 // m_value * numPixmaps()
132 // idx: 0 1 2 3
133 // val: 0.0 ... 0.3 ... 0.6 ... 1.0
134 // Even distribution of value range, value 1 is out of bounds (3).
135
136 // m_value * (numPixmaps() - 1)
137 // idx: 0 1 2
138 // val: 0.0 ... 0.5 ... 1.0
139 // Pixmap 2 is only shown at value 1.
140
141 // floor(m_value * (numPixmaps() - 1) + 0.5)
142 // idx: 0 1 2
143 // val: 0.0 ... 0.25 ... 0.75 ... 1.0
144 // Pixmap 0 and Pixmap 2 only shown for 0.25 of value range
145
146 // 4 pixmaps
147 // m_value * numPixmaps()
148 // idx: 0 1 2 3 4
149 // val: 0.0 ... 0.25 ... 0.5 ... 0.75 ... 1.0
150 // Even distribution of value range, value 1 is out of bounds (4).
151
152 // Subtracting an epsilon prevents out of bound values at the end of the
153 // range and biases the middle value towards the lower of the 2 center
154 // pixmaps when there are an even number of pixmaps.
155 return static_cast<int>(dParameter * numPixmaps() - 0.00001);
156 }
157
onConnectedControlChanged(double dParameter,double dValue)158 void WDisplay::onConnectedControlChanged(double dParameter, double dValue) {
159 Q_UNUSED(dValue);
160 int pixmap = getPixmapForParameter(dParameter);
161 if (pixmap != m_iCurrentPixmap) {
162 // paintEvent updates m_iCurrentPixmap.
163 update();
164 }
165 }
166
paintEvent(QPaintEvent *)167 void WDisplay::paintEvent(QPaintEvent* /*unused*/) {
168 QStyleOption option;
169 option.initFrom(this);
170 QStylePainter p(this);
171 p.drawPrimitive(QStyle::PE_Widget, option);
172
173 if (m_pPixmapBack) {
174 m_pPixmapBack->draw(rect(), &p);
175 }
176
177 // If we are disabled, use the disabled pixmaps. If not, use the regular
178 // pixmaps.
179 const QVector<PaintablePointer>& pixmaps = (!isEnabled() && m_bDisabledLoaded) ?
180 m_disabledPixmaps : m_pixmaps;
181
182 if (pixmaps.empty()) {
183 return;
184 }
185
186 int idx = getPixmapForParameter(getControlParameterDisplay());
187
188 // onConnectedControlChanged uses this to detect no-ops but it does not
189 // clamp so don't clamp.
190 m_iCurrentPixmap = idx;
191
192 // Clamp active pixmap index to valid ranges.
193 if (idx < 0) {
194 idx = 0;
195 } else if (idx >= pixmaps.size()) {
196 idx = pixmaps.size() - 1;
197 }
198
199 PaintablePointer pPixmap = pixmaps[idx];
200 if (pPixmap) {
201 pPixmap->draw(rect(), &p);
202 }
203 }
204