+ QAction *const export_all_rows =
+ new QAction(tr("Export all annotations"), this);
+ export_all_rows->setIcon(QIcon::fromTheme("document-save-as",
+ QIcon(":/icons/document-save-as.png")));
+ connect(export_all_rows, SIGNAL(triggered()), this, SLOT(on_export_all_rows()));
+ menu->addAction(export_all_rows);
+
+ QAction *const export_row =
+ new QAction(tr("Export all annotations for this row"), this);
+ export_row->setIcon(QIcon::fromTheme("document-save-as",
+ QIcon(":/icons/document-save-as.png")));
+ connect(export_row, SIGNAL(triggered()), this, SLOT(on_export_row()));
+ menu->addAction(export_row);
+
+ menu->addSeparator();
+
+ QAction *const export_all_rows_from_here =
+ new QAction(tr("Export all annotations, starting here"), this);
+ export_all_rows_from_here->setIcon(QIcon::fromTheme("document-save-as",
+ QIcon(":/icons/document-save-as.png")));
+ connect(export_all_rows_from_here, SIGNAL(triggered()), this, SLOT(on_export_all_rows_from_here()));
+ menu->addAction(export_all_rows_from_here);
+
+ QAction *const export_row_from_here =
+ new QAction(tr("Export annotations for this row, starting here"), this);
+ export_row_from_here->setIcon(QIcon::fromTheme("document-save-as",
+ QIcon(":/icons/document-save-as.png")));
+ connect(export_row_from_here, SIGNAL(triggered()), this, SLOT(on_export_row_from_here()));
+ menu->addAction(export_row_from_here);
+
+ menu->addSeparator();
+
+ QAction *const export_all_rows_with_cursor =
+ new QAction(tr("Export all annotations within cursor range"), this);
+ export_all_rows_with_cursor->setIcon(QIcon::fromTheme("document-save-as",
+ QIcon(":/icons/document-save-as.png")));
+ connect(export_all_rows_with_cursor, SIGNAL(triggered()), this, SLOT(on_export_all_rows_with_cursor()));
+ menu->addAction(export_all_rows_with_cursor);
+
+ QAction *const export_row_with_cursor =
+ new QAction(tr("Export annotations for this row within cursor range"), this);
+ export_row_with_cursor->setIcon(QIcon::fromTheme("document-save-as",
+ QIcon(":/icons/document-save-as.png")));
+ connect(export_row_with_cursor, SIGNAL(triggered()), this, SLOT(on_export_row_with_cursor()));
+ menu->addAction(export_row_with_cursor);
+
+ if (!view->cursors()->enabled()) {
+ export_all_rows_with_cursor->setEnabled(false);
+ export_row_with_cursor->setEnabled(false);
+ }
+
+ return menu;
+}
+
+void DecodeTrace::delete_pressed()
+{
+ on_delete();
+}
+
+void DecodeTrace::hover_point_changed(const QPoint &hp)
+{
+ Trace::hover_point_changed(hp);
+
+ assert(owner_);
+
+ DecodeTraceRow* hover_row = get_row_at_point(hp);
+
+ // Row expansion marker handling
+ for (DecodeTraceRow& r : rows_)
+ r.expand_marker_highlighted = false;
+
+ if (hover_row) {
+ int row_y = get_row_y(hover_row);
+ if ((hp.x() > 0) && (hp.x() < (int)(ArrowSize + 3 + hover_row->title_width)) &&
+ (hp.y() > (int)(row_y - ArrowSize)) && (hp.y() < (int)(row_y + ArrowSize)))
+ hover_row->expand_marker_highlighted = true;
+ }
+
+ // Tooltip handling
+ if (hp.x() > 0) {
+ QString ann = get_annotation_at_point(hp);
+
+ if (!ann.isEmpty()) {
+ QFontMetrics m(QToolTip::font());
+ const QRect text_size = m.boundingRect(QRect(), 0, ann);
+
+ // This is OS-specific and unfortunately we can't query it, so
+ // use an approximation to at least try to minimize the error.
+ const int padding = default_row_height_ + 8;
+
+ // Make sure the tool tip doesn't overlap with the mouse cursor.
+ // If it did, the tool tip would constantly hide and re-appear.
+ // We also push it up by one row so that it appears above the
+ // decode trace, not below.
+ QPoint p = hp;
+ p.setX(hp.x() - (text_size.width() / 2) - padding);
+
+ p.setY(get_row_y(hover_row) - default_row_height_ -
+ text_size.height() - padding);
+
+ const View *const view = owner_->view();
+ assert(view);
+ QToolTip::showText(view->viewport()->mapToGlobal(p), ann);
+
+ } else
+ QToolTip::hideText();
+
+ } else
+ QToolTip::hideText();
+}
+
+void DecodeTrace::mouse_left_press_event(const QMouseEvent* event)
+{
+ // Update container widths which depend on the scrollarea's current width
+ update_expanded_rows();
+
+ // Handle row expansion marker
+ for (DecodeTraceRow& r : rows_) {
+ if (!r.expand_marker_highlighted)
+ continue;
+
+ unsigned int y = get_row_y(&r);
+ if ((event->x() > 0) && (event->x() <= (int)(ArrowSize + 3 + r.title_width)) &&
+ (event->y() > (int)(y - (default_row_height_ / 2))) &&
+ (event->y() <= (int)(y + (default_row_height_ / 2)))) {
+
+ if (r.expanded) {
+ r.collapsing = true;
+ r.expanded = false;
+ r.anim_shape = ArrowSize;
+ } else {
+ r.expanding = true;
+ r.anim_shape = 0;
+
+ // Force geometry update of the widget container to get
+ // an up-to-date height (which also depends on the width)
+ forceUpdate(r.container);
+
+ r.container->setVisible(true);
+ r.expanded_height = 2 * default_row_height_ + r.container->sizeHint().height();
+ }
+
+ r.animation_step = 0;
+ r.anim_height = r.height;
+
+ animation_timer_.start();
+ }
+ }
+}
+
+void DecodeTrace::draw_annotations(vector<const Annotation*> annotations,
+ QPainter &p, const ViewItemPaintParams &pp, int y, const DecodeTraceRow& row)
+{
+ Annotation::Class block_class = 0;
+ bool block_class_uniform = true;
+ qreal block_start = 0;
+ int block_ann_count = 0;
+
+ const Annotation* prev_ann;
+ qreal prev_end = INT_MIN;
+
+ qreal a_end;