]> sigrok.org Git - pulseview.git/blobdiff - pv/session.cpp
Fix #1183 by adding a workaround for srzip handling
[pulseview.git] / pv / session.cpp
index 6d9ce830bdaf19ef8ee33ba651f0844516904778..a71ad1c56d2b2001bf0e65cdb2e706613ba09f1a 100644 (file)
@@ -17,9 +17,6 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <QDebug>
-#include <QFileInfo>
-
 #include <cassert>
 #include <memory>
 #include <mutex>
 
 #include <sys/stat.h>
 
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+
 #include "devicemanager.hpp"
 #include "mainwindow.hpp"
 #include "session.hpp"
+#include "util.hpp"
 
 #include "data/analog.hpp"
 #include "data/analogsegment.hpp"
@@ -66,8 +68,8 @@ using std::bad_alloc;
 using std::dynamic_pointer_cast;
 using std::find_if;
 using std::function;
-using std::lock_guard;
 using std::list;
+using std::lock_guard;
 using std::make_pair;
 using std::make_shared;
 using std::map;
@@ -83,7 +85,6 @@ using std::string;
 using std::unique_lock;
 #endif
 using std::unique_ptr;
-using std::unordered_set;
 using std::vector;
 
 using sigrok::Analog;
@@ -105,11 +106,18 @@ using Gst::ElementFactory;
 using Gst::Pipeline;
 #endif
 
+using pv::data::SignalGroup;
+using pv::util::Timestamp;
+using pv::views::trace::Signal;
+using pv::views::trace::AnalogSignal;
+using pv::views::trace::LogicSignal;
+
 namespace pv {
 
 shared_ptr<sigrok::Context> Session::sr_context;
 
 Session::Session(DeviceManager &device_manager, QString name) :
+       shutting_down_(false),
        device_manager_(device_manager),
        default_name_(name),
        name_(name),
@@ -121,8 +129,15 @@ Session::Session(DeviceManager &device_manager, QString name) :
 
 Session::~Session()
 {
+       shutting_down_ = true;
+
        // Stop and join to the thread
        stop_capture();
+
+       for (SignalGroup* group : signal_groups_) {
+               group->clear();
+               delete group;
+       }
 }
 
 DeviceManager& Session::device_manager()
@@ -162,7 +177,17 @@ void Session::set_name(QString name)
        name_changed();
 }
 
-const list< shared_ptr<views::ViewBase> > Session::views() const
+QString Session::save_path() const
+{
+       return save_path_;
+}
+
+void Session::set_save_path(QString path)
+{
+       save_path_ = path;
+}
+
+const vector< shared_ptr<views::ViewBase> > Session::views() const
 {
        return views_;
 }
@@ -189,13 +214,13 @@ bool Session::data_saved() const
 
 void Session::save_setup(QSettings &settings) const
 {
-       int decode_signals = 0, views = 0;
+       int i = 0;
 
        // Save channels and decoders
        for (const shared_ptr<data::SignalBase>& base : signalbases_) {
 #ifdef ENABLE_DECODE
                if (base->is_decode_signal()) {
-                       settings.beginGroup("decode_signal" + QString::number(decode_signals++));
+                       settings.beginGroup("decode_signal" + QString::number(i++));
                        base->save_settings(settings);
                        settings.endGroup();
                } else
@@ -207,23 +232,63 @@ void Session::save_setup(QSettings &settings) const
                }
        }
 
-       settings.setValue("decode_signals", decode_signals);
+       settings.setValue("decode_signals", i);
 
        // Save view states and their signal settings
        // Note: main_view must be saved as view0
-       settings.beginGroup("view" + QString::number(views++));
+       i = 0;
+       settings.beginGroup("view" + QString::number(i++));
        main_view_->save_settings(settings);
        settings.endGroup();
 
        for (const shared_ptr<views::ViewBase>& view : views_) {
                if (view != main_view_) {
-                       settings.beginGroup("view" + QString::number(views++));
+                       settings.beginGroup("view" + QString::number(i++));
+                       settings.setValue("type", view->get_type());
                        view->save_settings(settings);
                        settings.endGroup();
                }
        }
 
-       settings.setValue("views", views);
+       settings.setValue("views", i);
+
+       int view_id = 0;
+       i = 0;
+       for (const shared_ptr<views::ViewBase>& vb : views_) {
+               shared_ptr<views::trace::View> tv = dynamic_pointer_cast<views::trace::View>(vb);
+               if (tv) {
+                       for (const shared_ptr<views::trace::TimeItem>& time_item : tv->time_items()) {
+
+                               const shared_ptr<views::trace::Flag> flag =
+                                       dynamic_pointer_cast<views::trace::Flag>(time_item);
+                               if (flag) {
+                                       if (!flag->enabled())
+                                               continue;
+
+                                       settings.beginGroup("meta_obj" + QString::number(i++));
+                                       settings.setValue("type", "time_marker");
+                                       settings.setValue("assoc_view", view_id);
+                                       GlobalSettings::store_timestamp(settings, "time", flag->time());
+                                       settings.setValue("text", flag->get_text());
+                                       settings.endGroup();
+                               }
+                       }
+
+                       if (tv->cursors_shown()) {
+                               settings.beginGroup("meta_obj" + QString::number(i++));
+                               settings.setValue("type", "selection");
+                               settings.setValue("assoc_view", view_id);
+                               const shared_ptr<views::trace::CursorPair> cp = tv->cursors();
+                               GlobalSettings::store_timestamp(settings, "start_time", cp->first()->time());
+                               GlobalSettings::store_timestamp(settings, "end_time", cp->second()->time());
+                               settings.endGroup();
+                       }
+               }
+
+               view_id++;
+       }
+
+       settings.setValue("meta_objs", i);
 }
 
 void Session::save_settings(QSettings &settings) const
@@ -258,25 +323,36 @@ void Session::save_settings(QSettings &settings) const
                        settings.endGroup();
                }
 
-               shared_ptr<devices::SessionFile> sessionfile_device =
-                       dynamic_pointer_cast<devices::SessionFile>(device_);
-
-               if (sessionfile_device) {
+               // Having saved the data to srzip overrides the current device. This is
+               // a crappy hack around the fact that saving e.g. an imported file to
+               // srzip would require changing the underlying libsigrok device
+               if (!save_path_.isEmpty()) {
+                       QFileInfo fi = QFileInfo(QDir(save_path_), name_);
                        settings.setValue("device_type", "sessionfile");
                        settings.beginGroup("device");
-                       settings.setValue("filename", QString::fromStdString(
-                               sessionfile_device->full_name()));
+                       settings.setValue("filename", fi.absoluteFilePath());
                        settings.endGroup();
-               }
+               } else {
+                       shared_ptr<devices::SessionFile> sessionfile_device =
+                               dynamic_pointer_cast<devices::SessionFile>(device_);
+
+                       if (sessionfile_device) {
+                               settings.setValue("device_type", "sessionfile");
+                               settings.beginGroup("device");
+                               settings.setValue("filename", QString::fromStdString(
+                                       sessionfile_device->full_name()));
+                               settings.endGroup();
+                       }
 
-               shared_ptr<devices::InputFile> inputfile_device =
-                       dynamic_pointer_cast<devices::InputFile>(device_);
+                       shared_ptr<devices::InputFile> inputfile_device =
+                               dynamic_pointer_cast<devices::InputFile>(device_);
 
-               if (inputfile_device) {
-                       settings.setValue("device_type", "inputfile");
-                       settings.beginGroup("device");
-                       inputfile_device->save_meta_to_settings(settings);
-                       settings.endGroup();
+                       if (inputfile_device) {
+                               settings.setValue("device_type", "inputfile");
+                               settings.beginGroup("device");
+                               inputfile_device->save_meta_to_settings(settings);
+                               settings.endGroup();
+                       }
                }
 
                save_setup(settings);
@@ -312,20 +388,52 @@ void Session::restore_setup(QSettings &settings)
 
                if (i > 0) {
                        views::ViewType type = (views::ViewType)settings.value("type").toInt();
-                       add_view(name_, type, this);
+                       add_view(type, this);
                        views_.back()->restore_settings(settings);
                } else
                        main_view_->restore_settings(settings);
 
                settings.endGroup();
        }
+
+       // Restore meta objects like markers and cursors
+       int meta_objs = settings.value("meta_objs").toInt();
+
+       for (int i = 0; i < meta_objs; i++) {
+               settings.beginGroup("meta_obj" + QString::number(i));
+
+               shared_ptr<views::ViewBase> vb;
+               shared_ptr<views::trace::View> tv;
+               if (settings.contains("assoc_view"))
+                       vb = views_.at(settings.value("assoc_view").toInt());
+
+               if (vb)
+                       tv = dynamic_pointer_cast<views::trace::View>(vb);
+
+               const QString type = settings.value("type").toString();
+
+               if ((type == "time_marker") && tv) {
+                       Timestamp ts = GlobalSettings::restore_timestamp(settings, "time");
+                       shared_ptr<views::trace::Flag> flag = tv->add_flag(ts);
+                       flag->set_text(settings.value("text").toString());
+               }
+
+               if ((type == "selection") && tv) {
+                       Timestamp start = GlobalSettings::restore_timestamp(settings, "start_time");
+                       Timestamp end = GlobalSettings::restore_timestamp(settings, "end_time");
+                       tv->set_cursors(start, end);
+                       tv->show_cursors();
+               }
+
+               settings.endGroup();
+       }
 }
 
 void Session::restore_settings(QSettings &settings)
 {
        shared_ptr<devices::Device> device;
 
-       QString device_type = settings.value("device_type").toString();
+       const QString device_type = settings.value("device_type").toString();
 
        if (device_type == "hardware") {
                map<string, string> dev_info;
@@ -358,16 +466,17 @@ void Session::restore_settings(QSettings &settings)
                settings.endGroup();
        }
 
+
+       QString filename;
        if ((device_type == "sessionfile") || (device_type == "inputfile")) {
                if (device_type == "sessionfile") {
                        settings.beginGroup("device");
-                       QString filename = settings.value("filename").toString();
+                       filename = settings.value("filename").toString();
                        settings.endGroup();
 
-                       if (QFileInfo(filename).isReadable()) {
+                       if (QFileInfo(filename).isReadable())
                                device = make_shared<devices::SessionFile>(device_manager_.context(),
                                        filename.toStdString());
-                       }
                }
 
                if (device_type == "inputfile") {
@@ -386,6 +495,14 @@ void Session::restore_settings(QSettings &settings)
 
                        set_name(QString::fromStdString(
                                dynamic_pointer_cast<devices::File>(device)->display_name(device_manager_)));
+
+                       if (!filename.isEmpty()) {
+                               // Only set the save path if we load an srzip file
+                               if (device_type == "sessionfile")
+                                       set_save_path(QFileInfo(filename).absolutePath());
+
+                               set_name(QFileInfo(filename).fileName());
+                       }
                }
        }
 
@@ -423,14 +540,22 @@ void Session::set_device(shared_ptr<devices::Device> device)
 
        // Remove all stored data and reset all views
        for (shared_ptr<views::ViewBase> view : views_) {
-               view->clear_signals();
+               view->clear_signalbases();
 #ifdef ENABLE_DECODE
                view->clear_decode_signals();
 #endif
                view->reset_view_state();
        }
+
+       for (SignalGroup* group : signal_groups_) {
+               group->clear();
+               delete group;
+       }
+       signal_groups_.clear();
+
        for (const shared_ptr<data::SignalData>& d : all_signal_data_)
                d->clear();
+
        all_signal_data_.clear();
        signalbases_.clear();
        cur_logic_segment_.reset();
@@ -480,6 +605,17 @@ void Session::set_default_device()
        set_device((iter == devices.end()) ? devices.front() : *iter);
 }
 
+bool Session::using_file_device() const
+{
+       shared_ptr<devices::SessionFile> sessionfile_device =
+               dynamic_pointer_cast<devices::SessionFile>(device_);
+
+       shared_ptr<devices::InputFile> inputfile_device =
+               dynamic_pointer_cast<devices::InputFile>(device_);
+
+       return (sessionfile_device || inputfile_device);
+}
+
 /**
  * Convert generic options to data types that are specific to InputFormat.
  *
@@ -516,8 +652,12 @@ Session::input_format_options(vector<string> user_spec,
                 * data type.
                 */
                auto found = fmt_opts.find(key);
-               if (found == fmt_opts.end())
+               if (found == fmt_opts.end()) {
+                       qCritical() << "Supplied input option" << QString::fromStdString(key) <<
+                               "is not a valid option for this input module, it will be ignored!";
                        continue;
+               }
+
                shared_ptr<Option> opt = found->second;
                result[key] = opt->parse_string(val);
        }
@@ -542,7 +682,7 @@ void Session::load_init_file(const string &file_name,
                                return f.first == user_name; });
                if (iter == formats.end()) {
                        MainWindow::show_session_error(tr("Error"),
-                               tr("Unexpected input format: %s").arg(QString::fromStdString(format)));
+                               tr("Unexpected input format: %1").arg(QString::fromStdString(format)));
                        return;
                }
                input_format = (*iter).second;
@@ -577,7 +717,7 @@ void Session::load_file(QString file_name, QString setup_file_name,
                                        device_manager_.context(),
                                        file_name.toStdString())));
        } catch (Error& e) {
-               MainWindow::show_session_error(tr("Failed to load ") + file_name, e.what());
+               MainWindow::show_session_error(tr("Failed to load %1").arg(file_name), e.what());
                set_default_device();
                main_bar_->update_device_list();
                return;
@@ -600,6 +740,10 @@ void Session::load_file(QString file_name, QString setup_file_name,
        start_capture([&, errorMessage](QString infoMessage) {
                MainWindow::show_session_error(errorMessage, infoMessage); });
 
+       // Only set save path if we loaded an srzip file
+       if (dynamic_pointer_cast<devices::SessionFile>(device_))
+               set_save_path(QFileInfo(file_name).absolutePath());
+
        set_name(QFileInfo(file_name).fileName());
 }
 
@@ -663,9 +807,8 @@ void Session::stop_capture()
 
 void Session::register_view(shared_ptr<views::ViewBase> view)
 {
-       if (views_.empty()) {
+       if (views_.empty())
                main_view_ = view;
-       }
 
        views_.push_back(view);
 
@@ -673,35 +816,29 @@ void Session::register_view(shared_ptr<views::ViewBase> view)
        update_signals();
 
        // Add all other signals
-       unordered_set< shared_ptr<data::SignalBase> > view_signalbases =
-               view->signalbases();
-
-       views::trace::View *trace_view =
-               qobject_cast<views::trace::View*>(view.get());
-
-       if (trace_view) {
-               for (const shared_ptr<data::SignalBase>& signalbase : signalbases_) {
-                       const int sb_exists = count_if(
-                               view_signalbases.cbegin(), view_signalbases.cend(),
-                               [&](const shared_ptr<data::SignalBase> &sb) {
-                                       return sb == signalbase;
-                               });
-                       // Add the signal to the view as it doesn't have it yet
-                       if (!sb_exists)
-                               switch (signalbase->type()) {
-                               case data::SignalBase::AnalogChannel:
-                               case data::SignalBase::LogicChannel:
-                               case data::SignalBase::DecodeChannel:
+       vector< shared_ptr<data::SignalBase> > view_signalbases = view->signalbases();
+
+       for (const shared_ptr<data::SignalBase>& signalbase : signalbases_) {
+               const int sb_exists = count_if(
+                       view_signalbases.cbegin(), view_signalbases.cend(),
+                       [&](const shared_ptr<data::SignalBase> &sb) {
+                               return sb == signalbase;
+                       });
+
+               // Add the signal to the view if it doesn't have it yet
+               if (!sb_exists)
+                       switch (signalbase->type()) {
+                       case data::SignalBase::AnalogChannel:
+                       case data::SignalBase::LogicChannel:
+                       case data::SignalBase::MathChannel:
+                               view->add_signalbase(signalbase);
+                               break;
+                       case data::SignalBase::DecodeChannel:
 #ifdef ENABLE_DECODE
-                                       trace_view->add_decode_signal(
-                                               dynamic_pointer_cast<data::DecodeSignal>(signalbase));
+                               view->add_decode_signal(dynamic_pointer_cast<data::DecodeSignal>(signalbase));
 #endif
-                                       break;
-                               case data::SignalBase::MathChannel:
-                                       // TBD
-                                       break;
-                               }
-               }
+                               break;
+                       }
        }
 
        signals_changed();
@@ -709,7 +846,9 @@ void Session::register_view(shared_ptr<views::ViewBase> view)
 
 void Session::deregister_view(shared_ptr<views::ViewBase> view)
 {
-       views_.remove_if([&](shared_ptr<views::ViewBase> v) { return v == view; });
+       views_.erase(std::remove_if(views_.begin(), views_.end(),
+               [&](shared_ptr<views::ViewBase> v) { return v == view; }),
+               views_.end());
 
        if (views_.empty()) {
                main_view_.reset();
@@ -769,20 +908,34 @@ vector<util::Timestamp> Session::get_triggers(uint32_t segment_id) const
        return result;
 }
 
-const unordered_set< shared_ptr<data::SignalBase> > Session::signalbases() const
+const vector< shared_ptr<data::SignalBase> > Session::signalbases() const
 {
        return signalbases_;
 }
 
-bool Session::all_segments_complete(uint32_t segment_id) const
+void Session::add_generated_signal(shared_ptr<data::SignalBase> signal)
 {
-       bool all_complete = true;
+       signalbases_.push_back(signal);
 
-       for (const shared_ptr<data::SignalBase>& base : signalbases_)
-               if (!base->segment_is_complete(segment_id))
-                       all_complete = false;
+       for (shared_ptr<views::ViewBase>& view : views_)
+               view->add_signalbase(signal);
 
-       return all_complete;
+       update_signals();
+}
+
+void Session::remove_generated_signal(shared_ptr<data::SignalBase> signal)
+{
+       if (shutting_down_)
+               return;
+
+       signalbases_.erase(std::remove_if(signalbases_.begin(), signalbases_.end(),
+               [&](shared_ptr<data::SignalBase> s) { return s == signal; }),
+               signalbases_.end());
+
+       for (shared_ptr<views::ViewBase>& view : views_)
+               view->remove_signalbase(signal);
+
+       update_signals();
 }
 
 #ifdef ENABLE_DECODE
@@ -794,7 +947,7 @@ shared_ptr<data::DecodeSignal> Session::add_decode_signal()
                // Create the decode signal
                signal = make_shared<data::DecodeSignal>(*this);
 
-               signalbases_.insert(signal);
+               signalbases_.push_back(signal);
 
                // Add the decode signal to all views
                for (shared_ptr<views::ViewBase>& view : views_)
@@ -811,7 +964,12 @@ shared_ptr<data::DecodeSignal> Session::add_decode_signal()
 
 void Session::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
 {
-       signalbases_.erase(signal);
+       if (shutting_down_)
+               return;
+
+       signalbases_.erase(std::remove_if(signalbases_.begin(), signalbases_.end(),
+               [&](shared_ptr<data::SignalBase> s) { return s == signal; }),
+               signalbases_.end());
 
        for (shared_ptr<views::ViewBase>& view : views_)
                view->remove_decode_signal(signal);
@@ -820,6 +978,22 @@ void Session::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
 }
 #endif
 
+bool Session::all_segments_complete(uint32_t segment_id) const
+{
+       bool all_complete = true;
+
+       for (const shared_ptr<data::SignalBase>& base : signalbases_)
+               if (!base->segment_is_complete(segment_id))
+                       all_complete = false;
+
+       return all_complete;
+}
+
+MetadataObjManager* Session::metadata_obj_manager()
+{
+       return &metadata_obj_manager_;
+}
+
 void Session::set_capture_state(capture_state state)
 {
        bool changed;
@@ -845,7 +1019,7 @@ void Session::update_signals()
                signalbases_.clear();
                logic_data_.reset();
                for (shared_ptr<views::ViewBase>& view : views_) {
-                       view->clear_signals();
+                       view->clear_signalbases();
 #ifdef ENABLE_DECODE
                        view->clear_decode_signals();
 #endif
@@ -860,7 +1034,7 @@ void Session::update_signals()
                signalbases_.clear();
                logic_data_.reset();
                for (shared_ptr<views::ViewBase>& view : views_) {
-                       view->clear_signals();
+                       view->clear_signalbases();
 #ifdef ENABLE_DECODE
                        view->clear_decode_signals();
 #endif
@@ -875,7 +1049,7 @@ void Session::update_signals()
                [] (shared_ptr<Channel> channel) {
                        return channel->type() == sigrok::ChannelType::LOGIC; });
 
-       // Create data containers for the logic data segments
+       // Create a common data container for the logic signalbases
        {
                lock_guard<recursive_mutex> data_lock(data_mutex_);
 
@@ -883,91 +1057,96 @@ void Session::update_signals()
                        logic_data_.reset();
                } else if (!logic_data_ ||
                        logic_data_->num_channels() != logic_channel_count) {
-                       logic_data_.reset(new data::Logic(
-                               logic_channel_count));
+                       logic_data_.reset(new data::Logic(logic_channel_count));
                        assert(logic_data_);
                }
        }
 
-       // Make the signals list
-       for (shared_ptr<views::ViewBase>& viewbase : views_) {
-               views::trace::View *trace_view =
-                       qobject_cast<views::trace::View*>(viewbase.get());
-
-               if (trace_view) {
-                       unordered_set< shared_ptr<views::trace::Signal> >
-                               prev_sigs(trace_view->signals());
-                       trace_view->clear_signals();
-
-                       for (auto channel : sr_dev->channels()) {
-                               shared_ptr<data::SignalBase> signalbase;
-                               shared_ptr<views::trace::Signal> signal;
-
-                               // Find the channel in the old signals
-                               const auto iter = find_if(
-                                       prev_sigs.cbegin(), prev_sigs.cend(),
-                                       [&](const shared_ptr<views::trace::Signal> &s) {
-                                               return s->base()->channel() == channel;
-                                       });
-                               if (iter != prev_sigs.end()) {
-                                       // Copy the signal from the old set to the new
-                                       signal = *iter;
-                                       trace_view->add_signal(signal);
-                               } else {
-                                       // Find the signalbase for this channel if possible
-                                       signalbase.reset();
-                                       for (const shared_ptr<data::SignalBase>& b : signalbases_)
-                                               if (b->channel() == channel)
-                                                       signalbase = b;
-
-                                       switch(channel->type()->id()) {
-                                       case SR_CHANNEL_LOGIC:
-                                               if (!signalbase) {
-                                                       signalbase = make_shared<data::SignalBase>(channel,
-                                                               data::SignalBase::LogicChannel);
-                                                       signalbases_.insert(signalbase);
-
-                                                       all_signal_data_.insert(logic_data_);
-                                                       signalbase->set_data(logic_data_);
-
-                                                       connect(this, SIGNAL(capture_state_changed(int)),
-                                                               signalbase.get(), SLOT(on_capture_state_changed(int)));
-                                               }
-
-                                               signal = shared_ptr<views::trace::Signal>(
-                                                       new views::trace::LogicSignal(*this,
-                                                               device_, signalbase));
-                                               trace_view->add_signal(signal);
-                                               break;
+       // Create signalbases if necessary
+       for (auto channel : sr_dev->channels()) {
 
-                                       case SR_CHANNEL_ANALOG:
-                                       {
-                                               if (!signalbase) {
-                                                       signalbase = make_shared<data::SignalBase>(channel,
-                                                               data::SignalBase::AnalogChannel);
-                                                       signalbases_.insert(signalbase);
-
-                                                       shared_ptr<data::Analog> data(new data::Analog());
-                                                       all_signal_data_.insert(data);
-                                                       signalbase->set_data(data);
-
-                                                       connect(this, SIGNAL(capture_state_changed(int)),
-                                                               signalbase.get(), SLOT(on_capture_state_changed(int)));
-                                               }
-
-                                               signal = shared_ptr<views::trace::Signal>(
-                                                       new views::trace::AnalogSignal(
-                                                               *this, signalbase));
-                                               trace_view->add_signal(signal);
-                                               break;
-                                       }
+               // Try to find the channel in the list of existing signalbases
+               const auto iter = find_if(signalbases_.cbegin(), signalbases_.cend(),
+                       [&](const shared_ptr<SignalBase> &sb) { return sb->channel() == channel; });
+
+               // Not found, let's make a signalbase for it
+               if (iter == signalbases_.cend()) {
+                       shared_ptr<SignalBase> signalbase;
+                       switch(channel->type()->id()) {
+                       case SR_CHANNEL_LOGIC:
+                               signalbase = make_shared<data::SignalBase>(channel, data::SignalBase::LogicChannel);
+                               signalbases_.push_back(signalbase);
+
+                               all_signal_data_.insert(logic_data_);
+                               signalbase->set_data(logic_data_);
+
+                               connect(this, SIGNAL(capture_state_changed(int)),
+                                       signalbase.get(), SLOT(on_capture_state_changed(int)));
+                               break;
+
+                       case SR_CHANNEL_ANALOG:
+                               signalbase = make_shared<data::SignalBase>(channel, data::SignalBase::AnalogChannel);
+                               signalbases_.push_back(signalbase);
+
+                               shared_ptr<data::Analog> data(new data::Analog());
+                               all_signal_data_.insert(data);
+                               signalbase->set_data(data);
+
+                               connect(this, SIGNAL(capture_state_changed(int)),
+                                       signalbase.get(), SLOT(on_capture_state_changed(int)));
+                               break;
+                       }
+               }
+       }
+
+       // Create and assign default signal groups if needed
+       if (signal_groups_.empty()) {
+               for (auto& entry : sr_dev->channel_groups()) {
+                       const shared_ptr<sigrok::ChannelGroup>& group = entry.second;
 
-                                       default:
-                                               assert(false);
+                       if (group->channels().size() <= 1)
+                               continue;
+
+                       SignalGroup* sg = new SignalGroup(QString::fromStdString(entry.first));
+                       for (const shared_ptr<sigrok::Channel>& channel : group->channels()) {
+                               for (shared_ptr<data::SignalBase> s : signalbases_) {
+                                       if (s->channel() == channel) {
+                                               sg->append_signal(s);
                                                break;
                                        }
                                }
                        }
+                       signal_groups_.emplace_back(sg);
+               }
+       }
+
+       // Update all views
+       for (shared_ptr<views::ViewBase>& viewbase : views_) {
+               vector< shared_ptr<SignalBase> > view_signalbases =
+                               viewbase->signalbases();
+
+               // Add all non-decode signalbases that don't yet exist in the view
+               for (shared_ptr<SignalBase>& session_sb : signalbases_) {
+                       if (session_sb->type() == SignalBase::DecodeChannel)
+                               continue;
+
+                       const auto iter = find_if(view_signalbases.cbegin(), view_signalbases.cend(),
+                               [&](const shared_ptr<SignalBase> &sb) { return sb == session_sb; });
+
+                       if (iter == view_signalbases.cend())
+                               viewbase->add_signalbase(session_sb);
+               }
+
+               // Remove all non-decode signalbases that no longer exist
+               for (shared_ptr<SignalBase>& view_sb : view_signalbases) {
+                       if (view_sb->type() == SignalBase::DecodeChannel)
+                               continue;
+
+                       const auto iter = find_if(signalbases_.cbegin(), signalbases_.cend(),
+                               [&](const shared_ptr<SignalBase> &sb) { return sb == view_sb; });
+
+                       if (iter == signalbases_.cend())
+                               viewbase->remove_signalbase(view_sb);
                }
        }
 
@@ -1033,6 +1212,8 @@ void Session::sample_thread_proc(function<void (const QString)> error_handler)
                lock_guard<recursive_mutex> lock(data_mutex_);
                cur_logic_segment_.reset();
                cur_analog_segments_.clear();
+               for (shared_ptr<data::SignalBase> sb : signalbases_)
+                       sb->clear_sample_data();
        }
        highest_segment_id_ = -1;
        frame_began_ = false;
@@ -1275,7 +1456,7 @@ void Session::feed_in_frame_end()
        signal_segment_completed();
 }
 
-void Session::feed_in_logic(shared_ptr<Logic> logic)
+void Session::feed_in_logic(shared_ptr<sigrok::Logic> logic)
 {
        if (logic->data_length() == 0) {
                qDebug() << "WARNING: Received logic packet with 0 samples.";
@@ -1319,7 +1500,7 @@ void Session::feed_in_logic(shared_ptr<Logic> logic)
        data_received();
 }
 
-void Session::feed_in_analog(shared_ptr<Analog> analog)
+void Session::feed_in_analog(shared_ptr<sigrok::Analog> analog)
 {
        if (analog->num_samples() == 0) {
                qDebug() << "WARNING: Received analog packet with 0 samples.";
@@ -1478,8 +1659,10 @@ void Session::on_new_decoders_selected(vector<const srd_decoder*> decoders)
        shared_ptr<data::DecodeSignal> signal = add_decode_signal();
 
        if (signal)
-               for (const srd_decoder* d : decoders)
-                       signal->stack_decoder(d);
+               for (unsigned int i = 0; i < decoders.size(); i++) {
+                       const srd_decoder* d = decoders[i];
+                       signal->stack_decoder(d, !(i < decoders.size() - 1));
+               }
 }
 #endif