+void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
+{
+ // Set colors and fixed widths
+ QFontMetrics m(QApplication::font());
+
+ QPalette header_palette = owner_->view()->palette();
+ QPalette selector_palette = owner_->view()->palette();
+
+ if (GlobalSettings::current_theme_is_dark()) {
+ header_palette.setColor(QPalette::Background,
+ QColor(255, 255, 255, ExpansionAreaHeaderAlpha));
+ selector_palette.setColor(QPalette::Background,
+ QColor(255, 255, 255, ExpansionAreaAlpha));
+ } else {
+ header_palette.setColor(QPalette::Background,
+ QColor(0, 0, 0, ExpansionAreaHeaderAlpha));
+ selector_palette.setColor(QPalette::Background,
+ QColor(0, 0, 0, ExpansionAreaAlpha));
+ }
+
+ const int w = m.boundingRect(r->decode_row->title()).width() + RowTitleMargin;
+ r->title_width = w;
+
+ // Set up top-level container
+ connect(r->container, SIGNAL(widgetResized(QWidget*)),
+ this, SLOT(on_row_container_resized(QWidget*)));
+
+ QVBoxLayout* vlayout = new QVBoxLayout();
+ r->container->setLayout(vlayout);
+
+ // Add header container
+ vlayout->addWidget(r->header_container);
+ vlayout->setContentsMargins(0, 0, 0, 0);
+ vlayout->setSpacing(0);
+ QHBoxLayout* header_container_layout = new QHBoxLayout();
+ r->header_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ r->header_container->setMinimumSize(0, default_row_height_);
+ r->header_container->setLayout(header_container_layout);
+ r->header_container->layout()->setContentsMargins(10, 2, 10, 2);
+
+ r->header_container->setAutoFillBackground(true);
+ r->header_container->setPalette(header_palette);
+
+ // Add widgets inside the header container
+ QCheckBox* cb = new QCheckBox();
+ r->row_visibility_checkbox = cb;
+ header_container_layout->addWidget(cb);
+ cb->setText(tr("Show this row"));
+ cb->setChecked(r->decode_row->visible());
+
+ row_show_hide_mapper_.setMapping(cb, row_id);
+ connect(cb, SIGNAL(stateChanged(int)),
+ &row_show_hide_mapper_, SLOT(map()));
+
+ QPushButton* btn = new QPushButton();
+ header_container_layout->addWidget(btn);
+ btn->setFlat(true);
+ btn->setStyleSheet(":hover { background-color: palette(button); color: palette(button-text); border:0; }");
+ btn->setText(tr("Show All"));
+ btn->setProperty("decode_trace_row_ptr", QVariant::fromValue((void*)r));
+ connect(btn, SIGNAL(clicked(bool)), this, SLOT(on_show_all_classes()));
+
+ btn = new QPushButton();
+ header_container_layout->addWidget(btn);
+ btn->setFlat(true);
+ btn->setStyleSheet(":hover { background-color: palette(button); color: palette(button-text); border:0; }");
+ btn->setText(tr("Hide All"));
+ btn->setProperty("decode_trace_row_ptr", QVariant::fromValue((void*)r));
+ connect(btn, SIGNAL(clicked(bool)), this, SLOT(on_hide_all_classes()));
+
+ header_container_layout->addStretch(); // To left-align the header widgets
+
+ // Add selector container
+ vlayout->addWidget(r->selector_container);
+ r->selector_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ r->selector_container->setLayout(new FlowLayout(r->selector_container));
+
+ r->selector_container->setAutoFillBackground(true);
+ r->selector_container->setPalette(selector_palette);
+
+ // Add all classes that can be toggled
+ vector<AnnotationClass*> ann_classes = r->decode_row->ann_classes();
+
+ for (const AnnotationClass* ann_class : ann_classes) {
+ cb = new QCheckBox();
+ cb->setText(tr(ann_class->description));
+ cb->setChecked(ann_class->visible());
+
+ int dim = ViewItemPaintParams::text_height() - 2;
+ QPixmap pixmap(dim, dim);
+ pixmap.fill(r->decode_row->get_class_color(ann_class->id));
+ cb->setIcon(pixmap);
+
+ r->selector_container->layout()->addWidget(cb);
+ r->selectors.push_back(cb);
+
+ cb->setProperty("ann_class_ptr", QVariant::fromValue((void*)ann_class));
+ cb->setProperty("decode_trace_row_ptr", QVariant::fromValue((void*)r));
+
+ class_show_hide_mapper_.setMapping(cb, cb);
+ connect(cb, SIGNAL(stateChanged(int)),
+ &class_show_hide_mapper_, SLOT(map()));
+ }
+}
+
+void DecodeTrace::update_rows()
+{
+ if (!owner_)
+ return;
+
+ lock_guard<mutex> lock(row_modification_mutex_);
+
+ for (DecodeTraceRow& r : rows_)
+ r.exists = false;
+
+ unsigned int row_id = 0;
+ for (Row* decode_row : decode_signal_->get_rows()) {
+ // Find row in our list
+ auto r_it = find_if(rows_.begin(), rows_.end(),
+ [&](DecodeTraceRow& r){ return r.decode_row == decode_row; });
+
+ DecodeTraceRow* r = nullptr;
+ if (r_it == rows_.end()) {
+ // Row doesn't exist yet, create and append it
+ DecodeTraceRow nr;
+ nr.decode_row = decode_row;
+ nr.decode_row->set_base_color(base_->color());
+ nr.height = default_row_height_;
+ nr.expanded_height = default_row_height_;
+ nr.currently_visible = false;
+ nr.has_hidden_classes = decode_row->has_hidden_classes();
+ nr.expand_marker_highlighted = false;
+ nr.expanding = false;
+ nr.expanded = false;
+ nr.collapsing = false;
+ nr.expand_marker_shape = default_marker_shape_;
+ nr.container = new ContainerWidget(owner_->view()->scrollarea());
+ nr.header_container = new QWidget(nr.container);
+ nr.selector_container = new QWidget(nr.container);
+
+ rows_.push_back(nr);
+ r = &rows_.back();
+ initialize_row_widgets(r, row_id);
+ } else
+ r = &(*r_it);
+
+ r->exists = true;
+ row_id++;
+ }
+
+ // If there's only one row, it must not be hidden or else it can't be un-hidden
+ if (row_id == 1)
+ rows_.front().row_visibility_checkbox->setEnabled(false);
+
+ // Remove any rows that no longer exist, obeying that iterators are invalidated
+ bool any_exists;
+ do {
+ any_exists = false;
+
+ for (unsigned int i = 0; i < rows_.size(); i++)
+ if (!rows_[i].exists) {
+ delete rows_[i].row_visibility_checkbox;
+
+ for (QCheckBox* cb : rows_[i].selectors)
+ delete cb;
+
+ delete rows_[i].selector_container;
+ delete rows_[i].header_container;
+ delete rows_[i].container;
+
+ rows_.erase(rows_.begin() + i);
+ any_exists = true;
+ break;
+ }
+ } while (any_exists);
+}
+
+void DecodeTrace::set_row_expanded(DecodeTraceRow* r)
+{
+ r->height = r->expanded_height;
+ r->expanding = false;
+ r->expanded = true;
+
+ // For details on this, see on_animation_timer()
+ r->expand_marker_shape.setPoint(0, 0, 0);
+ r->expand_marker_shape.setPoint(1, ArrowSize, ArrowSize);
+ r->expand_marker_shape.setPoint(2, 2*ArrowSize, 0);
+
+ r->container->resize(owner_->view()->viewport()->width() - r->container->pos().x(),
+ r->height - 2 * default_row_height_);
+}
+
+void DecodeTrace::set_row_collapsed(DecodeTraceRow* r)
+{
+ r->height = default_row_height_;
+ r->collapsing = false;
+ r->expanded = false;
+ r->expand_marker_shape = default_marker_shape_;
+ r->container->setVisible(false);
+
+ r->container->resize(owner_->view()->viewport()->width() - r->container->pos().x(),
+ r->height - 2 * default_row_height_);
+}
+
+void DecodeTrace::update_expanded_rows()
+{
+ for (DecodeTraceRow& r : rows_) {
+ if (r.expanding || r.expanded)
+ r.expanded_height = 2 * default_row_height_ + r.container->sizeHint().height();
+
+ if (r.expanded)
+ r.height = r.expanded_height;
+
+ int x = 2 * ArrowSize;
+ int y = get_row_y(&r) + default_row_height_;
+ // Only update the position if it actually changes
+ if ((x != r.container->pos().x()) || (y != r.container->pos().y()))
+ r.container->move(x, y);
+
+ int w = owner_->view()->viewport()->width() - x;
+ int h = r.height - 2 * default_row_height_;
+ // Only update the dimension if they actually change
+ if ((w != r.container->sizeHint().width()) || (h != r.container->sizeHint().height()))
+ r.container->resize(w, h);
+ }
+}
+