return *this;
}
+const RowData* Annotation::row_data() const
+{
+ return data_;
+}
+
const Row* Annotation::row() const
{
return data_->row();
return QString(ann_class->name);
}
+const QString Annotation::ann_class_description() const
+{
+ const AnnotationClass* ann_class =
+ data_->row()->decoder()->get_ann_class_by_id(ann_class_id_);
+
+ return QString(ann_class->description);
+}
+
const vector<QString>* Annotation::annotations() const
{
return texts_;
Annotation(Annotation&& a);
Annotation& operator=(Annotation&& a);
+ const RowData* row_data() const;
const Row* row() const;
uint64_t start_sample() const;
Class ann_class_id() const;
const QString ann_class_name() const;
+ const QString ann_class_description() const;
const vector<QString>* annotations() const;
const QString longest_annotation() const;
}
}
+const deque<Annotation>& RowData::annotations() const
+{
+ return annotations_;
+}
+
const Annotation* RowData::emplace_annotation(srd_proto_data *pdata)
{
const srd_proto_data_annotation *const pda = (const srd_proto_data_annotation*)pdata->data;
Annotation::Class ann_class_id = (Annotation::Class)(pda->ann_class);
- // Look up the longest annotation text to see if we have it in storage
+ // Look up the longest annotation text to see if we have it in storage.
+ // This implies that if the longest text is the same, the shorter texts
+ // are expected to be the same, too. PDs that violate this assumption
+ // should be considered broken.
const char* const* ann_texts = (char**)pda->ann_text;
const QString ann0 = QString::fromUtf8(ann_texts[0]);
vector<QString>* storage_entry = &(ann_texts_[ann0]);
void get_annotation_subset(deque<const pv::data::decode::Annotation*> &dest,
uint64_t start_sample, uint64_t end_sample) const;
+ const deque<Annotation>& annotations() const;
+
const Annotation* emplace_annotation(srd_proto_data *pdata);
private:
deque<const Annotation*>& all_annotations =
ds->segments_[ds->current_segment_id_].all_annotations;
all_annotations.emplace_back(ann);
+
+ // When emplace_annotation() inserts instead of appends an annotation,
+ // the pointers in all_annotations that follow the inserted annotation and
+ // point to annotations for this row are off by one and must be updated
+ if (&(row_data.annotations().back()) != ann) {
+ // Search backwards until we find the annotation we just added
+ auto row_it = row_data.annotations().end();
+ auto all_it = all_annotations.end();
+ do {
+ all_it--;
+ if ((*all_it)->row_data() == &row_data)
+ row_it--;
+ } while (&(*row_it) != ann);
+
+ // Update the annotation addresses for this row's annotations until the end
+ do {
+ if ((*all_it)->row_data() == &row_data) {
+ *all_it = &(*row_it);
+ row_it++;
+ }
+ all_it++;
+ } while (all_it != all_annotations.end());
+ }
}
void DecodeSignal::binary_callback(srd_proto_data *pdata, void *decode_signal)
prev_segment_(0),
prev_last_row_(0)
{
+ GlobalSettings::add_change_handler(this);
+ theme_is_dark_ = GlobalSettings::current_theme_is_dark();
+
// TBD Maybe use empty columns as indentation levels to indicate stacked decoders
- header_data_.emplace_back(tr("Start Sample")); // Column #0
- header_data_.emplace_back(tr("Start Time")); // Column #1
- header_data_.emplace_back(tr("Ann Row Name")); // Column #2
- header_data_.emplace_back(tr("Ann Class Name")); // Column #3
- header_data_.emplace_back(tr("Value")); // Column #4
+ header_data_.emplace_back(tr("Sample")); // Column #0
+ header_data_.emplace_back(tr("Time")); // Column #1
+ header_data_.emplace_back(tr("Decoder")); // Column #2
+ header_data_.emplace_back(tr("Ann Row")); // Column #3
+ header_data_.emplace_back(tr("Ann Class")); // Column #4
+ header_data_.emplace_back(tr("Value")); // Column #5
}
QVariant AnnotationCollectionModel::data(const QModelIndex& index, int role) const
if (!index.isValid())
return QVariant();
- if (role == Qt::DisplayRole) {
- const Annotation* ann =
- static_cast<const Annotation*>(index.internalPointer());
+ const Annotation* ann =
+ static_cast<const Annotation*>(index.internalPointer());
+ if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0: return QVariant((qulonglong)ann->start_sample()); // Column #0, Start Sample
case 1: return QVariant(0/*(qulonglong)ann->start_sample()*/); // Column #1, Start Time
- case 2: return QVariant(ann->row()->title()); // Column #2, Ann Row Name
- case 3: return QVariant(ann->ann_class_name()); // Column #3, Ann Class Name
- case 4: return QVariant(ann->longest_annotation()); // Column #4, Value
+ case 2: return QVariant(ann->row()->decoder()->name()); // Column #2, Decoder
+ case 3: return QVariant(ann->row()->description()); // Column #3, Ann Row
+ case 4: return QVariant(ann->ann_class_description()); // Column #4, Ann Class
+ case 5: return QVariant(ann->longest_annotation()); // Column #5, Value
default: return QVariant();
}
}
+ if (role == Qt::BackgroundRole) {
+ if (theme_is_dark_)
+ return QBrush(ann->dark_color());
+ else
+ return QBrush(ann->bright_color());
+ }
+
return QVariant();
}
void AnnotationCollectionModel::set_signal_and_segment(data::DecodeSignal* signal, uint32_t current_segment)
{
+ if (!signal) {
+ all_annotations_ = nullptr;
+ dataChanged(QModelIndex(), QModelIndex());
+ layoutChanged();
+ return;
+ }
+
all_annotations_ = signal->get_all_annotations_by_segment(current_segment);
if (!all_annotations_ || all_annotations_->empty()) {
prev_last_row_ = new_row_count;
}
+void AnnotationCollectionModel::on_setting_changed(const QString &key, const QVariant &value)
+{
+ (void)key;
+ (void)value;
+
+ // We don't really care about the actual setting, we just update the
+ // flag that indicates whether we are using a bright or dark color theme
+ theme_is_dark_ = GlobalSettings::current_theme_is_dark();
+}
+
} // namespace tabular_decoder
} // namespace views
} // namespace pv
int width = 0;
for (int i = 0; i < horizontalHeader()->count(); i++)
if (!horizontalHeader()->isSectionHidden(i))
- width += horizontalHeader()->sectionSizeHint(i);
+ width += horizontalHeader()->sectionSize(i);
size.setWidth(width + (horizontalHeader()->count() * 1));
const int font_height = QFontMetrics(QApplication::font()).height();
table_view_->verticalHeader()->setDefaultSectionSize((font_height * 5) / 4);
- table_view_->horizontalHeader()->setSectionResizeMode(model_->columnCount() - 1, QHeaderView::Stretch);
+ table_view_->horizontalHeader()->setStretchLastSection(true);
+ table_view_->horizontalHeader()->setCascadingSectionResizes(true);
+ table_view_->horizontalHeader()->setSectionsMovable(true);
table_view_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
parent->setSizePolicy(table_view_->sizePolicy());
connect(signal.get(), SIGNAL(name_changed(const QString&)),
this, SLOT(on_signal_name_changed(const QString&)));
+
+ // Note: At time of initial creation, decode signals have no decoders so we
+ // need to watch for decoder stacking events
+
connect(signal.get(), SIGNAL(decoder_stacked(void*)),
this, SLOT(on_decoder_stacked(void*)));
connect(signal.get(), SIGNAL(decoder_removed(void*)),
this, SLOT(on_decoder_removed(void*)));
- // Add the top-level decoder provided by this signal
+ // Add the top-level decoder provided by an already-existing signal
auto stack = signal->decoder_stack();
if (!stack.empty()) {
shared_ptr<Decoder>& dec = stack.at(0);
void View::on_selected_decoder_changed(int index)
{
if (signal_) {
+ disconnect(signal_, SIGNAL(signal_color_changed()));
disconnect(signal_, SIGNAL(new_annotations()));
disconnect(signal_, SIGNAL(decode_reset()));
}
signal_ = ds.get();
if (signal_) {
+ connect(signal_, SIGNAL(color_changed(QColor)), this, SLOT(on_signal_color_changed(QColor)));
connect(signal_, SIGNAL(new_annotations()), this, SLOT(on_new_annotations()));
connect(signal_, SIGNAL(decode_reset()), this, SLOT(on_decoder_reset()));
}
}
}
+void View::on_signal_color_changed(const QColor &color)
+{
+ (void)color;
+
+ table_view_->update();
+}
+
void View::on_new_annotations()
{
if (!delayed_view_updater_.isActive())
assert(signal);
- if (signal == signal_)
- update_data();
+ const shared_ptr<Decoder>& dec = signal->decoder_stack().at(0);
+ int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+ if (index == -1) {
+ // Add the decoder to the list
+ QString title = QString("%1 (%2)").arg(signal->name(), d->name());
+ decoder_selector_->addItem(title, QVariant::fromValue((void*)d));
+ }
}
void View::on_decoder_removed(void* decoder)
#include <QTableView>
#include <QToolButton>
+#include "pv/globalsettings.hpp"
#include "pv/views/viewbase.hpp"
#include "pv/data/decodesignal.hpp"
namespace tabular_decoder {
-class AnnotationCollectionModel : public QAbstractTableModel
+class AnnotationCollectionModel : public QAbstractTableModel, public GlobalSettingsInterface
{
Q_OBJECT
void set_signal_and_segment(data::DecodeSignal* signal, uint32_t current_segment);
+ void on_setting_changed(const QString &key, const QVariant &value) override;
+
private:
vector<QVariant> header_data_;
const deque<const Annotation*>* all_annotations_;
uint32_t prev_segment_;
uint64_t prev_last_row_;
+ bool theme_is_dark_;
};
private Q_SLOTS:
void on_selected_decoder_changed(int index);
void on_signal_name_changed(const QString &name);
+ void on_signal_color_changed(const QColor &color);
void on_new_annotations();
void on_decoder_reset();
{
for (DecodeTraceRow& r : rows_)
r.decode_row->set_base_color(color);
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
}
void DecodeTrace::on_new_annotations()