+ connect(scrollarea_.verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(v_scroll_value_changed()));
+
+ connect(header_, SIGNAL(selection_changed()),
+ ruler_, SLOT(clear_selection()));
+ connect(ruler_, SIGNAL(selection_changed()),
+ header_, SLOT(clear_selection()));
+
+ connect(header_, SIGNAL(selection_changed()),
+ this, SIGNAL(selection_changed()));
+ connect(ruler_, SIGNAL(selection_changed()),
+ this, SIGNAL(selection_changed()));
+
+ connect(this, SIGNAL(hover_point_changed()),
+ this, SLOT(on_hover_point_changed()));
+
+ connect(&lazy_event_handler_, SIGNAL(timeout()),
+ this, SLOT(process_sticky_events()));
+ lazy_event_handler_.setSingleShot(true);
+
+ /* 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);
+ header_->installEventFilter(this);
+
+ // Trigger the initial event manually. The default device has signals
+ // which were created before this object came into being
+ signals_changed();
+
+ // make sure the transparent widgets are on the top
+ ruler_->raise();
+ header_->raise();
+
+ // Update the zoom state
+ calculate_tick_spacing();
+}
+
+Session& View::session()
+{
+ return session_;
+}
+
+const Session& View::session() const
+{
+ return session_;
+}
+
+unordered_set< shared_ptr<Signal> > View::signals() const
+{
+ return signals_;
+}
+
+void View::clear_signals()
+{
+ ViewBase::clear_signalbases();
+ signals_.clear();
+}
+
+void View::add_signal(const shared_ptr<Signal> signal)
+{
+ ViewBase::add_signalbase(signal->base());
+ 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;
+}
+
+const View* View::view() const
+{
+ return this;
+}
+
+Viewport* View::viewport()
+{
+ return viewport_;
+}
+
+const Viewport* View::viewport() const
+{
+ return viewport_;
+}
+
+void View::save_settings(QSettings &settings) const
+{
+ settings.setValue("scale", scale_);
+ settings.setValue("v_offset",
+ scrollarea_.verticalScrollBar()->sliderPosition());
+
+ 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)
+{
+ // Note: It is assumed that this function is only called once,
+ // immediately after restoring a previous session.
+
+ if (settings.contains("scale"))
+ set_scale(settings.value("scale").toDouble());
+
+ if (settings.contains("offset")) {
+ util::Timestamp offset;
+ stringstream ss;
+ ss << settings.value("offset").toString().toStdString();