X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fprop%2Fenum.cpp;h=d62c901cf4c70d2c3848b3a3875d9d088596b794;hp=065fd548f22b336151d03da49f0c03e43456bce1;hb=b571a8e7e0dc3e3b6daa58f27050e76466f006dd;hpb=9b25892767cee1d871ac18beb025033e905b28ae diff --git a/pv/prop/enum.cpp b/pv/prop/enum.cpp index 065fd548..d62c901c 100644 --- a/pv/prop/enum.cpp +++ b/pv/prop/enum.cpp @@ -14,71 +14,281 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ -#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include -#include "enum.h" +#include -using namespace boost; -using namespace std; +#include "enum.hpp" + +using std::abs; +// Note that "using std::isnan;" is _not_ put here since that would break +// compilation on some platforms. Use "std::isnan()" instead in checks below. +using std::numeric_limits; +using std::pair; +using std::vector; namespace pv { namespace prop { -Enum::Enum(QString name, - vector > values, +Enum::Enum(QString name, QString desc, + vector > values, Getter getter, Setter setter) : - Property(name, getter, setter), - _values(values), - _selector(NULL) + Property(name, desc, getter, setter), + values_(values), + is_range_(false), + selector_(nullptr), + slider_layout_widget_(nullptr), + slider_(nullptr), + slider_label_(nullptr) { + // Try to determine whether the values make up a range, created by e.g. + // std_gvar_min_max_step_thresholds() + + vector deltas; + double prev_value = 0; + + for (const pair &v : values_) { + gdouble value; + if (v.first.is_of_type(Glib::VariantType("d"))) { + g_variant_get((GVariant*)(v.first.gobj()), "d", &value); + } else if (v.first.is_of_type(Glib::VariantType("(dd)"))) { + gdouble dummy; + g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &value, &dummy); + } else + break; // Type not d or (dd), so not a range that we can handle + deltas.push_back(value - prev_value); + prev_value = value; + } + + if (deltas.size() > 0) { + bool constant_delta = true; + double prev_delta = numeric_limits::quiet_NaN(); + + bool skip_first = true; + for (double delta : deltas) { + // First value is incorrect, it's the delta to 0 since no + // previous value existed yet + if (skip_first) { + skip_first = false; + continue; + } + if (std::isnan(prev_delta)) + prev_delta = delta; + + // 2*DBL_EPSILON doesn't work here, so use a workaround + if (abs(delta - prev_delta) > (delta/10)) + constant_delta = false; + + prev_delta = delta; + } + + if (constant_delta) + is_range_ = true; + } } -Enum::~Enum() +QWidget* Enum::get_widget(QWidget *parent, bool auto_commit) { - for (unsigned int i = 0; i < _values.size(); i++) - g_variant_unref(_values[i].first); + if (!getter_) + return nullptr; + + Glib::VariantBase variant; + + try { + variant = getter_(); + } catch (const sigrok::Error &e) { + qWarning() << tr("Querying config key %1 resulted in %2").arg(name_, e.what()); + return nullptr; + } + + if (!variant.gobj()) + return nullptr; + + if (is_range_) { + // Use slider + if (slider_layout_widget_) + return slider_layout_widget_; + + slider_ = new QSlider(); + // Sliders can't handle float values, so we just use it to specify + // the number of steps that we're away from the range's beginning + slider_->setOrientation(Qt::Horizontal); + slider_->setMinimum(0); + slider_->setMaximum(values_.size() - 1); + slider_->setSingleStep(1); + + slider_label_ = new QLabel(); + + slider_layout_widget_ = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(slider_layout_widget_); + layout->addWidget(slider_); + layout->addWidget(slider_label_); + + update_widget(); + + if (auto_commit) + connect(slider_, SIGNAL(valueChanged(int)), + this, SLOT(on_value_changed(int))); + + return slider_layout_widget_; + + } else { + // Use combo box + if (selector_) + return selector_; + + selector_ = new QComboBox(parent); + for (unsigned int i = 0; i < values_.size(); i++) { + const pair &v = values_[i]; + selector_->addItem(v.second, qVariantFromValue(v.first)); + } + + update_widget(); + + if (auto_commit) + connect(selector_, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_current_index_changed(int))); + + return selector_; + } } -QWidget* Enum::get_widget(QWidget *parent) +void Enum::update_widget() { - if (_selector) - return _selector; - - GVariant *const value = _getter ? _getter() : NULL; - assert(value); - - _selector = new QComboBox(parent); - for (unsigned int i = 0; i < _values.size(); i++) { - const pair &v = _values[i]; - _selector->addItem(v.second, qVariantFromValue((void*)v.first)); - if (g_variant_compare(v.first, value) == 0) - _selector->setCurrentIndex(i); + Glib::VariantBase variant; + + try { + variant = getter_(); + } catch (const sigrok::Error &e) { + qWarning() << tr("Querying config key %1 resulted in %2").arg(name_, e.what()); + return; } - g_variant_unref(value); + assert(variant.gobj()); + + if (is_range_) { + + // Use slider + if (!slider_layout_widget_) + return; + + for (unsigned int i = 0; i < values_.size(); i++) { + const pair &v = values_[i]; + + // g_variant_equal() doesn't handle floating point properly + if (v.first.is_of_type(Glib::VariantType("d"))) { + gdouble a, b; + g_variant_get(variant.gobj(), "d", &a); + g_variant_get((GVariant*)(v.first.gobj()), "d", &b); + + if (abs(a - b) <= 2 * DBL_EPSILON) { + slider_->setValue(i); + slider_label_->setText(v.second); + } + } else { + // Check for "(dd)" type and handle it if it's found + if (v.first.is_of_type(Glib::VariantType("(dd)"))) { + gdouble a1, a2, b1, b2; + g_variant_get(variant.gobj(), "(dd)", &a1, &a2); + g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &b1, &b2); + + if ((abs(a1 - b1) <= 2 * DBL_EPSILON) && \ + (abs(a2 - b2) <= 2 * DBL_EPSILON)) { + slider_->setValue(i); + slider_label_->setText(v.second); + } + + } else { + qWarning() << "Enum property" << name() << "encountered unsupported type"; + return; + } + } + } + + } else { + // Use combo box + if (!selector_) + return; + + for (unsigned int i = 0; i < values_.size(); i++) { + const pair &v = values_[i]; + + // g_variant_equal() doesn't handle floating point properly + if (v.first.is_of_type(Glib::VariantType("d"))) { + gdouble a, b; + g_variant_get(variant.gobj(), "d", &a); + g_variant_get((GVariant*)(v.first.gobj()), "d", &b); + if (abs(a - b) <= 2 * DBL_EPSILON) + selector_->setCurrentIndex(i); + } else { + // Check for "(dd)" type and handle it if it's found + if (v.first.is_of_type(Glib::VariantType("(dd)"))) { + gdouble a1, a2, b1, b2; + g_variant_get(variant.gobj(), "(dd)", &a1, &a2); + g_variant_get((GVariant*)(v.first.gobj()), "(dd)", &b1, &b2); + if ((abs(a1 - b1) <= 2 * DBL_EPSILON) && \ + (abs(a2 - b2) <= 2 * DBL_EPSILON)) + selector_->setCurrentIndex(i); - return _selector; + } else + // Handle all other types + if (v.first.equal(variant)) + selector_->setCurrentIndex(i); + } + } + } } void Enum::commit() { - assert(_setter); + assert(setter_); - if (!_selector) - return; + if (is_range_) { + // Use slider + if (!slider_layout_widget_) + return; - const int index = _selector->currentIndex(); - if (index < 0) - return; + setter_(values_.at(slider_->value()).first); + + update_widget(); + } else { + // Use combo box + if (!selector_) + return; - _setter((GVariant*)_selector->itemData(index).value()); + const int index = selector_->currentIndex(); + if (index < 0) + return; + + setter_(selector_->itemData(index).value()); + + // The combo box needs no update, it already shows the current value + // by definition: the user picked it + } +} + +void Enum::on_current_index_changed(int) +{ + commit(); +} + +void Enum::on_value_changed(int) +{ + commit(); } -} // prop -} // pv +} // namespace prop +} // namespace pv