pv/prop/int.cpp
pv/prop/property.cpp
pv/prop/string.cpp
+ pv/subwindows/subwindowbase.cpp
pv/toolbars/mainbar.cpp
pv/views/trace/analogsignal.cpp
pv/views/trace/cursor.cpp
pv/prop/int.hpp
pv/prop/property.hpp
pv/prop/string.hpp
+ pv/subwindows/subwindowbase.hpp
pv/toolbars/mainbar.hpp
pv/views/trace/analogsignal.hpp
pv/views/trace/cursor.hpp
pv/data/decode/decoder.cpp
pv/data/decode/row.cpp
pv/data/decode/rowdata.cpp
+ pv/subwindows/decoder_selector/item.cpp
+ pv/subwindows/decoder_selector/model.cpp
+ pv/subwindows/decoder_selector/subwindow.cpp
pv/views/trace/decodetrace.cpp
pv/widgets/decodergroupbox.cpp
pv/widgets/decodermenu.cpp
list(APPEND pulseview_HEADERS
pv/data/decodesignal.hpp
+ pv/subwindows/decoder_selector/subwindow.hpp
pv/views/trace/decodetrace.hpp
pv/widgets/decodergroupbox.hpp
pv/widgets/decodermenu.hpp
#include "devices/hardwaredevice.hpp"
#include "dialogs/settings.hpp"
#include "globalsettings.hpp"
+#include "subwindows/decoder_selector/subwindow.hpp"
#include "toolbars/mainbar.hpp"
#include "util.hpp"
#include "views/trace/view.hpp"
{
GlobalSettings::remove_change_handler(this);
+ // Make sure we no longer hold any shared pointers to widgets after the
+ // destructor finishes (goes for sessions and sub windows alike)
+
while (!sessions_.empty())
remove_session(sessions_.front());
+
+ sub_windows_.clear();
}
void MainWindow::show_session_error(const QString text, const QString info_text)
connect(main_bar.get(), SIGNAL(new_view(Session*)),
this, SLOT(on_new_view(Session*)));
+ connect(main_bar.get(), SIGNAL(show_decoder_selector(Session*)),
+ this, SLOT(on_show_decoder_selector(Session*)));
main_bar->action_view_show_cursors()->setChecked(tv->cursors_shown());
}
}
+shared_ptr<subwindows::SubWindowBase> MainWindow::add_subwindow(
+ subwindows::SubWindowType type, Session &session)
+{
+ GlobalSettings settings;
+ shared_ptr<subwindows::SubWindowBase> v;
+
+ QMainWindow *main_window = nullptr;
+ for (auto entry : session_windows_)
+ if (entry.first.get() == &session)
+ main_window = entry.second;
+
+ assert(main_window);
+
+ QString title = "";
+
+ switch (type) {
+ case subwindows::SubWindowTypeDecoderSelector:
+ title = tr("Decoder Selector");
+ }
+
+ QDockWidget* dock = new QDockWidget(title, main_window);
+ dock->setObjectName(title);
+ main_window->addDockWidget(Qt::TopDockWidgetArea, dock);
+
+ // Insert a QMainWindow into the dock widget to allow for a tool bar
+ QMainWindow *dock_main = new QMainWindow(dock);
+ dock_main->setWindowFlags(Qt::Widget); // Remove Qt::Window flag
+
+ if (type == subwindows::SubWindowTypeDecoderSelector)
+ v = make_shared<subwindows::decoder_selector::SubWindow>(session, dock_main);
+
+ if (!v)
+ return nullptr;
+
+ sub_windows_[dock] = v;
+ dock_main->setCentralWidget(v.get());
+ dock->setWidget(dock_main);
+
+ dock->setContextMenuPolicy(Qt::PreventContextMenu);
+ dock->setFeatures(QDockWidget::DockWidgetMovable |
+ QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
+
+ QAbstractButton *close_btn =
+ dock->findChildren<QAbstractButton*>
+ ("qt_dockwidget_closebutton").front();
+
+ connect(close_btn, SIGNAL(clicked(bool)),
+ this, SLOT(on_sub_window_close_clicked()));
+
+ if (v->has_toolbar())
+ dock_main->addToolBar(v->create_toolbar(dock_main));
+
+ return v;
+}
+
shared_ptr<Session> MainWindow::add_session()
{
static int last_session_id = 1;
remove_session(session);
}
+void MainWindow::on_show_decoder_selector(Session *session)
+{
+ // Close dock widget if it's already showing and return
+ for (auto entry : sub_windows_) {
+ QDockWidget* dock = entry.first;
+ if (dynamic_pointer_cast<subwindows::decoder_selector::SubWindow>(entry.second)) {
+ sub_windows_.erase(dock);
+ dock->close();
+ return;
+ }
+ }
+
+ // We get a pointer and need a reference
+ for (shared_ptr<Session> s : sessions_)
+ if (s.get() == session)
+ add_subwindow(subwindows::SubWindowTypeDecoderSelector, *s);
+}
+
+void MainWindow::on_sub_window_close_clicked()
+{
+ // Find the dock widget that contains the close button that was clicked
+ QObject *w = QObject::sender();
+ QDockWidget *dock = nullptr;
+
+ while (w) {
+ dock = qobject_cast<QDockWidget*>(w);
+ if (dock)
+ break;
+ w = w->parent();
+ }
+
+ sub_windows_.erase(dock);
+ dock->close();
+}
+
void MainWindow::on_view_colored_bg_shortcut()
{
GlobalSettings settings;
#include "globalsettings.hpp"
#include "session.hpp"
+#include "subwindows/subwindowbase.hpp"
#include "views/viewbase.hpp"
using std::list;
void remove_view(shared_ptr<views::ViewBase> view);
+ shared_ptr<subwindows::SubWindowBase> add_subwindow(
+ subwindows::SubWindowType type, Session &session);
+
shared_ptr<Session> add_session();
void remove_session(shared_ptr<Session> session);
void on_tab_changed(int index);
void on_tab_close_requested(int index);
+ void on_show_decoder_selector(Session *session);
+ void on_sub_window_close_clicked();
+
void on_view_colored_bg_shortcut();
void on_view_sticky_scrolling_shortcut();
void on_view_show_sampling_points_shortcut();
shared_ptr<Session> last_focused_session_;
map< QDockWidget*, shared_ptr<views::ViewBase> > view_docks_;
+ map< QDockWidget*, shared_ptr<subwindows::SubWindowBase> > sub_windows_;
map< shared_ptr<Session>, QMainWindow*> session_windows_;
data_saved_ = true;
}
+#ifdef ENABLE_DECODE
+void Session::on_new_decoders_selected(vector<const srd_decoder*> decoders)
+{
+ assert(decoders.size() > 0);
+
+ shared_ptr<data::DecodeSignal> signal = add_decode_signal();
+
+ if (signal)
+ for (const srd_decoder* d : decoders)
+ signal->stack_decoder(d);
+}
+#endif
+
} // namespace pv
public Q_SLOTS:
void on_data_saved();
+#ifdef ENABLE_DECODE
+ void on_new_decoders_selected(vector<const srd_decoder*> decoders);
+#endif
+
private:
DeviceManager &device_manager_;
shared_ptr<devices::Device> device_;
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2018 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 "subwindow.hpp"
+
+using std::out_of_range;
+
+namespace pv {
+namespace subwindows {
+namespace decoder_selector {
+
+DecoderCollectionItem::DecoderCollectionItem(const vector<QVariant>& data,
+ shared_ptr<DecoderCollectionItem> parent) :
+ data_(data),
+ parent_(parent)
+{
+}
+
+void DecoderCollectionItem::appendSubItem(shared_ptr<DecoderCollectionItem> item)
+{
+ subItems_.push_back(item);
+}
+
+shared_ptr<DecoderCollectionItem> DecoderCollectionItem::subItem(int row) const
+{
+ try {
+ return subItems_.at(row);
+ } catch (out_of_range) {
+ return nullptr;
+ }
+}
+
+shared_ptr<DecoderCollectionItem> DecoderCollectionItem::parent() const
+{
+ return parent_;
+}
+
+shared_ptr<DecoderCollectionItem> DecoderCollectionItem::findSubItem(
+ const QVariant& value, int column)
+{
+ for (shared_ptr<DecoderCollectionItem> item : subItems_)
+ if (item->data(column) == value)
+ return item;
+
+ return nullptr;
+}
+
+int DecoderCollectionItem::subItemCount() const
+{
+ return subItems_.size();
+}
+
+int DecoderCollectionItem::columnCount() const
+{
+ return data_.size();
+}
+
+int DecoderCollectionItem::row() const
+{
+ if (parent_)
+ for (uint i = 0; i < parent_->subItems_.size(); i++)
+ if (parent_->subItems_.at(i).get() == const_cast<DecoderCollectionItem*>(this))
+ return i;
+
+ return 0;
+}
+
+QVariant DecoderCollectionItem::data(int column) const
+{
+ try {
+ return data_.at(column);
+ } catch (out_of_range) {
+ return QVariant();
+ }
+}
+
+} // namespace decoder_selector
+} // namespace subwindows
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2018 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 "subwindow.hpp"
+
+#include <libsigrokdecode/libsigrokdecode.h>
+
+using std::make_shared;
+
+namespace pv {
+namespace subwindows {
+namespace decoder_selector {
+
+DecoderCollectionModel::DecoderCollectionModel(QObject* parent) :
+ QAbstractItemModel(parent)
+{
+ vector<QVariant> header_data;
+ header_data.emplace_back(tr("Decoder")); // Column #0
+ header_data.emplace_back(tr("Name")); // Column #1
+ header_data.emplace_back(tr("ID")); // Column #2
+ root_ = make_shared<DecoderCollectionItem>(header_data);
+
+ // Note: the tag groups are sub-items of the root item
+
+ // Create "all decoders" group
+ vector<QVariant> item_data;
+ item_data.emplace_back(tr("All Decoders"));
+ // Add dummy entries to make the row count the same as the
+ // sub-item size, or else we can't query sub-item data
+ item_data.emplace_back();
+ item_data.emplace_back();
+ shared_ptr<DecoderCollectionItem> group_item_all =
+ make_shared<DecoderCollectionItem>(item_data, root_);
+ root_->appendSubItem(group_item_all);
+
+ GSList* l = g_slist_copy((GSList*)srd_decoder_list());
+ for (GSList* li = l; li; li = li->next) {
+ const srd_decoder *const d = (srd_decoder*)li->data;
+ assert(d);
+
+ const QString id = QString::fromUtf8(d->id);
+ const QString name = QString::fromUtf8(d->name);
+ const QString long_name = QString::fromUtf8(d->longname);
+
+ // Add decoder to the "all decoders" group
+ item_data.clear();
+ item_data.emplace_back(name);
+ item_data.emplace_back(long_name);
+ item_data.emplace_back(id);
+ shared_ptr<DecoderCollectionItem> decoder_item_all =
+ make_shared<DecoderCollectionItem>(item_data, group_item_all);
+ group_item_all->appendSubItem(decoder_item_all);
+
+ // Add decoder to all relevant groups using the tag information
+ GSList* t = g_slist_copy((GSList*)d->tags);
+ for (GSList* ti = t; ti; ti = ti->next) {
+ const QString tag = tr((char*)ti->data);
+ const QVariant tag_var = QVariant(tag);
+
+ // Find tag group and create it if it doesn't exist yet
+ shared_ptr<DecoderCollectionItem> group_item =
+ root_->findSubItem(tag_var, 0);
+
+ if (!group_item) {
+ item_data.clear();
+ item_data.emplace_back(tag);
+ // Add dummy entries to make the row count the same as the
+ // sub-item size, or else we can't query sub-item data
+ item_data.emplace_back();
+ item_data.emplace_back();
+ group_item = make_shared<DecoderCollectionItem>(item_data, root_);
+ root_->appendSubItem(group_item);
+ }
+
+ // Create decoder item
+ item_data.clear();
+ item_data.emplace_back(name);
+ item_data.emplace_back(long_name);
+ item_data.emplace_back(id);
+ shared_ptr<DecoderCollectionItem> decoder_item =
+ make_shared<DecoderCollectionItem>(item_data, group_item);
+
+ // Add decoder to tag group
+ group_item->appendSubItem(decoder_item);
+ }
+ g_slist_free(t);
+ }
+ g_slist_free(l);
+}
+
+QVariant DecoderCollectionModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ DecoderCollectionItem* item =
+ static_cast<DecoderCollectionItem*>(index.internalPointer());
+
+ return item->data(index.column());
+}
+
+Qt::ItemFlags DecoderCollectionModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return 0;
+
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+QVariant DecoderCollectionModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ return root_->data(section);
+
+ return QVariant();
+}
+
+QModelIndex DecoderCollectionModel::index(int row, int column,
+ const QModelIndex& parent_idx) const
+{
+ if (!hasIndex(row, column, parent_idx))
+ return QModelIndex();
+
+ DecoderCollectionItem* parent = root_.get();
+
+ if (parent_idx.isValid())
+ parent = static_cast<DecoderCollectionItem*>(parent_idx.internalPointer());
+
+ DecoderCollectionItem* subItem = parent->subItem(row).get();
+
+ return subItem ? createIndex(row, column, subItem) : QModelIndex();
+}
+
+QModelIndex DecoderCollectionModel::parent(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ DecoderCollectionItem* subItem =
+ static_cast<DecoderCollectionItem*>(index.internalPointer());
+
+ shared_ptr<DecoderCollectionItem> parent = subItem->parent();
+
+ return (parent == root_) ? QModelIndex() :
+ createIndex(parent->row(), 0, parent.get());
+}
+
+int DecoderCollectionModel::rowCount(const QModelIndex& parent_idx) const
+{
+ DecoderCollectionItem* parent = root_.get();
+
+ if (parent_idx.column() > 0)
+ return 0;
+
+ if (parent_idx.isValid())
+ parent = static_cast<DecoderCollectionItem*>(parent_idx.internalPointer());
+
+ return parent->subItemCount();
+}
+
+int DecoderCollectionModel::columnCount(const QModelIndex& parent_idx) const
+{
+ if (parent_idx.isValid())
+ return static_cast<DecoderCollectionItem*>(
+ parent_idx.internalPointer())->columnCount();
+ else
+ return root_->columnCount();
+}
+
+
+} // namespace decoder_selector
+} // namespace subwindows
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2018 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 <algorithm>
+
+#include <QDebug>
+#include <QInputDialog>
+#include <QLabel>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+#include "pv/session.hpp"
+#include "pv/subwindows/decoder_selector/subwindow.hpp"
+
+using std::reverse;
+using std::shared_ptr;
+
+namespace pv {
+namespace subwindows {
+namespace decoder_selector {
+
+
+SubWindow::SubWindow(Session& session, QWidget* parent) :
+ SubWindowBase(session, parent),
+ splitter_(new QSplitter()),
+ tree_view_(new QTreeView()),
+ model_(new DecoderCollectionModel())
+{
+ QVBoxLayout* root_layout = new QVBoxLayout(this);
+ root_layout->setContentsMargins(0, 0, 0, 0);
+ root_layout->addWidget(splitter_);
+
+ splitter_->addWidget(tree_view_);
+
+ tree_view_->setModel(model_);
+ tree_view_->setRootIsDecorated(true);
+
+ // Hide the columns that hold the detailed item information
+ tree_view_->hideColumn(2); // ID
+
+ connect(tree_view_, SIGNAL(doubleClicked(const QModelIndex&)),
+ this, SLOT(on_item_double_clicked(const QModelIndex&)));
+
+ connect(this, SIGNAL(new_decoders_selected(vector<const srd_decoder*>)),
+ &session, SLOT(on_new_decoders_selected(vector<const srd_decoder*>)));
+}
+
+bool SubWindow::has_toolbar() const
+{
+ return true;
+}
+
+QToolBar* SubWindow::create_toolbar(QWidget *parent) const
+{
+ QToolBar* toolbar = new QToolBar(parent);
+
+ return toolbar;
+}
+
+const srd_decoder* SubWindow::get_srd_decoder_from_id(QString id) const
+{
+ const srd_decoder* ret_val = nullptr;
+
+ GSList* l = g_slist_copy((GSList*)srd_decoder_list());
+ for (GSList* li = l; li; li = li->next) {
+ const srd_decoder* d = (srd_decoder*)li->data;
+ assert(d);
+
+ if (QString::fromUtf8(d->id) == id)
+ ret_val = d;
+ }
+ g_slist_free(l);
+
+ return ret_val;
+}
+
+vector<const char*> SubWindow::decoder_inputs(const srd_decoder* d) const
+{
+ vector<const char*> ret_val;
+
+ GSList* l = g_slist_copy(d->inputs);
+ for (GSList* li = l; li; li = li->next) {
+ const char* input = (const char*)li->data;
+ ret_val.push_back(input);
+ }
+ g_slist_free(l);
+
+ return ret_val;
+}
+
+vector<const srd_decoder*> SubWindow::decoders_providing(const char* output) const
+{
+ vector<const srd_decoder*> ret_val;
+
+ GSList* l = g_slist_copy((GSList*)srd_decoder_list());
+ for (GSList* li = l; li; li = li->next) {
+ const srd_decoder* d = (srd_decoder*)li->data;
+ assert(d);
+
+ if (!d->outputs)
+ continue;
+
+ // TODO For now we ignore that d->outputs is actually a list
+ if (strncmp((char*)(d->outputs->data), output, strlen(output)) == 0)
+ ret_val.push_back(d);
+ }
+ g_slist_free(l);
+
+ return ret_val;
+}
+
+void SubWindow::on_item_double_clicked(const QModelIndex& index)
+{
+ if (!index.isValid())
+ return;
+
+ QModelIndex id_index = index.model()->index(index.row(), 2, index.parent());
+ QString decoder_name = index.model()->data(id_index, Qt::DisplayRole).toString();
+
+ const srd_decoder* chosen_decoder = get_srd_decoder_from_id(decoder_name);
+ if (chosen_decoder == nullptr)
+ return;
+
+ vector<const srd_decoder*> decoders;
+ decoders.push_back(chosen_decoder);
+
+ // If the decoder only depends on logic inputs, we add it and are done
+ vector<const char*> inputs = decoder_inputs(decoders.front());
+ if (inputs.size() == 0) {
+ qWarning() << "Protocol decoder" << decoder_name << "cannot have 0 inputs!";
+ return;
+ }
+
+ if (strncmp(inputs.at(0), "logic", 5) == 0) {
+ new_decoders_selected(decoders);
+ return;
+ }
+
+ // Check if we can automatically fulfill the stacking requirements
+ while (strncmp(inputs.at(0), "logic", 5) != 0) {
+ vector<const srd_decoder*> prov_decoders = decoders_providing(inputs.at(0));
+
+ if (prov_decoders.size() == 0) {
+ // Emit warning and add the stack that we could gather so far
+ qWarning() << "Protocol decoder" << QString::fromUtf8(decoders.back()->id) \
+ << "has input that no other decoder provides:" << QString::fromUtf8(inputs.at(0));
+ break;
+ }
+
+ if (prov_decoders.size() == 1) {
+ decoders.push_back(prov_decoders.front());
+ } else {
+ // Let user decide which one to use
+ QString caption = QString(tr("Protocol decoder <b>%1</b> requires input type <b>%2</b> " \
+ "which several decoders provide.<br>Choose which one to use:<br>"))
+ .arg(QString::fromUtf8(decoders.back()->id), QString::fromUtf8(inputs.at(0)));
+
+ QStringList items;
+ for (const srd_decoder* d : prov_decoders)
+ items << QString::fromUtf8(d->id) + " (" + QString::fromUtf8(d->longname) + ")";
+ bool ok_clicked;
+ QString item = QInputDialog::getItem(this, tr("Choose Decoder"),
+ tr(caption.toUtf8()), items, 0, false, &ok_clicked);
+
+ if ((!ok_clicked) || (item.isEmpty()))
+ return;
+
+ QString d = item.section(' ', 0, 0);
+ decoders.push_back(get_srd_decoder_from_id(d));
+ }
+
+ inputs = decoder_inputs(decoders.back());
+ }
+
+ // Reverse decoder list and add the stack
+ reverse(decoders.begin(), decoders.end());
+ new_decoders_selected(decoders);
+}
+
+} // namespace decoder_selector
+} // namespace subwindows
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2018 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_SUBWINDOWS_DECODERSELECTOR_SUBWINDOW_HPP
+#define PULSEVIEW_PV_SUBWINDOWS_DECODERSELECTOR_SUBWINDOW_HPP
+
+#include <vector>
+
+#include <QAbstractItemModel>
+#include <QSplitter>
+#include <QTreeView>
+
+#include "pv/subwindows/subwindowbase.hpp"
+
+using std::shared_ptr;
+
+namespace pv {
+namespace subwindows {
+namespace decoder_selector {
+
+class DecoderCollectionItem
+{
+public:
+ DecoderCollectionItem(const vector<QVariant>& data,
+ shared_ptr<DecoderCollectionItem> parent = nullptr);
+
+ void appendSubItem(shared_ptr<DecoderCollectionItem> item);
+
+ shared_ptr<DecoderCollectionItem> subItem(int row) const;
+ shared_ptr<DecoderCollectionItem> parent() const;
+ shared_ptr<DecoderCollectionItem> findSubItem(const QVariant& value, int column);
+
+ int subItemCount() const;
+ int columnCount() const;
+ int row() const;
+ QVariant data(int column) const;
+
+private:
+ vector< shared_ptr<DecoderCollectionItem> > subItems_;
+ vector<QVariant> data_;
+ shared_ptr<DecoderCollectionItem> parent_;
+};
+
+
+class DecoderCollectionModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ DecoderCollectionModel(QObject* parent = 0);
+
+ 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<DecoderCollectionItem> root_;
+};
+
+
+class SubWindow : public SubWindowBase
+{
+ Q_OBJECT
+
+public:
+ explicit SubWindow(Session &session, QWidget *parent = nullptr);
+
+ bool has_toolbar() const;
+ QToolBar* create_toolbar(QWidget *parent) const;
+
+ const srd_decoder* get_srd_decoder_from_id(QString id) const;
+
+ /**
+ * Returns a list of input types that a given protocol decoder requires
+ * ("logic", "uart", etc.)
+ */
+ vector<const char*> decoder_inputs(const srd_decoder* d) const;
+
+ /**
+ * Returns a list of protocol decoder IDs which provide a given output
+ * ("uart", "spi", etc.)
+ */
+ vector<const srd_decoder*> decoders_providing(const char* output) const;
+
+Q_SIGNALS:
+ void new_decoders_selected(vector<const srd_decoder*> decoders);
+
+public Q_SLOTS:
+ void on_item_double_clicked(const QModelIndex& index);
+
+private:
+ QSplitter* splitter_;
+ QTreeView* tree_view_;
+ DecoderCollectionModel* model_;
+};
+
+} // decoder_selector
+} // namespace subwindows
+} // namespace pv
+
+#endif // PULSEVIEW_PV_SUBWINDOWS_DECODERSELECTOR_SUBWINDOW_HPP
+
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2018 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/>.
+ */
+
+#ifdef ENABLE_DECODE
+#include <libsigrokdecode/libsigrokdecode.h>
+#endif
+
+#include <QWidget>
+
+#include "pv/session.hpp"
+#include "pv/subwindows/subwindowbase.hpp"
+
+using std::shared_ptr;
+
+namespace pv {
+namespace subwindows {
+
+SubWindowBase::SubWindowBase(Session &session, QWidget *parent) :
+ QWidget(parent),
+ session_(session)
+{
+ connect(&session_, SIGNAL(signals_changed()), this, SLOT(on_signals_changed()));
+}
+
+bool SubWindowBase::has_toolbar() const
+{
+ return false;
+}
+
+QToolBar* SubWindowBase::create_toolbar(QWidget *parent) const
+{
+ (void)parent;
+
+ return nullptr;
+}
+
+Session& SubWindowBase::session()
+{
+ return session_;
+}
+
+const Session& SubWindowBase::session() const
+{
+ return session_;
+}
+
+unordered_set< shared_ptr<data::SignalBase> > SubWindowBase::signalbases() const
+{
+ return signalbases_;
+}
+
+void SubWindowBase::clear_signalbases()
+{
+ for (shared_ptr<data::SignalBase> signalbase : signalbases_) {
+ disconnect(signalbase.get(), SIGNAL(samples_cleared()),
+ this, SLOT(on_data_updated()));
+ disconnect(signalbase.get(), SIGNAL(samples_added(uint64_t, uint64_t, uint64_t)),
+ this, SLOT(on_samples_added(uint64_t, uint64_t, uint64_t)));
+ }
+
+ signalbases_.clear();
+}
+
+void SubWindowBase::add_signalbase(const shared_ptr<data::SignalBase> signalbase)
+{
+ signalbases_.insert(signalbase);
+}
+
+#ifdef ENABLE_DECODE
+void SubWindowBase::clear_decode_signals()
+{
+}
+
+void SubWindowBase::add_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+ (void)signal;
+}
+
+void SubWindowBase::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+ (void)signal;
+}
+#endif
+
+void SubWindowBase::on_signals_changed()
+{
+}
+
+} // namespace subwindows
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2018 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_SUBWINDOWBASE_HPP
+#define PULSEVIEW_PV_SUBWINDOWBASE_HPP
+
+#include <cstdint>
+#include <memory>
+#include <unordered_set>
+
+#include <QToolBar>
+#include <QWidget>
+
+#include <pv/data/signalbase.hpp>
+
+#ifdef ENABLE_DECODE
+#include <pv/data/decodesignal.hpp>
+#endif
+
+using std::shared_ptr;
+using std::unordered_set;
+
+namespace pv {
+
+class Session;
+
+namespace subwindows {
+
+enum SubWindowType {
+ SubWindowTypeDecoderSelector,
+};
+
+class SubWindowBase : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit SubWindowBase(Session &session, QWidget *parent = nullptr);
+
+ virtual bool has_toolbar() const;
+ virtual QToolBar* create_toolbar(QWidget *parent) const;
+
+ Session& session();
+ const Session& session() const;
+
+ /**
+ * Returns the signal bases contained in this view.
+ */
+ unordered_set< shared_ptr<data::SignalBase> > signalbases() const;
+
+ virtual void clear_signalbases();
+
+ virtual void add_signalbase(const shared_ptr<data::SignalBase> signalbase);
+
+#ifdef ENABLE_DECODE
+ 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);
+#endif
+
+public Q_SLOTS:
+ virtual void on_signals_changed();
+
+protected:
+ Session &session_;
+
+ unordered_set< shared_ptr<data::SignalBase> > signalbases_;
+};
+
+} // namespace subwindows
+} // namespace pv
+
+#endif // PULSEVIEW_PV_SUBWINDOWBASE_HPP
#include <pv/widgets/exportmenu.hpp>
#include <pv/widgets/importmenu.hpp>
#ifdef ENABLE_DECODE
-#include <pv/widgets/decodermenu.hpp>
#include <pv/data/decodesignal.hpp>
#endif
updating_sample_count_(false),
sample_count_supported_(false)
#ifdef ENABLE_DECODE
- , add_decoder_button_(new QToolButton()),
- menu_decoders_add_(new pv::widgets::DecoderMenu(this, true))
+ , add_decoder_button_(new QToolButton())
#endif
{
setObjectName(QString::fromUtf8("MainBar"));
// Setup the decoder button
#ifdef ENABLE_DECODE
- menu_decoders_add_->setTitle(tr("&Add"));
- connect(menu_decoders_add_, SIGNAL(decoder_selected(srd_decoder*)),
- this, SLOT(add_decoder(srd_decoder*)));
-
add_decoder_button_->setIcon(QIcon(":/icons/add-decoder.svg"));
add_decoder_button_->setPopupMode(QToolButton::InstantPopup);
- add_decoder_button_->setMenu(menu_decoders_add_);
- add_decoder_button_->setToolTip(tr("Add low-level, non-stacked protocol decoder"));
+ add_decoder_button_->setToolTip(tr("Add protocol decoder"));
+
+ connect(add_decoder_button_, SIGNAL(clicked()),
+ this, SLOT(on_add_decoder_clicked()));
#endif
connect(&sample_count_, SIGNAL(value_changed()),
msg.exec();
}
-void MainBar::add_decoder(srd_decoder *decoder)
-{
-#ifdef ENABLE_DECODE
- assert(decoder);
- shared_ptr<data::DecodeSignal> signal = session_.add_decode_signal();
- if (signal)
- signal->stack_decoder(decoder);
-#else
- (void)decoder;
-#endif
-}
-
void MainBar::export_file(shared_ptr<OutputFormat> format, bool selection_only)
{
using pv::dialogs::StoreProgress;
update_device_list();
}
+void MainBar::on_add_decoder_clicked()
+{
+ show_decoder_selector(&session_);
+}
+
void MainBar::add_toolbar_widgets()
{
addAction(action_new_view_);
private Q_SLOTS:
void show_session_error(const QString text, const QString info_text);
- void add_decoder(srd_decoder *decoder);
-
void export_file(shared_ptr<sigrok::OutputFormat> format,
bool selection_only = false);
void import_file(shared_ptr<sigrok::InputFormat> format);
void on_actionConnect_triggered();
+ void on_add_decoder_clicked();
+
protected:
void add_toolbar_widgets();
Q_SIGNALS:
void new_view(Session *session);
+ void show_decoder_selector(Session *session);
private:
QToolButton *open_button_, *save_button_;
#ifdef ENABLE_DECODE
QToolButton *add_decoder_button_;
- QMenu *const menu_decoders_add_;
#endif
};
${PROJECT_SOURCE_DIR}/pv/prop/string.cpp
${PROJECT_SOURCE_DIR}/pv/popups/channels.cpp
${PROJECT_SOURCE_DIR}/pv/popups/deviceoptions.cpp
+ ${PROJECT_SOURCE_DIR}/pv/subwindows/subwindowbase.cpp
${PROJECT_SOURCE_DIR}/pv/toolbars/mainbar.cpp
${PROJECT_SOURCE_DIR}/pv/views/trace/analogsignal.cpp
${PROJECT_SOURCE_DIR}/pv/views/trace/cursor.cpp
${PROJECT_SOURCE_DIR}/pv/prop/int.hpp
${PROJECT_SOURCE_DIR}/pv/prop/property.hpp
${PROJECT_SOURCE_DIR}/pv/prop/string.hpp
+ ${PROJECT_SOURCE_DIR}/pv/subwindows/subwindowbase.cpp
${PROJECT_SOURCE_DIR}/pv/toolbars/mainbar.hpp
${PROJECT_SOURCE_DIR}/pv/views/trace/analogsignal.hpp
${PROJECT_SOURCE_DIR}/pv/views/trace/cursor.hpp
${PROJECT_SOURCE_DIR}/pv/data/decode/decoder.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/row.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/rowdata.cpp
+ ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/item.cpp
+ ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/model.cpp
+ ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.cpp
${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp
list(APPEND pulseview_TEST_HEADERS
${PROJECT_SOURCE_DIR}/pv/data/decodesignal.hpp
+ ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.hpp
${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.hpp