]> sigrok.org Git - pulseview.git/commitdiff
Add tabular decoder view
authorSoeren Apel <redacted>
Tue, 7 Apr 2020 19:12:58 +0000 (21:12 +0200)
committerUwe Hermann <redacted>
Sun, 3 May 2020 15:20:55 +0000 (17:20 +0200)
CMakeLists.txt
pv/mainwindow.cpp
pv/views/decoder_binary/view.cpp
pv/views/tabular_decoder/item.cpp [new file with mode: 0644]
pv/views/tabular_decoder/model.cpp [new file with mode: 0644]
pv/views/tabular_decoder/view.cpp [new file with mode: 0644]
pv/views/tabular_decoder/view.hpp [new file with mode: 0644]
pv/views/viewbase.cpp
pv/views/viewbase.hpp
test/CMakeLists.txt

index e157e7dbe934cc96c3f75a4b9b94d6cf94d7a3a3..f429553ebba2c26edec055f013e50daa1eba3962 100644 (file)
@@ -399,6 +399,9 @@ if(ENABLE_DECODE)
                pv/subwindows/decoder_selector/subwindow.cpp
                pv/views/decoder_binary/view.cpp
                pv/views/decoder_binary/QHexView.cpp
                pv/subwindows/decoder_selector/subwindow.cpp
                pv/views/decoder_binary/view.cpp
                pv/views/decoder_binary/QHexView.cpp
+               pv/views/tabular_decoder/item.cpp
+               pv/views/tabular_decoder/model.cpp
+               pv/views/tabular_decoder/view.cpp
                pv/views/trace/decodetrace.cpp
                pv/widgets/decodergroupbox.cpp
                pv/widgets/decodermenu.cpp
                pv/views/trace/decodetrace.cpp
                pv/widgets/decodergroupbox.cpp
                pv/widgets/decodermenu.cpp
@@ -409,6 +412,7 @@ if(ENABLE_DECODE)
                pv/subwindows/decoder_selector/subwindow.hpp
                pv/views/decoder_binary/view.hpp
                pv/views/decoder_binary/QHexView.hpp
                pv/subwindows/decoder_selector/subwindow.hpp
                pv/views/decoder_binary/view.hpp
                pv/views/decoder_binary/QHexView.hpp
+               pv/views/tabular_decoder/view.hpp
                pv/views/trace/decodetrace.hpp
                pv/widgets/decodergroupbox.hpp
                pv/widgets/decodermenu.hpp
                pv/views/trace/decodetrace.hpp
                pv/widgets/decodergroupbox.hpp
                pv/widgets/decodermenu.hpp
index ea9f01af7fd13f6ca056ef7dfe37333b158cbb9d..6758d77df88a1854141964ab54e1b80a9be1ad4a 100644 (file)
@@ -53,6 +53,7 @@
 #ifdef ENABLE_DECODE
 #include "subwindows/decoder_selector/subwindow.hpp"
 #include "views/decoder_binary/view.hpp"
 #ifdef ENABLE_DECODE
 #include "subwindows/decoder_selector/subwindow.hpp"
 #include "views/decoder_binary/view.hpp"
+#include "views/tabular_decoder/view.hpp"
 #endif
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
 #endif
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
@@ -164,6 +165,8 @@ shared_ptr<views::ViewBase> MainWindow::add_view(views::ViewType type,
 #ifdef ENABLE_DECODE
        if (type == views::ViewTypeDecoderBinary)
                v = make_shared<views::decoder_binary::View>(session, false, dock_main);
 #ifdef ENABLE_DECODE
        if (type == views::ViewTypeDecoderBinary)
                v = make_shared<views::decoder_binary::View>(session, false, dock_main);
+       if (type == views::ViewTypeTabularDecoder)
+               v = make_shared<views::tabular_decoder::View>(session, false, dock_main);
 #endif
 
        if (!v)
 #endif
 
        if (!v)
index 5f55b19104d9449e12fc4556861c7546f04be46c..797321fc2a67f7665275bf3316fb1a44097dde93 100644 (file)
@@ -206,14 +206,14 @@ void View::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
 
 void View::save_settings(QSettings &settings) const
 {
 
 void View::save_settings(QSettings &settings) const
 {
-       (void)settings;
+       ViewBase::save_settings(settings);
 }
 
 void View::restore_settings(QSettings &settings)
 {
        // Note: It is assumed that this function is only called once,
        // immediately after restoring a previous session.
 }
 
 void View::restore_settings(QSettings &settings)
 {
        // Note: It is assumed that this function is only called once,
        // immediately after restoring a previous session.
-       (void)settings;
+       ViewBase::restore_settings(settings);
 }
 
 void View::reset_data()
 }
 
 void View::reset_data()
diff --git a/pv/views/tabular_decoder/item.cpp b/pv/views/tabular_decoder/item.cpp
new file mode 100644 (file)
index 0000000..a4b79f3
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pv/views/tabular_decoder/view.hpp"
+
+using std::out_of_range;
+
+namespace pv {
+namespace views {
+namespace tabular_decoder {
+
+AnnotationCollectionItem::AnnotationCollectionItem(const vector<QVariant>& data,
+       shared_ptr<AnnotationCollectionItem> parent) :
+       data_(data),
+       parent_(parent)
+{
+}
+
+void AnnotationCollectionItem::appendSubItem(shared_ptr<AnnotationCollectionItem> item)
+{
+       subItems_.push_back(item);
+}
+
+shared_ptr<AnnotationCollectionItem> AnnotationCollectionItem::subItem(int row) const
+{
+       try {
+               return subItems_.at(row);
+       } catch (out_of_range&) {
+               return nullptr;
+       }
+}
+
+shared_ptr<AnnotationCollectionItem> AnnotationCollectionItem::parent() const
+{
+       return parent_;
+}
+
+shared_ptr<AnnotationCollectionItem> AnnotationCollectionItem::findSubItem(
+       const QVariant& value, int column)
+{
+       for (shared_ptr<AnnotationCollectionItem> item : subItems_)
+               if (item->data(column) == value)
+                       return item;
+
+       return nullptr;
+}
+
+int AnnotationCollectionItem::subItemCount() const
+{
+       return subItems_.size();
+}
+
+int AnnotationCollectionItem::columnCount() const
+{
+       return data_.size();
+}
+
+int AnnotationCollectionItem::row() const
+{
+       if (parent_)
+               for (size_t i = 0; i < parent_->subItems_.size(); i++)
+                       if (parent_->subItems_.at(i).get() == const_cast<AnnotationCollectionItem*>(this))
+                               return i;
+
+       return 0;
+}
+
+QVariant AnnotationCollectionItem::data(int column) const
+{
+       try {
+               return data_.at(column);
+       } catch (out_of_range&) {
+               return QVariant();
+       }
+}
+
+} // namespace tabular_decoder
+} // namespace views
+} // namespace pv
diff --git a/pv/views/tabular_decoder/model.cpp b/pv/views/tabular_decoder/model.cpp
new file mode 100644 (file)
index 0000000..35cdc95
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QString>
+
+#include "pv/views/tabular_decoder/view.hpp"
+
+using std::make_shared;
+
+namespace pv {
+namespace views {
+namespace tabular_decoder {
+
+AnnotationCollectionModel::AnnotationCollectionModel(QObject* parent) :
+       QAbstractItemModel(parent)
+{
+       vector<QVariant> header_data;
+       header_data.emplace_back(tr("ID"));                // Column #0
+       header_data.emplace_back(tr("Start Time"));        // Column #1
+       header_data.emplace_back(tr("End Time"));          // Column #2
+       header_data.emplace_back(tr("Ann Row Name"));      // Column #3
+       header_data.emplace_back(tr("Class Row Name"));    // Column #4
+       header_data.emplace_back(tr("Value"));             // Column #5
+       root_ = make_shared<AnnotationCollectionItem>(header_data);
+}
+
+QVariant AnnotationCollectionModel::data(const QModelIndex& index, int role) const
+{
+       if (!index.isValid())
+               return QVariant();
+
+       if (role == Qt::DisplayRole)
+       {
+               AnnotationCollectionItem* item =
+                       static_cast<AnnotationCollectionItem*>(index.internalPointer());
+
+               return item->data(index.column());
+       }
+
+       if ((role == Qt::FontRole) && (index.parent().isValid()) && (index.column() == 0))
+       {
+               QFont font;
+               font.setItalic(true);
+               return QVariant(font);
+       }
+
+       return QVariant();
+}
+
+Qt::ItemFlags AnnotationCollectionModel::flags(const QModelIndex& index) const
+{
+       if (!index.isValid())
+               return nullptr;
+
+       return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+QVariant AnnotationCollectionModel::headerData(int section, Qt::Orientation orientation,
+       int role) const
+{
+       if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+               return root_->data(section);
+
+       return QVariant();
+}
+
+QModelIndex AnnotationCollectionModel::index(int row, int column,
+       const QModelIndex& parent_idx) const
+{
+       if (!hasIndex(row, column, parent_idx))
+               return QModelIndex();
+
+       AnnotationCollectionItem* parent = root_.get();
+
+       if (parent_idx.isValid())
+               parent = static_cast<AnnotationCollectionItem*>(parent_idx.internalPointer());
+
+       AnnotationCollectionItem* subItem = parent->subItem(row).get();
+
+       return subItem ? createIndex(row, column, subItem) : QModelIndex();
+}
+
+QModelIndex AnnotationCollectionModel::parent(const QModelIndex& index) const
+{
+       if (!index.isValid())
+               return QModelIndex();
+
+       AnnotationCollectionItem* subItem =
+               static_cast<AnnotationCollectionItem*>(index.internalPointer());
+
+       shared_ptr<AnnotationCollectionItem> parent = subItem->parent();
+
+       return (parent == root_) ? QModelIndex() :
+               createIndex(parent->row(), 0, parent.get());
+}
+
+int AnnotationCollectionModel::rowCount(const QModelIndex& parent_idx) const
+{
+       AnnotationCollectionItem* parent = root_.get();
+
+       if (parent_idx.column() > 0)
+               return 0;
+
+       if (parent_idx.isValid())
+               parent = static_cast<AnnotationCollectionItem*>(parent_idx.internalPointer());
+
+       return parent->subItemCount();
+}
+
+int AnnotationCollectionModel::columnCount(const QModelIndex& parent_idx) const
+{
+       if (parent_idx.isValid())
+               return static_cast<AnnotationCollectionItem*>(
+                       parent_idx.internalPointer())->columnCount();
+       else
+               return root_->columnCount();
+}
+
+
+} // namespace tabular_decoder
+} // namespace views
+} // namespace pv
diff --git a/pv/views/tabular_decoder/view.cpp b/pv/views/tabular_decoder/view.cpp
new file mode 100644 (file)
index 0000000..8c5859e
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <climits>
+
+#include <QDebug>
+#include <QFileDialog>
+#include <QLabel>
+#include <QMenu>
+#include <QMessageBox>
+#include <QToolBar>
+#include <QVBoxLayout>
+
+#include <libsigrokdecode/libsigrokdecode.h>
+
+#include "view.hpp"
+
+#include "pv/globalsettings.hpp"
+#include "pv/util.hpp"
+#include "pv/data/decode/decoder.hpp"
+
+using pv::data::DecodeSignal;
+using pv::data::SignalBase;
+using pv::data::decode::Decoder;
+using pv::util::Timestamp;
+
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace tabular_decoder {
+
+
+View::View(Session &session, bool is_main_view, QMainWindow *parent) :
+       ViewBase(session, is_main_view, parent),
+
+       // Note: Place defaults in View::reset_view_state(), not here
+       parent_(parent),
+       decoder_selector_(new QComboBox()),
+       save_button_(new QToolButton()),
+       save_action_(new QAction(this)),
+       table_view_(new QTableView()),
+       model_(new AnnotationCollectionModel()),
+       signal_(nullptr)
+{
+       QVBoxLayout *root_layout = new QVBoxLayout(this);
+       root_layout->setContentsMargins(0, 0, 0, 0);
+       root_layout->addWidget(table_view_);
+
+       // Create toolbar
+       QToolBar* toolbar = new QToolBar();
+       toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
+       parent->addToolBar(toolbar);
+
+       // Populate toolbar
+       toolbar->addWidget(new QLabel(tr("Decoder:")));
+       toolbar->addWidget(decoder_selector_);
+       toolbar->addSeparator();
+       toolbar->addWidget(save_button_);
+
+       connect(decoder_selector_, SIGNAL(currentIndexChanged(int)),
+               this, SLOT(on_selected_decoder_changed(int)));
+
+       // Configure widgets
+       decoder_selector_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+       // Configure actions
+       save_action_->setText(tr("&Save..."));
+       save_action_->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+       save_action_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+       connect(save_action_, SIGNAL(triggered(bool)),
+               this, SLOT(on_actionSave_triggered()));
+
+       QMenu *save_menu = new QMenu();
+       connect(save_menu, SIGNAL(triggered(QAction*)),
+               this, SLOT(on_actionSave_triggered(QAction*)));
+
+       save_button_->setMenu(save_menu);
+       save_button_->setDefaultAction(save_action_);
+       save_button_->setPopupMode(QToolButton::MenuButtonPopup);
+
+       // Set up the table view
+       table_view_->setModel(model_);
+       table_view_->setSortingEnabled(true);
+       table_view_->sortByColumn(0, Qt::AscendingOrder);
+
+       reset_view_state();
+}
+
+ViewType View::get_type() const
+{
+       return ViewTypeTabularDecoder;
+}
+
+void View::reset_view_state()
+{
+       ViewBase::reset_view_state();
+
+       decoder_selector_->clear();
+}
+
+void View::clear_decode_signals()
+{
+       ViewBase::clear_decode_signals();
+
+       reset_data();
+       reset_view_state();
+}
+
+void View::add_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+       ViewBase::add_decode_signal(signal);
+
+       connect(signal.get(), SIGNAL(name_changed(const QString&)),
+               this, SLOT(on_signal_name_changed(const QString&)));
+       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 all decoders provided by this signal
+       auto stack = signal->decoder_stack();
+       if (stack.size() > 1) {
+               for (const shared_ptr<Decoder>& dec : stack) {
+                       QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
+                       decoder_selector_->addItem(title, QVariant::fromValue((void*)dec.get()));
+               }
+       } else
+               if (!stack.empty()) {
+                       shared_ptr<Decoder>& dec = stack.at(0);
+                       decoder_selector_->addItem(signal->name(), QVariant::fromValue((void*)dec.get()));
+               }
+}
+
+void View::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+       // Remove all decoders provided by this signal
+       for (const shared_ptr<Decoder>& dec : signal->decoder_stack()) {
+               int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+               if (index != -1)
+                       decoder_selector_->removeItem(index);
+       }
+
+       ViewBase::remove_decode_signal(signal);
+
+       if (signal.get() == signal_) {
+               reset_data();
+               update_data();
+               reset_view_state();
+       }
+}
+
+void View::save_settings(QSettings &settings) const
+{
+       ViewBase::save_settings(settings);
+}
+
+void View::restore_settings(QSettings &settings)
+{
+       // Note: It is assumed that this function is only called once,
+       // immediately after restoring a previous session.
+       ViewBase::restore_settings(settings);
+}
+
+void View::reset_data()
+{
+       signal_ = nullptr;
+       decoder_ = nullptr;
+}
+
+void View::update_data()
+{
+       if (!signal_)
+               return;
+
+       // TBD
+}
+
+void View::save_data() const
+{
+       assert(decoder_);
+       assert(signal_);
+
+       if (!signal_)
+               return;
+
+/*     GlobalSettings settings;
+       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
+
+       const QString file_name = QFileDialog::getSaveFileName(
+               parent_, tr("Save Binary Data"), dir, tr("Binary Data Files (*.bin);;All Files (*)"));
+
+       if (file_name.isEmpty())
+               return;
+
+       QFile file(file_name);
+       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+               pair<size_t, size_t> selection = hex_view_->get_selection();
+
+               vector<uint8_t> data;
+               data.resize(selection.second - selection.first + 1);
+
+               signal_->get_merged_binary_data_chunks_by_offset(current_segment_, decoder_,
+                       bin_class_id_, selection.first, selection.second, &data);
+
+               int64_t bytes_written = file.write((const char*)data.data(), data.size());
+
+               if ((bytes_written == -1) || ((uint64_t)bytes_written != data.size())) {
+                       QMessageBox msg(parent_);
+                       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();
+                       return;
+               }
+       } */
+}
+
+void View::on_selected_decoder_changed(int index)
+{
+       if (signal_)
+               disconnect(signal_, SIGNAL(new_annotations()));
+
+       reset_data();
+
+       decoder_ = (Decoder*)decoder_selector_->itemData(index).value<void*>();
+
+       // Find the signal that contains the selected decoder
+       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
+               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
+                       if (decoder_ == dec.get())
+                               signal_ = ds.get();
+
+       if (signal_) {
+               connect(signal_, SIGNAL(new_annotations()), this, SLOT(on_new_annotations()));
+       }
+
+       update_data();
+}
+
+void View::on_signal_name_changed(const QString &name)
+{
+       (void)name;
+
+       SignalBase* sb = qobject_cast<SignalBase*>(QObject::sender());
+       assert(sb);
+
+       DecodeSignal* signal = dynamic_cast<DecodeSignal*>(sb);
+       assert(signal);
+
+       // Update all decoder entries provided by this signal
+       auto stack = signal->decoder_stack();
+       if (stack.size() > 1) {
+               for (const shared_ptr<Decoder>& dec : stack) {
+                       QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
+                       int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+                       if (index != -1)
+                               decoder_selector_->setItemText(index, title);
+               }
+       } else
+               if (!stack.empty()) {
+                       shared_ptr<Decoder>& dec = stack.at(0);
+                       int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+                       if (index != -1)
+                               decoder_selector_->setItemText(index, signal->name());
+               }
+}
+
+void View::on_new_annotations()
+{
+       if (!delayed_view_updater_.isActive())
+               delayed_view_updater_.start();
+}
+
+void View::on_decoder_stacked(void* decoder)
+{
+       // TODO This doesn't change existing entries for the same signal - but it should as the naming scheme may change
+
+       Decoder* d = static_cast<Decoder*>(decoder);
+
+       // Find the signal that contains the selected decoder
+       DecodeSignal* signal = nullptr;
+
+       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
+               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
+                       if (d == dec.get())
+                               signal = ds.get();
+
+       assert(signal);
+
+       // 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)
+{
+       Decoder* d = static_cast<Decoder*>(decoder);
+
+       // Remove the decoder from the list
+       int index = decoder_selector_->findData(QVariant::fromValue((void*)d));
+
+       if (index != -1)
+               decoder_selector_->removeItem(index);
+}
+
+void View::on_actionSave_triggered(QAction* action)
+{
+       (void)action;
+
+       save_data();
+}
+
+void View::perform_delayed_view_update()
+{
+       update_data();
+}
+
+
+} // namespace tabular_decoder
+} // namespace views
+} // namespace pv
diff --git a/pv/views/tabular_decoder/view.hpp b/pv/views/tabular_decoder/view.hpp
new file mode 100644 (file)
index 0000000..83a4549
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TABULARDECODER_VIEW_HPP
+#define PULSEVIEW_PV_VIEWS_TABULARDECODER_VIEW_HPP
+
+#include <QAction>
+#include <QComboBox>
+#include <QTableView>
+#include <QToolButton>
+
+#include "pv/views/viewbase.hpp"
+#include "pv/data/decodesignal.hpp"
+
+namespace pv {
+class Session;
+
+namespace views {
+
+namespace tabular_decoder {
+
+class AnnotationCollectionItem
+{
+public:
+       AnnotationCollectionItem(const vector<QVariant>& data,
+               shared_ptr<AnnotationCollectionItem> parent = nullptr);
+
+       void appendSubItem(shared_ptr<AnnotationCollectionItem> item);
+
+       shared_ptr<AnnotationCollectionItem> subItem(int row) const;
+       shared_ptr<AnnotationCollectionItem> parent() const;
+       shared_ptr<AnnotationCollectionItem> findSubItem(const QVariant& value, int column);
+
+       int subItemCount() const;
+       int columnCount() const;
+       int row() const;
+       QVariant data(int column) const;
+
+private:
+       vector< shared_ptr<AnnotationCollectionItem> > subItems_;
+       vector<QVariant> data_;
+       shared_ptr<AnnotationCollectionItem> parent_;
+};
+
+
+class AnnotationCollectionModel : public QAbstractItemModel
+{
+       Q_OBJECT
+
+public:
+       AnnotationCollectionModel(QObject* parent = nullptr);
+
+       QVariant data(const QModelIndex& index, int role) const override;
+       Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+       QVariant headerData(int section, Qt::Orientation orientation,
+               int role = Qt::DisplayRole) const override;
+       QModelIndex index(int row, int column,
+               const QModelIndex& parent_idx = QModelIndex()) const override;
+
+       QModelIndex parent(const QModelIndex& index) const override;
+
+       int rowCount(const QModelIndex& parent_idx = QModelIndex()) const override;
+       int columnCount(const QModelIndex& parent_idx = QModelIndex()) const override;
+
+private:
+       shared_ptr<AnnotationCollectionItem> root_;
+};
+
+
+class View : public ViewBase
+{
+       Q_OBJECT
+
+public:
+       explicit View(Session &session, bool is_main_view=false, QMainWindow *parent = nullptr);
+
+       virtual ViewType get_type() const;
+
+       /**
+        * Resets the view to its default state after construction. It does however
+        * not reset the signal bases or any other connections with the session.
+        */
+       virtual void reset_view_state();
+
+       virtual void clear_decode_signals();
+       virtual void add_decode_signal(shared_ptr<data::DecodeSignal> signal);
+       virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
+
+       virtual void save_settings(QSettings &settings) const;
+       virtual void restore_settings(QSettings &settings);
+
+private:
+       void reset_data();
+       void update_data();
+
+       void save_data() const;
+
+private Q_SLOTS:
+       void on_selected_decoder_changed(int index);
+       void on_signal_name_changed(const QString &name);
+       void on_new_annotations();
+
+       void on_decoder_stacked(void* decoder);
+       void on_decoder_removed(void* decoder);
+
+       void on_actionSave_triggered(QAction* action = nullptr);
+
+       virtual void perform_delayed_view_update();
+
+private:
+       QWidget* parent_;
+
+       QComboBox *decoder_selector_;
+
+       QToolButton* save_button_;
+       QAction* save_action_;
+
+       QTableView* table_view_;
+
+       AnnotationCollectionModel* model_;
+
+       data::DecodeSignal *signal_;
+       const data::decode::Decoder *decoder_;
+};
+
+} // namespace tabular_decoder
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TABULARDECODER_VIEW_HPP
index 24e4bb9a8816b2dcf945907f297637b4d8e1ea07..9e5a2887c8af2cdcd3bf384810ed976858fdb214 100644 (file)
@@ -36,7 +36,8 @@ namespace views {
 const char* ViewTypeNames[ViewTypeCount] = {
        "Trace View",
 #ifdef ENABLE_DECODE
 const char* ViewTypeNames[ViewTypeCount] = {
        "Trace View",
 #ifdef ENABLE_DECODE
-       "Binary Decoder Output View"
+       "Binary Decoder Output View",
+       "Tabular Decoder Output View"
 #endif
 };
 
 #endif
 };
 
index 585dfa0c91ffd6e204c4cfe203e2de5846881834..e3a3c6a404925ee6d03f4a10fba7d89b89fb71b0 100644 (file)
@@ -56,6 +56,7 @@ enum ViewType {
        ViewTypeTrace,
 #ifdef ENABLE_DECODE
        ViewTypeDecoderBinary,
        ViewTypeTrace,
 #ifdef ENABLE_DECODE
        ViewTypeDecoderBinary,
+       ViewTypeTabularDecoder,
 #endif
        ViewTypeCount  // Indicates how many view types there are, must always be last
 };
 #endif
        ViewTypeCount  // Indicates how many view types there are, must always be last
 };
index e3362ac8ef961d1ce7da45568acef3510b8097a3..d800368b7ba8daa38d6051731fa676e4cfef899a 100644 (file)
@@ -176,6 +176,9 @@ if(ENABLE_DECODE)
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.cpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/view.cpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/QHexView.cpp
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.cpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/view.cpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/QHexView.cpp
+               ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/item.cpp
+               ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/model.cpp
+               ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/view.cpp
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp
@@ -186,6 +189,7 @@ if(ENABLE_DECODE)
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/view.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/QHexView.hpp
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/view.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/QHexView.hpp
+               ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/view.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.hpp