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