+
+ return v;
+}
+
+void MainWindow::remove_view(shared_ptr<views::ViewBase> view)
+{
+ for (shared_ptr<Session> session : sessions_) {
+ if (!session->has_view(view))
+ continue;
+
+ // Find the dock the view is contained in and remove it
+ for (auto& entry : view_docks_)
+ if (entry.second == view) {
+ // Remove the view from the session
+ session->deregister_view(view);
+
+ // Remove the view from its parent; otherwise, Qt will
+ // call deleteLater() on it, which causes a double free
+ // since the shared_ptr in view_docks_ doesn't know
+ // that Qt keeps a pointer to the view around
+ view->setParent(nullptr);
+
+ // Delete the view's dock widget and all widgets inside it
+ entry.first->deleteLater();
+
+ // Remove the dock widget from the list and stop iterating
+ view_docks_.erase(entry.first);
+ break;
+ }
+ }
+}
+
+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) {
+#ifdef ENABLE_DECODE
+ case subwindows::SubWindowTypeDecoderSelector:
+ title = tr("Decoder Selector");
+ break;
+#endif
+ default:
+ break;
+ }
+
+ 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
+
+#ifdef ENABLE_DECODE
+ if (type == subwindows::SubWindowTypeDecoderSelector)
+ v = make_shared<subwindows::decoder_selector::SubWindow>(session, dock_main);
+#endif
+
+ 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));
+
+ if (v->minimum_width() > 0)
+ dock->setMinimumSize(v->minimum_width(), 0);
+
+ return v;
+}
+
+shared_ptr<Session> MainWindow::add_session()
+{
+ static int last_session_id = 1;
+ QString name = tr("Session %1").arg(last_session_id++);
+
+ shared_ptr<Session> session = make_shared<Session>(device_manager_, name);
+
+ connect(session.get(), SIGNAL(add_view(const QString&, views::ViewType, Session*)),
+ this, SLOT(on_add_view(const QString&, views::ViewType, Session*)));
+ connect(session.get(), SIGNAL(name_changed()),
+ this, SLOT(on_session_name_changed()));
+ session_state_mapper_.setMapping(session.get(), session.get());
+ connect(session.get(), SIGNAL(capture_state_changed(int)),
+ &session_state_mapper_, SLOT(map()));
+
+ sessions_.push_back(session);
+
+ QMainWindow *window = new QMainWindow();
+ window->setWindowFlags(Qt::Widget); // Remove Qt::Window flag
+ session_windows_[session] = window;
+
+ int index = session_selector_.addTab(window, name);
+ session_selector_.setCurrentIndex(index);
+ last_focused_session_ = session;
+
+ window->setDockNestingEnabled(true);
+
+ shared_ptr<views::ViewBase> main_view =
+ add_view(name, views::ViewTypeTrace, *session);
+
+ return session;
+}
+
+void MainWindow::remove_session(shared_ptr<Session> session)
+{
+ // Determine the height of the button before it collapses
+ int h = new_session_button_->height();
+
+ // Stop capture while the session still exists so that the UI can be
+ // updated in case we're currently running. If so, this will schedule a
+ // call to our on_capture_state_changed() slot for the next run of the
+ // event loop. We need to have this executed immediately or else it will
+ // be dismissed since the session object will be deleted by the time we
+ // leave this method and the event loop gets a chance to run again.
+ session->stop_capture();
+ QApplication::processEvents();
+
+ for (const shared_ptr<views::ViewBase>& view : session->views())
+ remove_view(view);
+
+ QMainWindow *window = session_windows_.at(session);
+ session_selector_.removeTab(session_selector_.indexOf(window));
+
+ session_windows_.erase(session);
+
+ if (last_focused_session_ == session)
+ last_focused_session_.reset();
+
+ // Remove the session from our list of sessions (which also destroys it)
+ sessions_.remove_if([&](shared_ptr<Session> s) {
+ return s == session; });
+
+ if (sessions_.empty()) {
+ // When there are no more tabs, the height of the QTabWidget
+ // drops to zero. We must prevent this to keep the static
+ // widgets visible
+ for (QWidget *w : static_tab_widget_->findChildren<QWidget*>()) // clazy:exclude=range-loop
+ w->setMinimumHeight(h);
+
+ int margin = static_tab_widget_->layout()->contentsMargins().bottom();
+ static_tab_widget_->setMinimumHeight(h + 2 * margin);
+ session_selector_.setMinimumHeight(h + 2 * margin);
+
+ // Update the window title if there is no view left to
+ // generate focus change events
+ setWindowTitle(WindowTitle);
+ }
+}
+
+void MainWindow::add_session_with_file(string open_file_name,
+ string open_file_format)
+{
+ shared_ptr<Session> session = add_session();
+ session->load_init_file(open_file_name, open_file_format);
+}
+
+void MainWindow::add_default_session()
+{
+ // Only add the default session if there would be no session otherwise
+ if (sessions_.size() > 0)
+ return;
+
+ shared_ptr<Session> session = add_session();
+
+ // Check the list of available devices. Prefer the one that was
+ // found with user supplied scan specs (if applicable). Then try
+ // one of the auto detected devices that are not the demo device.
+ // Pick demo in the absence of "genuine" hardware devices.
+ shared_ptr<devices::HardwareDevice> user_device, other_device, demo_device;
+ for (const shared_ptr<devices::HardwareDevice>& dev : device_manager_.devices()) {
+ if (dev == device_manager_.user_spec_device()) {
+ user_device = dev;
+ } else if (dev->hardware_device()->driver()->name() == "demo") {
+ demo_device = dev;
+ } else {
+ other_device = dev;
+ }
+ }
+ if (user_device)
+ session->select_device(user_device);
+ else if (other_device)
+ session->select_device(other_device);
+ else
+ session->select_device(demo_device);
+}
+
+void MainWindow::save_sessions()
+{
+ QSettings settings;
+ int id = 0;
+
+ for (shared_ptr<Session>& session : sessions_) {
+ // Ignore sessions using the demo device or no device at all
+ if (session->device()) {
+ shared_ptr<devices::HardwareDevice> device =
+ dynamic_pointer_cast< devices::HardwareDevice >
+ (session->device());
+
+ if (device &&
+ device->hardware_device()->driver()->name() == "demo")
+ continue;
+
+ settings.beginGroup("Session" + QString::number(id++));
+ settings.remove(""); // Remove all keys in this group
+ session->save_settings(settings);
+ settings.endGroup();
+ }
+ }
+
+ settings.setValue("sessions", id);
+}
+
+void MainWindow::restore_sessions()
+{
+ QSettings settings;
+ int i, session_count;
+
+ session_count = settings.value("sessions", 0).toInt();
+
+ for (i = 0; i < session_count; i++) {
+ settings.beginGroup("Session" + QString::number(i));
+ shared_ptr<Session> session = add_session();
+ session->restore_settings(settings);
+ settings.endGroup();
+ }
+}
+
+void MainWindow::on_setting_changed(const QString &key, const QVariant &value)
+{
+ if (key == GlobalSettings::Key_View_ColoredBG)
+ on_settingViewColoredBg_changed(value);
+
+ if (key == GlobalSettings::Key_View_ShowSamplingPoints)
+ on_settingViewShowSamplingPoints_changed(value);
+
+ if (key == GlobalSettings::Key_View_ShowAnalogMinorGrid)
+ on_settingViewShowAnalogMinorGrid_changed(value);