]> sigrok.org Git - pulseview.git/blobdiff - pv/view/view.cpp
Change namespace for the trace view and implement ViewBase
[pulseview.git] / pv / view / view.cpp
index 8d5bd6d3ad02b836d7d5ee1f0f29e8f1cccf4a99..a086d23de523f07e2be1192cbfc3b4bd2a028b09 100644 (file)
 #include <cassert>
 #include <climits>
 #include <cmath>
+#include <iostream>
 #include <iterator>
 #include <mutex>
 #include <unordered_set>
 
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/serialization/serialization.hpp>
 #include <boost/thread/locks.hpp>
 
 #include <QApplication>
+#include <QHBoxLayout>
 #include <QEvent>
 #include <QFontMetrics>
 #include <QMouseEvent>
@@ -91,7 +96,8 @@ using std::vector;
 using std::weak_ptr;
 
 namespace pv {
-namespace view {
+namespace views {
+namespace TraceView {
 
 const Timestamp View::MaxScale("1e9");
 const Timestamp View::MinScale("1e-12");
@@ -101,12 +107,41 @@ const int View::MaxViewAutoUpdateRate = 25; // No more than 25 Hz with sticky sc
 
 const int View::ScaleUnits[3] = {1, 2, 5};
 
+
+CustomAbstractScrollArea::CustomAbstractScrollArea(QWidget *parent) :
+       QAbstractScrollArea(parent)
+{
+}
+
+void CustomAbstractScrollArea::setViewportMargins(int left, int top, int right, int bottom)
+{
+       QAbstractScrollArea::setViewportMargins(left, top, right, bottom);
+}
+
+bool CustomAbstractScrollArea::viewportEvent(QEvent *event)
+{
+       switch (event->type()) {
+       case QEvent::Paint:
+       case QEvent::MouseButtonPress:
+       case QEvent::MouseButtonRelease:
+       case QEvent::MouseButtonDblClick:
+       case QEvent::MouseMove:
+       case QEvent::Wheel:
+       case QEvent::TouchBegin:
+       case QEvent::TouchUpdate:
+       case QEvent::TouchEnd:
+               return false;
+       default:
+               return QAbstractScrollArea::viewportEvent(event);
+       }
+}
+
 View::View(Session &session, QWidget *parent) :
-       QAbstractScrollArea(parent),
-       session_(session),
+       ViewBase(session, parent),
        viewport_(new Viewport(*this)),
        ruler_(new Ruler(*this)),
        header_(new Header(*this)),
+       scrollarea_(this),
        scale_(1e-3),
        offset_(0),
        updating_scroll_(false),
@@ -122,20 +157,11 @@ View::View(Session &session, QWidget *parent) :
        trigger_markers_(),
        hover_point_(-1, -1)
 {
-       connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
+       connect(scrollarea_.horizontalScrollBar(), SIGNAL(valueChanged(int)),
                this, SLOT(h_scroll_value_changed(int)));
-       connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
+       connect(scrollarea_.verticalScrollBar(), SIGNAL(valueChanged(int)),
                this, SLOT(v_scroll_value_changed()));
 
-       connect(&session_, SIGNAL(signals_changed()),
-               this, SLOT(signals_changed()));
-       connect(&session_, SIGNAL(capture_state_changed(int)),
-               this, SLOT(capture_state_updated(int)));
-       connect(&session_, SIGNAL(data_received()),
-               this, SLOT(data_updated()));
-       connect(&session_, SIGNAL(frame_ended()),
-               this, SLOT(data_updated()));
-
        connect(header_, SIGNAL(selection_changed()),
                ruler_, SLOT(clear_selection()));
        connect(ruler_, SIGNAL(selection_changed()),
@@ -158,7 +184,13 @@ View::View(Session &session, QWidget *parent) :
        delayed_view_updater_.setSingleShot(true);
        delayed_view_updater_.setInterval(1000 / MaxViewAutoUpdateRate);
 
-       setViewport(viewport_);
+       /* To let the scroll area fill up the parent QWidget (this), we need a layout */
+       QHBoxLayout *layout = new QHBoxLayout(this);
+       setLayout(layout);
+       layout->setContentsMargins(0, 0, 0, 0);
+       layout->addWidget(&scrollarea_);
+
+       scrollarea_.setViewport(viewport_);
 
        viewport_->installEventFilter(this);
        ruler_->installEventFilter(this);
@@ -186,6 +218,45 @@ const Session& View::session() const
        return session_;
 }
 
+std::unordered_set< std::shared_ptr<Signal> > View::signals() const
+{
+       return signals_;
+}
+
+void View::clear_signals()
+{
+       signals_.clear();
+}
+
+void View::add_signal(const shared_ptr<Signal> signal)
+{
+       signals_.insert(signal);
+}
+
+#ifdef ENABLE_DECODE
+void View::clear_decode_signals()
+{
+       decode_traces_.clear();
+}
+
+void View::add_decode_signal(shared_ptr<data::SignalBase> signalbase)
+{
+       shared_ptr<DecodeTrace> d(
+               new DecodeTrace(session_, signalbase, decode_traces_.size()));
+       decode_traces_.push_back(d);
+}
+
+void View::remove_decode_signal(shared_ptr<data::SignalBase> signalbase)
+{
+       for (auto i = decode_traces_.begin(); i != decode_traces_.end(); i++)
+               if ((*i)->base() == signalbase) {
+                       decode_traces_.erase(i);
+                       signals_changed();
+                       return;
+               }
+}
+#endif
+
 View* View::view()
 {
        return this;
@@ -206,6 +277,45 @@ const Viewport* View::viewport() const
        return viewport_;
 }
 
+void View::save_settings(QSettings &settings) const
+{
+       settings.setValue("scale", scale_);
+
+       std::stringstream ss;
+       boost::archive::text_oarchive oa(ss);
+       oa << boost::serialization::make_nvp("offset", offset_);
+       settings.setValue("offset", QString::fromStdString(ss.str()));
+
+       for (shared_ptr<Signal> signal : signals_) {
+               settings.beginGroup(signal->base()->internal_name());
+               signal->save_settings(settings);
+               settings.endGroup();
+       }
+}
+
+void View::restore_settings(QSettings &settings)
+{
+       if (settings.contains("scale"))
+               set_scale(settings.value("scale").toDouble());
+
+       if (settings.contains("offset")) {
+               util::Timestamp offset;
+               std::stringstream ss;
+               ss << settings.value("offset").toString().toStdString();
+
+               boost::archive::text_iarchive ia(ss);
+               ia >> boost::serialization::make_nvp("offset", offset);
+
+               set_offset(offset);
+       }
+
+       for (shared_ptr<Signal> signal : signals_) {
+               settings.beginGroup(signal->base()->internal_name());
+               signal->restore_settings(settings);
+               settings.endGroup();
+       }
+}
+
 vector< shared_ptr<TimeItem> > View::time_items() const
 {
        const vector<shared_ptr<Flag>> f(flags());
@@ -248,12 +358,12 @@ void View::set_offset(const pv::util::Timestamp& offset)
 
 int View::owner_visual_v_offset() const
 {
-       return -verticalScrollBar()->sliderPosition();
+       return -scrollarea_.verticalScrollBar()->sliderPosition();
 }
 
 void View::set_v_offset(int offset)
 {
-       verticalScrollBar()->setSliderPosition(offset);
+       scrollarea_.verticalScrollBar()->setSliderPosition(offset);
        header_->update();
        viewport_->update();
 }
@@ -397,11 +507,9 @@ void View::set_scale_offset(double scale, const Timestamp& offset)
 
 set< shared_ptr<SignalData> > View::get_visible_data() const
 {
-       const unordered_set< shared_ptr<Signal> > sigs(session().signals());
-
        // Make a set of all the visible data objects
        set< shared_ptr<SignalData> > visible_data;
-       for (const shared_ptr<Signal> sig : sigs)
+       for (const shared_ptr<Signal> sig : signals_)
                if (sig->enabled())
                        visible_data.insert(sig->data());
 
@@ -527,13 +635,6 @@ const QPoint& View::hover_point() const
        return hover_point_;
 }
 
-void View::update_viewport()
-{
-       assert(viewport_);
-       viewport_->update();
-       header_->update();
-}
-
 void View::restack_all_trace_tree_items()
 {
        // Make a list of owners that is sorted from deepest first
@@ -657,6 +758,8 @@ void View::calculate_tick_spacing()
 void View::update_scroll()
 {
        assert(viewport_);
+       QScrollBar *hscrollbar = scrollarea_.horizontalScrollBar();
+       QScrollBar *vscrollbar = scrollarea_.verticalScrollBar();
 
        const QSize areaSize = viewport_->size();
 
@@ -668,31 +771,31 @@ void View::update_scroll()
 
        int major_tick_distance = (tick_period_ / scale_).convert_to<int>();
 
-       horizontalScrollBar()->setPageStep(areaSize.width() / 2);
-       horizontalScrollBar()->setSingleStep(major_tick_distance);
+       hscrollbar->setPageStep(areaSize.width() / 2);
+       hscrollbar->setSingleStep(major_tick_distance);
 
        updating_scroll_ = true;
 
        if (length < MaxScrollValue) {
-               horizontalScrollBar()->setRange(0, length);
-               horizontalScrollBar()->setSliderPosition(offset.convert_to<double>());
+               hscrollbar->setRange(0, length);
+               hscrollbar->setSliderPosition(offset.convert_to<double>());
        } else {
-               horizontalScrollBar()->setRange(0, MaxScrollValue);
-               horizontalScrollBar()->setSliderPosition(
+               hscrollbar->setRange(0, MaxScrollValue);
+               hscrollbar->setSliderPosition(
                        (offset_ * MaxScrollValue / (scale_ * length)).convert_to<double>());
        }
 
        updating_scroll_ = false;
 
        // Set the vertical scrollbar
-       verticalScrollBar()->setPageStep(areaSize.height());
-       verticalScrollBar()->setSingleStep(areaSize.height() / 8);
+       vscrollbar->setPageStep(areaSize.height());
+       vscrollbar->setSingleStep(areaSize.height() / 8);
 
        const pair<int, int> extents = v_extents();
 
        // Don't change the scrollbar range if there are no traces
        if (extents.first != extents.second)
-               verticalScrollBar()->setRange(extents.first - areaSize.height(),
+               vscrollbar->setRange(extents.first - areaSize.height(),
                        extents.second);
 
        if (scroll_needs_defaults)
@@ -701,7 +804,7 @@ void View::update_scroll()
 
 void View::reset_scroll()
 {
-       verticalScrollBar()->setRange(0, 0);
+       scrollarea_.verticalScrollBar()->setRange(0, 0);
 }
 
 void View::set_scroll_default()
@@ -734,8 +837,8 @@ void View::set_scroll_default()
 
 void View::update_layout()
 {
-       setViewportMargins(
-               header_->sizeHint().width() - pv::view::Header::BaselineOffset,
+       scrollarea_.setViewportMargins(
+               header_->sizeHint().width() - Header::BaselineOffset,
                ruler_->sizeHint().height(), 0, 0);
        ruler_->setGeometry(viewport_->x(), 0,
                viewport_->width(), ruler_->extended_size_hint().height());
@@ -744,22 +847,9 @@ void View::update_layout()
        update_scroll();
 }
 
-void View::paint_label(QPainter &p, const QRect &rect, bool hover)
-{
-       (void)p;
-       (void)rect;
-       (void)hover;
-}
-
-QRectF View::label_rect(const QRectF &rect)
-{
-       (void)rect;
-       return QRectF();
-}
-
 TraceTreeItemOwner* View::find_prevalent_trace_group(
        const shared_ptr<sigrok::ChannelGroup> &group,
-       const unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+       const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
                &signal_map)
 {
        assert(group);
@@ -769,13 +859,13 @@ TraceTreeItemOwner* View::find_prevalent_trace_group(
 
        // Make a set and a list of all the owners
        for (const auto &channel : group->channels()) {
-               const auto iter = signal_map.find(channel);
-               if (iter == signal_map.end())
-                       continue;
-
-               TraceTreeItemOwner *const o = (*iter).second->owner();
-               owner_list.push_back(o);
-               owners.insert(o);
+               for (auto entry : signal_map) {
+                       if (entry.first->channel() == channel) {
+                               TraceTreeItemOwner *const o = (entry.second)->owner();
+                               owner_list.push_back(o);
+                               owners.insert(o);
+                       }
+               }
        }
 
        // Iterate through the list of owners, and find the most prevalent
@@ -796,24 +886,24 @@ TraceTreeItemOwner* View::find_prevalent_trace_group(
 
 vector< shared_ptr<Trace> > View::extract_new_traces_for_channels(
        const vector< shared_ptr<sigrok::Channel> > &channels,
-       const unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+       const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
                &signal_map,
        set< shared_ptr<Trace> > &add_list)
 {
        vector< shared_ptr<Trace> > filtered_traces;
 
        for (const auto &channel : channels) {
-               const auto map_iter = signal_map.find(channel);
-               if (map_iter == signal_map.end())
-                       continue;
-
-               shared_ptr<Trace> trace = (*map_iter).second;
-               const auto list_iter = add_list.find(trace);
-               if (list_iter == add_list.end())
-                       continue;
-
-               filtered_traces.push_back(trace);
-               add_list.erase(list_iter);
+               for (auto entry : signal_map) {
+                       if (entry.first->channel() == channel) {
+                               shared_ptr<Trace> trace = entry.second;
+                               const auto list_iter = add_list.find(trace);
+                               if (list_iter == add_list.end())
+                                       continue;
+
+                               filtered_traces.push_back(trace);
+                               add_list.erase(list_iter);
+                       }
+               }
        }
 
        return filtered_traces;
@@ -823,10 +913,8 @@ void View::determine_time_unit()
 {
        // Check whether we know the sample rate and hence can use time as the unit
        if (time_unit_ == util::TimeUnit::Samples) {
-               const unordered_set< shared_ptr<Signal> > sigs(session().signals());
-
                // Check all signals but...
-               for (const shared_ptr<Signal> signal : sigs) {
+               for (const shared_ptr<Signal> signal : signals_) {
                        const shared_ptr<SignalData> data = signal->data();
 
                        // ...only check first segment of each
@@ -865,24 +953,6 @@ bool View::eventFilter(QObject *object, QEvent *event)
        return QObject::eventFilter(object, event);
 }
 
-bool View::viewportEvent(QEvent *event)
-{
-       switch (event->type()) {
-       case QEvent::Paint:
-       case QEvent::MouseButtonPress:
-       case QEvent::MouseButtonRelease:
-       case QEvent::MouseButtonDblClick:
-       case QEvent::MouseMove:
-       case QEvent::Wheel:
-       case QEvent::TouchBegin:
-       case QEvent::TouchUpdate:
-       case QEvent::TouchEnd:
-               return false;
-       default:
-               return QAbstractScrollArea::viewportEvent(event);
-       }
-}
-
 void View::resizeEvent(QResizeEvent*)
 {
        update_layout();
@@ -924,7 +994,7 @@ void View::h_scroll_value_changed(int value)
                sticky_scrolling_changed(false);
        }
 
-       const int range = horizontalScrollBar()->maximum();
+       const int range = scrollarea_.horizontalScrollBar()->maximum();
        if (range < MaxScrollValue)
                set_offset(scale_ * value);
        else {
@@ -954,13 +1024,15 @@ void View::signals_changed()
        // Do we need to set the vertical scrollbar to its default position later?
        // We do if there are no traces, i.e. the scroll bar has no range set
        bool reset_scrollbar =
-               verticalScrollBar()->minimum() == verticalScrollBar()->maximum();
+               (scrollarea_.verticalScrollBar()->minimum() ==
+                       scrollarea_.verticalScrollBar()->maximum());
 
        if (!session_.device()) {
                reset_scroll();
+               signals_.clear();
        } else {
-               assert(sr_dev);
                sr_dev = session_.device()->device();
+               assert(sr_dev);
                channels = sr_dev->channels();
        }
 
@@ -972,14 +1044,10 @@ void View::signals_changed()
        const set<shared_ptr<Trace>> prev_traces(
                prev_trace_list.begin(), prev_trace_list.end());
 
-       const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
-
-       set< shared_ptr<Trace> > traces(sigs.begin(), sigs.end());
+       set< shared_ptr<Trace> > traces(signals_.begin(), signals_.end());
 
 #ifdef ENABLE_DECODE
-       const vector< shared_ptr<DecodeTrace> > decode_traces(
-               session().get_decode_signals());
-       traces.insert(decode_traces.begin(), decode_traces.end());
+       traces.insert(decode_traces_.begin(), decode_traces_.end());
 #endif
 
        set< shared_ptr<Trace> > add_traces;
@@ -993,10 +1061,10 @@ void View::signals_changed()
                inserter(remove_traces, remove_traces.begin()));
 
        // Make a look-up table of sigrok Channels to pulseview Signals
-       unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+       unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
                signal_map;
-       for (const shared_ptr<Signal> &sig : sigs)
-               signal_map[sig->channel()] = sig;
+       for (const shared_ptr<Signal> &sig : signals_)
+               signal_map[sig->base()] = sig;
 
        // Populate channel groups
        if (sr_dev)
@@ -1196,5 +1264,6 @@ void View::on_hover_point_changed()
                r->hover_point_changed();
 }
 
-} // namespace view
+} // namespace TraceView
+} // namespace views
 } // namespace pv