+ if (!state)
+ owner_->extents_changed(false, true);
+
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::on_show_hide_row(int row_id)
+{
+ if (row_id >= (int)rows_.size())
+ return;
+
+ rows_[row_id].decode_row->set_visible(!rows_[row_id].decode_row->visible());
+
+ if (!rows_[row_id].decode_row->visible())
+ set_row_collapsed(&rows_[row_id]);
+
+ owner_->extents_changed(false, true);
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::on_show_hide_class(QWidget* sender)
+{
+ void* ann_class_ptr = sender->property("ann_class_ptr").value<void*>();
+ assert(ann_class_ptr);
+ AnnotationClass* ann_class = (AnnotationClass*)ann_class_ptr;
+
+ ann_class->set_visible(!ann_class->visible());
+
+ void* row_ptr = sender->property("decode_trace_row_ptr").value<void*>();
+ assert(row_ptr);
+ DecodeTraceRow* row = (DecodeTraceRow*)row_ptr;
+
+ row->has_hidden_classes = row->decode_row->has_hidden_classes();
+
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::on_show_all_classes()
+{
+ void* row_ptr = QObject::sender()->property("decode_trace_row_ptr").value<void*>();
+ assert(row_ptr);
+ DecodeTraceRow* row = (DecodeTraceRow*)row_ptr;
+
+ for (QCheckBox* cb : row->selectors)
+ cb->setChecked(true);
+
+ row->has_hidden_classes = false;
+
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::on_hide_all_classes()
+{
+ void* row_ptr = QObject::sender()->property("decode_trace_row_ptr").value<void*>();
+ assert(row_ptr);
+ DecodeTraceRow* row = (DecodeTraceRow*)row_ptr;
+
+ for (QCheckBox* cb : row->selectors)
+ cb->setChecked(false);
+
+ row->has_hidden_classes = true;
+
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::on_row_container_resized(QWidget* sender)
+{
+ sender->update();
+
+ owner_->extents_changed(false, true);
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::on_copy_annotation_to_clipboard()
+{
+ if (!selected_row_)
+ return;
+
+ deque<const Annotation*> annotations;
+
+ decode_signal_->get_annotation_subset(annotations, selected_row_,
+ current_segment_, selected_sample_range_.first, selected_sample_range_.first);
+
+ if (annotations.empty())
+ return;
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(annotations.front()->annotations()->front(), QClipboard::Clipboard);
+
+ if (clipboard->supportsSelection())
+ clipboard->setText(annotations.front()->annotations()->front(), QClipboard::Selection);
+}
+
+void DecodeTrace::on_export_row()
+{
+ selected_sample_range_ = make_pair(0, numeric_limits<uint64_t>::max());
+ on_export_row_from_here();
+}
+
+void DecodeTrace::on_export_all_rows()
+{
+ selected_sample_range_ = make_pair(0, numeric_limits<uint64_t>::max());
+ on_export_all_rows_from_here();
+}
+
+void DecodeTrace::on_export_row_with_cursor()
+{
+ const View *view = owner_->view();
+ assert(view);
+
+ if (!view->cursors()->enabled())
+ return;
+
+ const double samplerate = session_.get_samplerate();
+
+ const pv::util::Timestamp& start_time = view->cursors()->first()->time();
+ const pv::util::Timestamp& end_time = view->cursors()->second()->time();
+
+ const uint64_t start_sample = (uint64_t)max(
+ 0.0, start_time.convert_to<double>() * samplerate);
+ const uint64_t end_sample = (uint64_t)max(
+ 0.0, end_time.convert_to<double>() * samplerate);
+
+ // Are both cursors negative and thus were clamped to 0?
+ if ((start_sample == 0) && (end_sample == 0))
+ return;
+
+ selected_sample_range_ = make_pair(start_sample, end_sample);
+ on_export_row_from_here();
+}
+
+void DecodeTrace::on_export_all_rows_with_cursor()
+{
+ const View *view = owner_->view();
+ assert(view);
+
+ if (!view->cursors()->enabled())
+ return;
+
+ const double samplerate = session_.get_samplerate();
+
+ const pv::util::Timestamp& start_time = view->cursors()->first()->time();
+ const pv::util::Timestamp& end_time = view->cursors()->second()->time();
+
+ const uint64_t start_sample = (uint64_t)max(
+ 0.0, start_time.convert_to<double>() * samplerate);
+ const uint64_t end_sample = (uint64_t)max(
+ 0.0, end_time.convert_to<double>() * samplerate);
+
+ // Are both cursors negative and thus were clamped to 0?
+ if ((start_sample == 0) && (end_sample == 0))
+ return;
+
+ selected_sample_range_ = make_pair(start_sample, end_sample);
+ on_export_all_rows_from_here();
+}
+
+void DecodeTrace::on_export_row_from_here()
+{
+ if (!selected_row_)
+ return;
+
+ deque<const Annotation*> annotations;
+
+ decode_signal_->get_annotation_subset(annotations, selected_row_,
+ current_segment_, selected_sample_range_.first, selected_sample_range_.second);
+
+ if (annotations.empty())
+ return;
+
+ export_annotations(annotations);
+}
+
+void DecodeTrace::on_export_all_rows_from_here()
+{
+ deque<const Annotation*> annotations;
+
+ decode_signal_->get_annotation_subset(annotations, current_segment_,
+ selected_sample_range_.first, selected_sample_range_.second);
+
+ if (!annotations.empty())
+ export_annotations(annotations);
+}
+
+void DecodeTrace::on_animation_timer()
+{
+ bool animation_finished = true;
+
+ for (DecodeTraceRow& r : rows_) {
+ if (!(r.expanding || r.collapsing))
+ continue;
+
+ unsigned int height_delta = r.expanded_height - default_row_height_;
+
+ if (r.expanding) {
+ if (r.height < r.expanded_height) {
+ r.anim_height += height_delta / (float)AnimationDurationInTicks;
+ r.height = min((int)r.anim_height, (int)r.expanded_height);
+ r.anim_shape += ArrowSize / (float)AnimationDurationInTicks;
+ animation_finished = false;
+ } else
+ set_row_expanded(&r);
+ }
+
+ if (r.collapsing) {
+ if (r.height > default_row_height_) {
+ r.anim_height -= height_delta / (float)AnimationDurationInTicks;
+ r.height = max((int)r.anim_height, (int)0);
+ r.anim_shape -= ArrowSize / (float)AnimationDurationInTicks;
+ animation_finished = false;
+ } else
+ set_row_collapsed(&r);
+ }
+
+ // The expansion marker shape switches between
+ // 0/-A, A/0, 0/A (default state; anim_shape=0) and
+ // 0/ 0, A/A, 2A/0 (expanded state; anim_shape=ArrowSize)
+
+ r.expand_marker_shape.setPoint(0, 0, -ArrowSize + r.anim_shape);
+ r.expand_marker_shape.setPoint(1, ArrowSize, r.anim_shape);
+ r.expand_marker_shape.setPoint(2, 2*r.anim_shape, ArrowSize - r.anim_shape);
+ }
+
+ if (animation_finished)
+ animation_timer_.stop();
+
+ owner_->extents_changed(false, true);
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::on_hide_hidden_rows()
+{
+ // Make all hidden traces invisible again unless the user is hovering over a row name
+ bool any_highlighted = false;
+
+ for (DecodeTraceRow& r : rows_)
+ if (r.expand_marker_highlighted)
+ any_highlighted = true;
+
+ if (!any_highlighted) {
+ show_hidden_rows_ = false;
+
+ owner_->extents_changed(false, true);