#include <QAction>
#include <QApplication>
+#include <QClipboard>
#include <QComboBox>
#include <QFileDialog>
#include <QFormLayout>
using pv::data::decode::Annotation;
using pv::data::decode::Row;
-using pv::data::DecodeChannel;
+using pv::data::decode::DecodeChannel;
using pv::data::DecodeSignal;
namespace pv {
{
decode_signal_ = dynamic_pointer_cast<data::DecodeSignal>(base_);
+ GlobalSettings settings;
+ always_show_all_rows_ = settings.value(GlobalSettings::Key_Dec_AlwaysShowAllRows).toBool();
+
+ GlobalSettings::add_change_handler(this);
+
// Determine shortest string we want to see displayed in full
QFontMetrics m(QApplication::font());
min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
delayed_trace_updater_.setInterval(1000 / MaxTraceUpdateRate);
}
+DecodeTrace::~DecodeTrace()
+{
+ GlobalSettings::remove_change_handler(this);
+}
+
bool DecodeTrace::enabled() const
{
return true;
sample_range.second = min((int64_t)sample_range.second,
decode_signal_->get_decoded_sample_count(current_segment_, false));
- const vector<Row> rows = decode_signal_->visible_rows();
+ const vector<Row> rows = decode_signal_->get_rows(!always_show_all_rows_);
visible_rows_.clear();
for (const Row& row : rows) {
// Cache the row title widths
int row_title_width;
- try {
- row_title_width = row_title_widths_.at(row);
- } catch (out_of_range&) {
+ auto cached_width = row_title_widths_.find(row);
+
+ if (cached_width != row_title_widths_.end())
+ row_title_width = cached_width->second;
+ else {
const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
RowTitleMargin;
row_title_widths_[row] = w;
vector<Annotation> annotations;
decode_signal_->get_annotation_subset(annotations, row,
current_segment_, sample_range.first, sample_range.second);
- if (!annotations.empty()) {
+ if (always_show_all_rows_ || !annotations.empty()) {
draw_annotations(annotations, p, annotation_height, pp, y,
get_row_color(row.index()), row_title_width);
y += row_height_;
paint_hover_marker(p);
}
+void DecodeTrace::update_stack_button()
+{
+ const vector< shared_ptr<data::decode::Decoder> > &stack = decode_signal_->decoder_stack();
+
+ // Only show decoders in the menu that can be stacked onto the last one in the stack
+ if (!stack.empty()) {
+ const srd_decoder* d = stack.back()->decoder();
+
+ if (d->outputs) {
+ pv::widgets::DecoderMenu *const decoder_menu =
+ new pv::widgets::DecoderMenu(stack_button_, (const char*)(d->outputs->data));
+ connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
+ this, SLOT(on_stack_decoder(srd_decoder*)));
+
+ decoder_menu->setStyleSheet("QMenu { menu-scrollable: 1; }");
+
+ stack_button_->setMenu(decoder_menu);
+ stack_button_->show();
+ return;
+ }
+ }
+
+ // No decoders available for stacking
+ stack_button_->setMenu(nullptr);
+ stack_button_->hide();
+}
+
void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
{
using pv::data::decode::Decoder;
}
// Add stacking button
- pv::widgets::DecoderMenu *const decoder_menu =
- new pv::widgets::DecoderMenu(parent);
- connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
- this, SLOT(on_stack_decoder(srd_decoder*)));
-
- QPushButton *const stack_button =
- new QPushButton(tr("Stack Decoder"), parent);
- stack_button->setMenu(decoder_menu);
- stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
+ stack_button_ = new QPushButton(tr("Stack Decoder"), parent);
+ stack_button_->setToolTip(tr("Stack a higher-level decoder on top of this one"));
+ update_stack_button();
QHBoxLayout *stack_button_box = new QHBoxLayout;
- stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
+ stack_button_box->addWidget(stack_button_, 0, Qt::AlignRight);
form->addRow(stack_button_box);
}
QMenu* default_menu = Trace::create_view_context_menu(parent, click_pos);
if (default_menu) {
- for (QAction *action : default_menu->actions()) {
+ for (QAction *action : default_menu->actions()) { // clazy:exclude=range-loop
menu->addAction(action);
if (action->parent() == default_menu)
action->setParent(menu);
selected_row_ = nullptr;
}
+ const View *const view = owner_->view();
+ assert(view);
+ QPoint pos = view->viewport()->mapFrom(parent, click_pos);
+
// Default sample range is "from here"
- const pair<uint64_t, uint64_t> sample_range =
- get_view_sample_range(click_pos.x(), click_pos.x() + 1);
+ const pair<uint64_t, uint64_t> sample_range = get_view_sample_range(pos.x(), pos.x() + 1);
selected_sample_range_ = make_pair(sample_range.first, numeric_limits<uint64_t>::max());
if (decode_signal_->is_paused()) {
menu->addAction(pause);
}
+ QAction *const copy_annotation_to_clipboard =
+ new QAction(tr("Copy annotation text to clipboard"), this);
+ copy_annotation_to_clipboard->setIcon(QIcon::fromTheme("edit-paste",
+ QIcon(":/icons/edit-paste.svg")));
+ connect(copy_annotation_to_clipboard, SIGNAL(triggered()), this, SLOT(on_copy_annotation_to_clipboard()));
+ menu->addAction(copy_annotation_to_clipboard);
+
menu->addSeparator();
QAction *const export_all_rows =
connect(export_row_with_cursor, SIGNAL(triggered()), this, SLOT(on_export_row_with_cursor()));
menu->addAction(export_row_with_cursor);
- const View *view = owner_->view();
- assert(view);
-
if (!view->cursors()->enabled()) {
export_all_rows_with_cursor->setEnabled(false);
export_row_with_cursor->setEnabled(false);
QString ann = get_annotation_at_point(hp);
- assert(view);
-
if (!row_height_ || ann.isEmpty()) {
QToolTip::hideText();
return;
const vector<DecodeChannel> channels = decode_signal_->get_channels();
// Add the channels
- for (DecodeChannel ch : channels) {
+ for (const DecodeChannel& ch : channels) {
// Ignore channels not part of the decoder we create the form for
if (ch.decoder_ != dec)
continue;
}
QMessageBox msg(owner_->view());
- msg.setText(tr("Error"));
- msg.setInformativeText(tr("File %1 could not be written to.").arg(file_name));
+ msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name));
msg.setStandardButtons(QMessageBox::Ok);
msg.setIcon(QMessageBox::Warning);
msg.exec();
}
+void DecodeTrace::on_setting_changed(const QString &key, const QVariant &value)
+{
+ if (key == GlobalSettings::Key_Dec_AlwaysShowAllRows) {
+ visible_rows_.clear();
+ max_visible_rows_ = 0;
+ always_show_all_rows_ = value.toBool();
+ }
+}
+
void DecodeTrace::on_new_annotations()
{
if (!delayed_trace_updater_.isActive())
owner_->row_item_appearance_changed(false, true);
}
+void DecodeTrace::on_copy_annotation_to_clipboard()
+{
+ using namespace pv::data::decode;
+
+ if (!selected_row_)
+ return;
+
+ vector<Annotation> *annotations = new vector<Annotation>();
+
+ 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);
+
+ delete annotations;
+}
+
void DecodeTrace::on_export_row()
{
selected_sample_range_ = make_pair(0, numeric_limits<uint64_t>::max());