]> sigrok.org Git - pulseview.git/blame - pv/prop/enum.cpp
Enum: Use slider instead of combo box for contiguous ranges
[pulseview.git] / pv / prop / enum.cpp
CommitLineData
820c3dea
JH
1/*
2 * This file is part of the PulseView project.
3 *
4 * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
5 *
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
efdec55a 17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
820c3dea
JH
18 */
19
eb8269e3 20#include <cassert>
a2f96263
SA
21#include <cfloat>
22#include <cmath>
73078ef7
SA
23#include <limits>
24#include <vector>
24bb0bd1 25
820c3dea 26#include <QComboBox>
73078ef7
SA
27#include <QDebug>
28#include <QHBoxLayout>
29#include <QLabel>
30#include <QSlider>
820c3dea 31
2acdb232 32#include "enum.hpp"
820c3dea 33
a2f96263 34using std::abs;
73078ef7
SA
35// Note that "using std::isnan;" is _not_ put here since that would break
36// compilation on some platforms. Use "std::isnan()" instead in checks below.
37using std::numeric_limits;
819f4c25
JH
38using std::pair;
39using std::vector;
820c3dea
JH
40
41namespace pv {
42namespace prop {
43
9a267f8d 44Enum::Enum(QString name, QString desc,
e8d00928 45 vector<pair<Glib::VariantBase, QString> > values,
9b258927 46 Getter getter, Setter setter) :
9a267f8d 47 Property(name, desc, getter, setter),
8dbbc7f0 48 values_(values),
73078ef7
SA
49 is_range_(false),
50 selector_(nullptr),
51 slider_layout_widget_(nullptr),
52 slider_(nullptr),
53 slider_label_(nullptr)
820c3dea 54{
73078ef7
SA
55 // Try to determine whether the values make up a range, created by e.g.
56 // std_gvar_min_max_step_thresholds()
57
58 vector<double> deltas;
59 double prev_value;
60
61 for (const pair<Glib::VariantBase, QString> &v : values_) {
62 gdouble value;
63 if (v.first.is_of_type(Glib::VariantType("d"))) {
64 g_variant_get((GVariant*)(v.first.gobj()), "d", &value);
65 } else if (v.first.is_of_type(Glib::VariantType("(dd)"))) {
66 gdouble dummy;
67 g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &value, &dummy);
68 } else
69 break; // Type not d or (dd), so not a range that we can handle
70 deltas.push_back(value - prev_value);
71 prev_value = value;
72 }
73
74 if (deltas.size() > 0) {
75 bool constant_delta = true;
76 double prev_delta = numeric_limits<double>::quiet_NaN();
77
78 bool skip_first = true;
79 for (double delta : deltas) {
80 // First value is incorrect, it's the delta to 0 since no
81 // previous value existed yet
82 if (skip_first) {
83 skip_first = false;
84 continue;
85 }
86 if (std::isnan(prev_delta))
87 prev_delta = delta;
88
89 // 2*DBL_EPSILON doesn't work here, so use a workaround
90 if (abs(delta - prev_delta) > (delta/10))
91 constant_delta = false;
92
93 prev_delta = delta;
94 }
95
96 if (constant_delta)
97 is_range_ = true;
98 }
820c3dea
JH
99}
100
b1fe148e 101QWidget* Enum::get_widget(QWidget *parent, bool auto_commit)
820c3dea 102{
8dbbc7f0 103 if (!getter_)
4c60462b 104 return nullptr;
e8d00928 105
8dbbc7f0 106 Glib::VariantBase variant = getter_();
e8d00928 107 if (!variant.gobj())
4c60462b 108 return nullptr;
d191508b 109
73078ef7
SA
110 if (is_range_) {
111 // Use slider
112 if (slider_layout_widget_)
113 return slider_layout_widget_;
114
115 slider_ = new QSlider();
116 // Sliders can't handle float values, so we just use it to specify
117 // the number of steps that we're away from the range's beginning
118 slider_->setOrientation(Qt::Horizontal);
119 slider_->setMinimum(0);
120 slider_->setMaximum(values_.size() - 1);
121 slider_->setSingleStep(1);
122
123 slider_label_ = new QLabel();
124
125 slider_layout_widget_ = new QWidget(parent);
126 QHBoxLayout *layout = new QHBoxLayout(slider_layout_widget_);
127 layout->addWidget(slider_);
128 layout->addWidget(slider_label_);
129
130 update_widget();
131
132 if (auto_commit)
133 connect(slider_, SIGNAL(valueChanged(int)),
134 this, SLOT(on_value_changed(int)));
135
136 return slider_layout_widget_;
137
138 } else {
139 // Use combo box
140 if (selector_)
141 return selector_;
142
143 selector_ = new QComboBox(parent);
144 for (unsigned int i = 0; i < values_.size(); i++) {
145 const pair<Glib::VariantBase, QString> &v = values_[i];
146 selector_->addItem(v.second, qVariantFromValue(v.first));
147 }
820c3dea 148
73078ef7 149 update_widget();
dbed5609 150
73078ef7
SA
151 if (auto_commit)
152 connect(selector_, SIGNAL(currentIndexChanged(int)),
153 this, SLOT(on_current_index_changed(int)));
b1fe148e 154
73078ef7
SA
155 return selector_;
156 }
820c3dea
JH
157}
158
dbed5609
SA
159void Enum::update_widget()
160{
dbed5609
SA
161 Glib::VariantBase variant = getter_();
162 assert(variant.gobj());
163
73078ef7 164 if (is_range_) {
a2f96263 165
73078ef7
SA
166 // Use slider
167 if (!slider_layout_widget_)
168 return;
169
170 for (unsigned int i = 0; i < values_.size(); i++) {
171 const pair<Glib::VariantBase, QString> &v = values_[i];
172
173 // g_variant_equal() doesn't handle floating point properly
174 if (v.first.is_of_type(Glib::VariantType("d"))) {
175 gdouble a, b;
176 g_variant_get(variant.gobj(), "d", &a);
177 g_variant_get((GVariant*)(v.first.gobj()), "d", &b);
178
179 if (abs(a - b) <= 2 * DBL_EPSILON) {
180 slider_->setValue(i);
181 slider_label_->setText(v.second);
182 }
183 } else {
184 // Check for "(dd)" type and handle it if it's found
185 if (v.first.is_of_type(Glib::VariantType("(dd)"))) {
186 gdouble a1, a2, b1, b2;
187 g_variant_get(variant.gobj(), "(dd)", &a1, &a2);
188 g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &b1, &b2);
189
190 if ((abs(a1 - b1) <= 2 * DBL_EPSILON) && \
191 (abs(a2 - b2) <= 2 * DBL_EPSILON)) {
192 slider_->setValue(i);
193 slider_label_->setText(v.second);
194 }
195
196 } else {
197 qWarning() << "Enum property" << name() << "encountered unsupported type";
198 return;
199 }
200 }
201 }
a2f96263 202
73078ef7
SA
203 } else {
204 // Use combo box
205 if (!selector_)
206 return;
207
208 for (unsigned int i = 0; i < values_.size(); i++) {
209 const pair<Glib::VariantBase, QString> &v = values_[i];
210
211 // g_variant_equal() doesn't handle floating point properly
212 if (v.first.is_of_type(Glib::VariantType("d"))) {
213 gdouble a, b;
214 g_variant_get(variant.gobj(), "d", &a);
215 g_variant_get((GVariant*)(v.first.gobj()), "d", &b);
216 if (abs(a - b) <= 2 * DBL_EPSILON)
a2f96263 217 selector_->setCurrentIndex(i);
73078ef7
SA
218 } else {
219 // Check for "(dd)" type and handle it if it's found
220 if (v.first.is_of_type(Glib::VariantType("(dd)"))) {
221 gdouble a1, a2, b1, b2;
222 g_variant_get(variant.gobj(), "(dd)", &a1, &a2);
223 g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &b1, &b2);
224 if ((abs(a1 - b1) <= 2 * DBL_EPSILON) && \
225 (abs(a2 - b2) <= 2 * DBL_EPSILON))
226 selector_->setCurrentIndex(i);
227
228 } else
229 // Handle all other types
230 if (v.first.equal(variant))
231 selector_->setCurrentIndex(i);
232 }
a2f96263 233 }
dbed5609
SA
234 }
235}
236
24bb0bd1
JH
237void Enum::commit()
238{
8dbbc7f0 239 assert(setter_);
24bb0bd1 240
73078ef7
SA
241 if (is_range_) {
242 // Use slider
243 if (!slider_layout_widget_)
244 return;
245
246 setter_(values_.at(slider_->value()).first);
247
248 update_widget();
249 } else {
250 // Use combo box
251 if (!selector_)
252 return;
253
254 const int index = selector_->currentIndex();
255 if (index < 0)
256 return;
24bb0bd1 257
73078ef7 258 setter_(selector_->itemData(index).value<Glib::VariantBase>());
24bb0bd1 259
73078ef7
SA
260 // The combo box needs no update, it already shows the current value
261 // by definition: the user picked it
262 }
263}
264
265void Enum::on_current_index_changed(int)
266{
267 commit();
24bb0bd1
JH
268}
269
73078ef7 270void Enum::on_value_changed(int)
b1fe148e
JH
271{
272 commit();
273}
274
870ea3db
UH
275} // namespace prop
276} // namespace pv