2 * This file is part of the PulseView project.
4 * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
28 #include <QHBoxLayout>
32 #include <libsigrokcxx/libsigrokcxx.hpp>
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.
39 using std::numeric_limits;
46 Enum::Enum(QString name, QString desc,
47 vector<pair<Glib::VariantBase, QString> > values,
48 Getter getter, Setter setter) :
49 Property(name, desc, getter, setter),
53 slider_layout_widget_(nullptr),
55 slider_label_(nullptr)
57 // Try to determine whether the values make up a range, created by e.g.
58 // std_gvar_min_max_step_thresholds()
60 vector<double> deltas;
61 double prev_value = 0;
63 for (const pair<Glib::VariantBase, QString> &v : values_) {
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)"))) {
69 g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &value, &dummy);
71 break; // Type not d or (dd), so not a range that we can handle
72 deltas.push_back(value - prev_value);
76 if (deltas.size() > 0) {
77 bool constant_delta = true;
78 double prev_delta = numeric_limits<double>::quiet_NaN();
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
88 if (std::isnan(prev_delta))
91 // 2*DBL_EPSILON doesn't work here, so use a workaround
92 if (abs(delta - prev_delta) > (delta/10))
93 constant_delta = false;
103 QWidget* Enum::get_widget(QWidget *parent, bool auto_commit)
108 Glib::VariantBase variant;
112 } catch (const sigrok::Error &e) {
113 qWarning() << tr("Querying config key %1 resulted in %2").arg(name_, e.what());
122 if (slider_layout_widget_)
123 return slider_layout_widget_;
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);
133 slider_label_ = new QLabel();
135 slider_layout_widget_ = new QWidget(parent);
136 QHBoxLayout *layout = new QHBoxLayout(slider_layout_widget_);
137 layout->addWidget(slider_);
138 layout->addWidget(slider_label_);
143 connect(slider_, SIGNAL(valueChanged(int)),
144 this, SLOT(on_value_changed(int)));
146 return slider_layout_widget_;
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));
162 connect(selector_, SIGNAL(currentIndexChanged(int)),
163 this, SLOT(on_current_index_changed(int)));
169 void Enum::update_widget()
171 Glib::VariantBase variant;
175 } catch (const sigrok::Error &e) {
176 qWarning() << tr("Querying config key %1 resulted in %2").arg(name_, e.what());
180 assert(variant.gobj());
185 if (!slider_layout_widget_)
188 for (unsigned int i = 0; i < values_.size(); i++) {
189 const pair<Glib::VariantBase, QString> &v = values_[i];
191 // g_variant_equal() doesn't handle floating point properly
192 if (v.first.is_of_type(Glib::VariantType("d"))) {
194 g_variant_get(variant.gobj(), "d", &a);
195 g_variant_get((GVariant*)(v.first.gobj()), "d", &b);
197 if (abs(a - b) <= 2 * DBL_EPSILON) {
198 slider_->setValue(i);
199 slider_label_->setText(v.second);
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);
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);
215 qWarning() << "Enum property" << name() << "encountered unsupported type";
226 for (unsigned int i = 0; i < values_.size(); i++) {
227 const pair<Glib::VariantBase, QString> &v = values_[i];
229 // g_variant_equal() doesn't handle floating point properly
230 if (v.first.is_of_type(Glib::VariantType("d"))) {
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)
235 selector_->setCurrentIndex(i);
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);
247 // Handle all other types
248 if (v.first.equal(variant))
249 selector_->setCurrentIndex(i);
261 if (!slider_layout_widget_)
264 setter_(values_.at(slider_->value()).first);
272 const int index = selector_->currentIndex();
276 setter_(selector_->itemData(index).value<Glib::VariantBase>());
278 // The combo box needs no update, it already shows the current value
279 // by definition: the user picked it
283 void Enum::on_current_index_changed(int)
288 void Enum::on_value_changed(int)