]> sigrok.org Git - pulseview.git/commitdiff
Make the first view own the toolbar instead of the main window
authorSoeren Apel <redacted>
Fri, 26 Aug 2016 20:29:40 +0000 (22:29 +0200)
committerSoeren Apel <redacted>
Fri, 26 Aug 2016 20:29:40 +0000 (22:29 +0200)
Handling multiple sessions requires that every session can be
controlled individually. This means that either

a) there could be one toolbar that adjusts dynamically to the
session whose view currently has focus

or

b) every session has its own toolbar.

I opted for b) because it's more intuitive and more straightforward
to implement. So now every session has a main view (the first one
created) and a main bar that sits in the same dock widget.

pv/mainwindow.cpp
pv/mainwindow.hpp
pv/session.cpp
pv/session.hpp
pv/toolbars/mainbar.cpp
pv/toolbars/mainbar.hpp

index b3377f7ab9154208bad84f8fbbc6750c6c1df046..f8463257b5b882531692abc3c0ab4803efdcfa64 100644 (file)
 #include <algorithm>
 #include <iterator>
 
-#include <boost/algorithm/string/join.hpp>
-
 #include <QAction>
 #include <QApplication>
-#include <QButtonGroup>
 #include <QCloseEvent>
-#include <QFileDialog>
-#include <QMessageBox>
-#include <QMenu>
-#include <QMenuBar>
+#include <QDockWidget>
 #include <QSettings>
-#include <QStatusBar>
-#include <QVBoxLayout>
 #include <QWidget>
 
-#include <QDockWidget>
-
 #include "mainwindow.hpp"
 
 #include "devicemanager.hpp"
 #include "util.hpp"
-#include "data/segment.hpp"
-#include "devices/hardwaredevice.hpp"
-#include "devices/inputfile.hpp"
-#include "devices/sessionfile.hpp"
 #include "dialogs/about.hpp"
-#include "dialogs/connect.hpp"
-#include "dialogs/inputoutputoptions.hpp"
-#include "dialogs/storeprogress.hpp"
 #include "toolbars/mainbar.hpp"
-#include "view/logicsignal.hpp"
 #include "view/view.hpp"
-#include "widgets/exportmenu.hpp"
-#include "widgets/importmenu.hpp"
-#ifdef ENABLE_DECODE
-#include "widgets/decodermenu.hpp"
-#endif
 
-#include <inttypes.h>
 #include <stdint.h>
 #include <stdarg.h>
-#include <glib.h>
 #include <libsigrokcxx/libsigrokcxx.hpp>
 
-using std::cerr;
-using std::endl;
 using std::list;
 using std::make_shared;
 using std::map;
-using std::max;
-using std::pair;
 using std::shared_ptr;
 using std::string;
-using std::vector;
-
-using boost::algorithm::join;
-
-using sigrok::Error;
-using sigrok::OutputFormat;
-using sigrok::InputFormat;
 
 namespace pv {
 
@@ -94,8 +58,7 @@ namespace view {
 class ViewItem;
 }
 
-const char *MainWindow::SettingOpenDirectory = "MainWindow/OpenDirectory";
-const char *MainWindow::SettingSaveDirectory = "MainWindow/SaveDirectory";
+using toolbars::MainBar;
 
 MainWindow::MainWindow(DeviceManager &device_manager,
        string open_file_name, string open_file_format,
@@ -103,88 +66,36 @@ MainWindow::MainWindow(DeviceManager &device_manager,
        QMainWindow(parent),
        device_manager_(device_manager),
        session_(device_manager),
-       action_open_(new QAction(this)),
-       action_save_as_(new QAction(this)),
-       action_save_selection_as_(new QAction(this)),
-       action_connect_(new QAction(this)),
-       action_quit_(new QAction(this)),
-       action_view_zoom_in_(new QAction(this)),
-       action_view_zoom_out_(new QAction(this)),
-       action_view_zoom_fit_(new QAction(this)),
-       action_view_zoom_one_to_one_(new QAction(this)),
+       open_file_name_(open_file_name),
+       open_file_format_(open_file_format),
        action_view_sticky_scrolling_(new QAction(this)),
        action_view_coloured_bg_(new QAction(this)),
-       action_view_show_cursors_(new QAction(this)),
        action_about_(new QAction(this))
-#ifdef ENABLE_DECODE
-       , menu_decoders_add_(new pv::widgets::DecoderMenu(this, true))
-#endif
 {
        qRegisterMetaType<util::Timestamp>("util::Timestamp");
 
        setup_ui();
        restore_ui_settings();
-       if (open_file_name.empty())
-               select_init_device();
-       else
-               load_init_file(open_file_name, open_file_format);
 }
 
 MainWindow::~MainWindow()
 {
        for (auto entry : view_docks_) {
+
                const std::shared_ptr<QDockWidget> dock = entry.first;
+
+               // Remove view from the dock widget's QMainWindow
+               QMainWindow *dock_main = dynamic_cast<QMainWindow*>(dock->widget());
+               dock_main->setCentralWidget(0);
+
+               // Remove the QMainWindow
                dock->setWidget(0);
+
                const std::shared_ptr<pv::view::View> view = entry.second;
                session_.deregister_view(view);
        }
 }
 
-QAction* MainWindow::action_open() const
-{
-       return action_open_;
-}
-
-QAction* MainWindow::action_save_as() const
-{
-       return action_save_as_;
-}
-
-QAction* MainWindow::action_save_selection_as() const
-{
-       return action_save_selection_as_;
-}
-
-QAction* MainWindow::action_connect() const
-{
-       return action_connect_;
-}
-
-QAction* MainWindow::action_quit() const
-{
-       return action_quit_;
-}
-
-QAction* MainWindow::action_view_zoom_in() const
-{
-       return action_view_zoom_in_;
-}
-
-QAction* MainWindow::action_view_zoom_out() const
-{
-       return action_view_zoom_out_;
-}
-
-QAction* MainWindow::action_view_zoom_fit() const
-{
-       return action_view_zoom_fit_;
-}
-
-QAction* MainWindow::action_view_zoom_one_to_one() const
-{
-       return action_view_zoom_one_to_one_;
-}
-
 QAction* MainWindow::action_view_sticky_scrolling() const
 {
        return action_view_sticky_scrolling_;
@@ -195,23 +106,11 @@ QAction* MainWindow::action_view_coloured_bg() const
        return action_view_coloured_bg_;
 }
 
-QAction* MainWindow::action_view_show_cursors() const
-{
-       return action_view_show_cursors_;
-}
-
 QAction* MainWindow::action_about() const
 {
        return action_about_;
 }
 
-#ifdef ENABLE_DECODE
-QMenu* MainWindow::menu_decoder_add() const
-{
-       return menu_decoders_add_;
-}
-#endif
-
 shared_ptr<pv::view::View> MainWindow::get_active_view() const
 {
        // If there's only one view, use it...
@@ -242,15 +141,21 @@ shared_ptr<pv::view::View> MainWindow::add_view(const QString &title,
 {
        shared_ptr<pv::view::View> v;
 
-       if (type == pv::view::TraceView)
-               v = make_shared<pv::view::View>(session, this);
-
-       if (v) {
+       if (type == pv::view::TraceView) {
                shared_ptr<QDockWidget> dock = make_shared<QDockWidget>(title, this);
-               dock->setWidget(v.get());
                dock->setObjectName(title);
                addDockWidget(Qt::TopDockWidgetArea, dock.get());
+
+               // Insert a QMainWindow into the dock widget to allow for a tool bar
+               QMainWindow *dock_main = new QMainWindow(dock.get());
+               dock_main->setWindowFlags(Qt::Widget);  // Remove Qt::Window flag
+
+               v = make_shared<pv::view::View>(session, dock_main);
                view_docks_[dock] = v;
+               session.register_view(v);
+
+               dock_main->setCentralWidget(v.get());
+               dock->setWidget(dock_main);
 
                dock->setFeatures(QDockWidget::DockWidgetMovable |
                        QDockWidget::DockWidgetFloatable);
@@ -265,168 +170,22 @@ shared_ptr<pv::view::View> MainWindow::add_view(const QString &title,
 
                        v->enable_sticky_scrolling(action_view_sticky_scrolling_->isChecked());
                        v->enable_coloured_bg(action_view_coloured_bg_->isChecked());
-                       action_view_show_cursors_->setChecked(v->cursors_shown());
-               }
-
-               session.register_view(v);
-       }
-
-       return v;
-}
 
-void MainWindow::run_stop()
-{
-       switch (session_.get_capture_state()) {
-       case Session::Stopped:
-               session_.start_capture([&](QString message) {
-                       session_error("Capture failed", message); });
-               break;
-       case Session::AwaitingTrigger:
-       case Session::Running:
-               session_.stop_capture();
-               break;
-       }
-}
-
-void MainWindow::select_device(shared_ptr<devices::Device> device)
-{
-       try {
-               if (device)
-                       session_.set_device(device);
-               else
-                       session_.set_default_device();
-       } catch (const QString &e) {
-               QMessageBox msg(this);
-               msg.setText(e);
-               msg.setInformativeText(tr("Failed to Select Device"));
-               msg.setStandardButtons(QMessageBox::Ok);
-               msg.setIcon(QMessageBox::Warning);
-               msg.exec();
-       }
-}
-
-void MainWindow::export_file(shared_ptr<OutputFormat> format,
-       bool selection_only)
-{
-       using pv::dialogs::StoreProgress;
-
-       // Make sure there's a view selected to pull the data from
-       shared_ptr<pv::view::View> view = get_active_view();
-       if (!view) {
-               show_session_error(tr("No View Selected"), tr("Please click on the " \
-                               "view whose data you want to save and try again."));
-               return;
-       }
-
-       // Stop any currently running capture session
-       session_.stop_capture();
-
-       QSettings settings;
-       const QString dir = settings.value(SettingSaveDirectory).toString();
-
-       std::pair<uint64_t, uint64_t> sample_range;
-
-       // Selection only? Verify that the cursors are active and fetch their values
-       if (selection_only) {
-               if (!view->cursors()->enabled()) {
-                       show_session_error(tr("Missing Cursors"), tr("You need to set the " \
-                                       "cursors before you can save the data enclosed by them " \
-                                       "to a session file (e.g. using ALT-V - Show Cursors)."));
-                       return;
+                       shared_ptr<MainBar> main_bar = session.main_bar();
+                       if (!main_bar) {
+                               main_bar = make_shared<MainBar>(session_, *this,
+                                       open_file_name_, open_file_format_);
+                               dock_main->addToolBar(main_bar.get());
+                               session.set_main_bar(main_bar);
+
+                               open_file_name_.clear();
+                               open_file_format_.clear();
+                       }
+                       main_bar->action_view_show_cursors()->setChecked(v->cursors_shown());
                }
-
-               const double samplerate = session_.get_samplerate();
-
-               const pv::util::Timestamp& start_time = view->cursors()->first()->time();
-               const pv::util::Timestamp& end_time = view->cursors()->second()->time();
-
-               const uint64_t start_sample =
-                       std::max((double)0, start_time.convert_to<double>() * samplerate);
-               const uint64_t end_sample = end_time.convert_to<double>() * samplerate;
-
-               sample_range = std::make_pair(start_sample, end_sample);
-       } else {
-               sample_range = std::make_pair(0, 0);
-       }
-
-       // Construct the filter
-       const vector<string> exts = format->extensions();
-       QString filter = tr("%1 files ").arg(
-               QString::fromStdString(format->description()));
-
-       if (exts.empty())
-               filter += "(*.*)";
-       else
-               filter += QString("(*.%1);;%2 (*.*)").arg(
-                       QString::fromStdString(join(exts, ", *.")),
-                       tr("All Files"));
-
-       // Show the file dialog
-       const QString file_name = QFileDialog::getSaveFileName(
-               this, tr("Save File"), dir, filter);
-
-       if (file_name.isEmpty())
-               return;
-
-       const QString abs_path = QFileInfo(file_name).absolutePath();
-       settings.setValue(SettingSaveDirectory, abs_path);
-
-       // Show the options dialog
-       map<string, Glib::VariantBase> options;
-       if (!format->options().empty()) {
-               dialogs::InputOutputOptions dlg(
-                       tr("Export %1").arg(QString::fromStdString(
-                               format->description())),
-                       format->options(), this);
-               if (!dlg.exec())
-                       return;
-               options = dlg.options();
        }
 
-       StoreProgress *dlg = new StoreProgress(file_name, format, options,
-               sample_range, session_, this);
-       dlg->run();
-}
-
-void MainWindow::import_file(shared_ptr<InputFormat> format)
-{
-       assert(format);
-
-       QSettings settings;
-       const QString dir = settings.value(SettingOpenDirectory).toString();
-
-       // Construct the filter
-       const vector<string> exts = format->extensions();
-       const QString filter = exts.empty() ? "" :
-               tr("%1 files (*.%2)").arg(
-                       QString::fromStdString(format->description()),
-                       QString::fromStdString(join(exts, ", *.")));
-
-       // Show the file dialog
-       const QString file_name = QFileDialog::getOpenFileName(
-               this, tr("Import File"), dir, tr(
-                       "%1 files (*.*);;All Files (*.*)").arg(
-                       QString::fromStdString(format->description())));
-
-       if (file_name.isEmpty())
-               return;
-
-       // Show the options dialog
-       map<string, Glib::VariantBase> options;
-       if (!format->options().empty()) {
-               dialogs::InputOutputOptions dlg(
-                       tr("Import %1").arg(QString::fromStdString(
-                               format->description())),
-                       format->options(), this);
-               if (!dlg.exec())
-                       return;
-               options = dlg.options();
-       }
-
-       load_file(file_name, format, options);
-
-       const QString abs_path = QFileInfo(file_name).absolutePath();
-       settings.setValue(SettingOpenDirectory, abs_path);
+       return v;
 }
 
 void MainWindow::setup_ui()
@@ -438,77 +197,6 @@ void MainWindow::setup_ui()
        icon.addFile(QString(":/icons/sigrok-logo-notext.png"));
        setWindowIcon(icon);
 
-       action_open_->setText(tr("&Open..."));
-       action_open_->setIcon(QIcon::fromTheme("document-open",
-               QIcon(":/icons/document-open.png")));
-       action_open_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
-       action_open_->setObjectName(QString::fromUtf8("actionOpen"));
-
-       action_save_as_->setText(tr("&Save As..."));
-       action_save_as_->setIcon(QIcon::fromTheme("document-save-as",
-               QIcon(":/icons/document-save-as.png")));
-       action_save_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
-       action_save_as_->setObjectName(QString::fromUtf8("actionSaveAs"));
-
-       action_save_selection_as_->setText(tr("Save Selected &Range As..."));
-       action_save_selection_as_->setIcon(QIcon::fromTheme("document-save-as",
-               QIcon(":/icons/document-save-as.png")));
-       action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
-       action_save_selection_as_->setObjectName(QString::fromUtf8("actionSaveSelectionAs"));
-
-       widgets::ExportMenu *menu_file_export = new widgets::ExportMenu(this,
-               device_manager_.context());
-       menu_file_export->setTitle(tr("&Export"));
-       connect(menu_file_export,
-               SIGNAL(format_selected(std::shared_ptr<sigrok::OutputFormat>)),
-               this, SLOT(export_file(std::shared_ptr<sigrok::OutputFormat>)));
-
-       widgets::ImportMenu *menu_file_import = new widgets::ImportMenu(this,
-               device_manager_.context());
-       menu_file_import->setTitle(tr("&Import"));
-       connect(menu_file_import,
-               SIGNAL(format_selected(std::shared_ptr<sigrok::InputFormat>)),
-               this, SLOT(import_file(std::shared_ptr<sigrok::InputFormat>)));
-
-       action_connect_->setText(tr("&Connect to Device..."));
-       action_connect_->setObjectName(QString::fromUtf8("actionConnect"));
-
-       action_quit_->setText(tr("&Quit"));
-       action_quit_->setIcon(QIcon::fromTheme("application-exit",
-               QIcon(":/icons/application-exit.png")));
-       action_quit_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
-       action_quit_->setObjectName(QString::fromUtf8("actionQuit"));
-
-       action_view_zoom_in_->setText(tr("Zoom &In"));
-       action_view_zoom_in_->setIcon(QIcon::fromTheme("zoom-in",
-               QIcon(":/icons/zoom-in.png")));
-       // simply using Qt::Key_Plus shows no + in the menu
-       action_view_zoom_in_->setShortcut(QKeySequence::ZoomIn);
-       action_view_zoom_in_->setObjectName(
-               QString::fromUtf8("actionViewZoomIn"));
-
-       action_view_zoom_out_->setText(tr("Zoom &Out"));
-       action_view_zoom_out_->setIcon(QIcon::fromTheme("zoom-out",
-               QIcon(":/icons/zoom-out.png")));
-       action_view_zoom_out_->setShortcut(QKeySequence::ZoomOut);
-       action_view_zoom_out_->setObjectName(
-               QString::fromUtf8("actionViewZoomOut"));
-
-       action_view_zoom_fit_->setCheckable(true);
-       action_view_zoom_fit_->setText(tr("Zoom to &Fit"));
-       action_view_zoom_fit_->setIcon(QIcon::fromTheme("zoom-fit",
-               QIcon(":/icons/zoom-fit.png")));
-       action_view_zoom_fit_->setShortcut(QKeySequence(Qt::Key_F));
-       action_view_zoom_fit_->setObjectName(
-               QString::fromUtf8("actionViewZoomFit"));
-
-       action_view_zoom_one_to_one_->setText(tr("Zoom to O&ne-to-One"));
-       action_view_zoom_one_to_one_->setIcon(QIcon::fromTheme("zoom-original",
-               QIcon(":/icons/zoom-original.png")));
-       action_view_zoom_one_to_one_->setShortcut(QKeySequence(Qt::Key_O));
-       action_view_zoom_one_to_one_->setObjectName(
-               QString::fromUtf8("actionViewZoomOneToOne"));
-
        action_view_sticky_scrolling_->setCheckable(true);
        action_view_sticky_scrolling_->setChecked(true);
        action_view_sticky_scrolling_->setShortcut(QKeySequence(Qt::Key_S));
@@ -523,118 +211,17 @@ void MainWindow::setup_ui()
                QString::fromUtf8("actionViewColouredBg"));
        action_view_coloured_bg_->setText(tr("Use &coloured backgrounds"));
 
-       action_view_show_cursors_->setCheckable(true);
-       action_view_show_cursors_->setIcon(QIcon::fromTheme("show-cursors",
-               QIcon(":/icons/show-cursors.svg")));
-       action_view_show_cursors_->setShortcut(QKeySequence(Qt::Key_C));
-       action_view_show_cursors_->setObjectName(
-               QString::fromUtf8("actionViewShowCursors"));
-       action_view_show_cursors_->setText(tr("Show &Cursors"));
-
-#ifdef ENABLE_DECODE
-       menu_decoders_add_->setTitle(tr("&Add"));
-       connect(menu_decoders_add_, SIGNAL(decoder_selected(srd_decoder*)),
-               this, SLOT(add_decoder(srd_decoder*)));
-#endif
-
        action_about_->setObjectName(QString::fromUtf8("actionAbout"));
        action_about_->setText(tr("&About..."));
 
-       QMetaObject::connectSlotsByName(this);
-
-       // Setup the toolbar
-       main_bar_ = new toolbars::MainBar(session_, *this);
-
        // Set up the initial view
-       add_view(tr("Untitled"), pv::view::TraceView, session_);
-
-       // Populate the device list and select the initially selected device
-       update_device_list();
-
-       addToolBar(main_bar_);
+       shared_ptr<view::View> main_view =
+               add_view(tr("Untitled"), pv::view::TraceView, session_);
 
        // Set the title
        setWindowTitle(tr("PulseView"));
-
-       // Setup session_ events
-       connect(&session_, SIGNAL(capture_state_changed(int)), this,
-               SLOT(capture_state_changed(int)));
-       connect(&session_, SIGNAL(device_selected()), this,
-               SLOT(device_selected()));
-}
-
-void MainWindow::select_init_device()
-{
-       QSettings settings;
-       map<string, string> dev_info;
-       list<string> key_list;
-       shared_ptr<devices::HardwareDevice> device;
-
-       // Re-select last used device if possible but only if it's not demo
-       settings.beginGroup("Device");
-       key_list.push_back("vendor");
-       key_list.push_back("model");
-       key_list.push_back("version");
-       key_list.push_back("serial_num");
-       key_list.push_back("connection_id");
-
-       for (string key : key_list) {
-               const QString k = QString::fromStdString(key);
-               if (!settings.contains(k))
-                       continue;
-
-               const string value = settings.value(k).toString().toStdString();
-               if (!value.empty())
-                       dev_info.insert(std::make_pair(key, value));
-       }
-
-       if (dev_info.count("model") > 0)
-               if (dev_info.at("model").find("Demo device") == std::string::npos)
-                       device = device_manager_.find_device_from_info(dev_info);
-
-       // When we can't find a device similar to the one we used last
-       // time and there is at least one device aside from demo, use it
-       if (!device) {
-               for (shared_ptr<devices::HardwareDevice> dev : device_manager_.devices()) {
-                       dev_info = device_manager_.get_device_info(dev);
-
-                       if (dev_info.count("model") > 0)
-                               if (dev_info.at("model").find("Demo device") == std::string::npos) {
-                                       device = dev;
-                                       break;
-                               }
-               }
-       }
-
-       select_device(device);
-       update_device_list();
-
-       settings.endGroup();
 }
 
-void MainWindow::load_init_file(const std::string &file_name,
-       const std::string &format)
-{
-       shared_ptr<InputFormat> input_format;
-
-       if (!format.empty()) {
-               const map<string, shared_ptr<InputFormat> > formats =
-                       device_manager_.context()->input_formats();
-               const auto iter = find_if(formats.begin(), formats.end(),
-                       [&](const pair<string, shared_ptr<InputFormat> > f) {
-                               return f.first == format; });
-               if (iter == formats.end()) {
-                       cerr << "Unexpected input format: " << format << endl;
-                       return;
-               }
-
-               input_format = (*iter).second;
-       }
-
-       load_file(QString::fromStdString(file_name), input_format);
-}
-
-
 void MainWindow::save_ui_settings()
 {
        QSettings settings;
@@ -685,66 +272,12 @@ void MainWindow::restore_ui_settings()
        settings.endGroup();
 }
 
-void MainWindow::session_error(
-       const QString text, const QString info_text)
-{
-       QMetaObject::invokeMethod(this, "show_session_error",
-               Qt::QueuedConnection, Q_ARG(QString, text),
-               Q_ARG(QString, info_text));
-}
-
-void MainWindow::update_device_list()
-{
-       main_bar_->update_device_list();
-}
-
-void MainWindow::load_file(QString file_name,
-       std::shared_ptr<sigrok::InputFormat> format,
-       const std::map<std::string, Glib::VariantBase> &options)
-{
-       const QString errorMessage(
-               QString("Failed to load file %1").arg(file_name));
-
-       try {
-               if (format)
-                       session_.set_device(shared_ptr<devices::Device>(
-                               new devices::InputFile(
-                                       device_manager_.context(),
-                                       file_name.toStdString(),
-                                       format, options)));
-               else
-                       session_.set_device(shared_ptr<devices::Device>(
-                               new devices::SessionFile(
-                                       device_manager_.context(),
-                                       file_name.toStdString())));
-       } catch (Error e) {
-               show_session_error(tr("Failed to load ") + file_name, e.what());
-               session_.set_default_device();
-               update_device_list();
-               return;
-       }
-
-       update_device_list();
-
-       session_.start_capture([&, errorMessage](QString infoMessage) {
-               session_error(errorMessage, infoMessage); });
-}
-
 void MainWindow::closeEvent(QCloseEvent *event)
 {
        save_ui_settings();
        event->accept();
 }
 
-void MainWindow::keyReleaseEvent(QKeyEvent *event)
-{
-       if (event->key() == Qt::Key_Alt) {
-               menuBar()->setHidden(!menuBar()->isHidden());
-               menuBar()->setFocus();
-       }
-       QMainWindow::keyReleaseEvent(event);
-}
-
 QMenu* MainWindow::createPopupMenu()
 {
        return nullptr;
@@ -761,94 +294,6 @@ bool MainWindow::restoreState(const QByteArray &state, int version)
        return false;
 }
 
-void MainWindow::show_session_error(
-       const QString text, const QString info_text)
-{
-       QMessageBox msg(this);
-       msg.setText(text);
-       msg.setInformativeText(info_text);
-       msg.setStandardButtons(QMessageBox::Ok);
-       msg.setIcon(QMessageBox::Warning);
-       msg.exec();
-}
-
-void MainWindow::on_actionOpen_triggered()
-{
-       QSettings settings;
-       const QString dir = settings.value(SettingOpenDirectory).toString();
-
-       // Show the dialog
-       const QString file_name = QFileDialog::getOpenFileName(
-               this, tr("Open File"), dir, tr(
-                       "Sigrok Sessions (*.sr);;"
-                       "All Files (*.*)"));
-
-       if (!file_name.isEmpty()) {
-               load_file(file_name);
-
-               const QString abs_path = QFileInfo(file_name).absolutePath();
-               settings.setValue(SettingOpenDirectory, abs_path);
-       }
-}
-
-void MainWindow::on_actionSaveAs_triggered()
-{
-       export_file(device_manager_.context()->output_formats()["srzip"]);
-}
-
-void MainWindow::on_actionSaveSelectionAs_triggered()
-{
-       export_file(device_manager_.context()->output_formats()["srzip"], true);
-}
-
-void MainWindow::on_actionConnect_triggered()
-{
-       // Stop any currently running capture session
-       session_.stop_capture();
-
-       dialogs::Connect dlg(this, device_manager_);
-
-       // If the user selected a device, select it in the device list. Select the
-       // current device otherwise.
-       if (dlg.exec())
-               select_device(dlg.get_selected_device());
-
-       update_device_list();
-}
-
-void MainWindow::on_actionQuit_triggered()
-{
-       close();
-}
-
-void MainWindow::on_actionViewZoomIn_triggered()
-{
-       shared_ptr<pv::view::View> view = get_active_view();
-       if (view)
-               view->zoom(1);
-}
-
-void MainWindow::on_actionViewZoomOut_triggered()
-{
-       shared_ptr<pv::view::View> view = get_active_view();
-       if (view)
-               view->zoom(-1);
-}
-
-void MainWindow::on_actionViewZoomFit_triggered()
-{
-       shared_ptr<pv::view::View> view = get_active_view();
-       if (view)
-               view->zoom_fit(action_view_zoom_fit_->isChecked());
-}
-
-void MainWindow::on_actionViewZoomOneToOne_triggered()
-{
-       shared_ptr<pv::view::View> view = get_active_view();
-       if (view)
-               view->zoom_one_to_one();
-}
-
 void MainWindow::on_actionViewStickyScrolling_triggered()
 {
        shared_ptr<pv::view::View> view = get_active_view();
@@ -863,62 +308,10 @@ void MainWindow::on_actionViewColouredBg_triggered()
                view->enable_coloured_bg(action_view_coloured_bg_->isChecked());
 }
 
-void MainWindow::on_actionViewShowCursors_triggered()
-{
-       shared_ptr<pv::view::View> view = get_active_view();
-       if (!view)
-               return;
-
-       const bool show = !view->cursors_shown();
-       if (show)
-               view->centre_cursors();
-
-       view->show_cursors(show);
-}
-
 void MainWindow::on_actionAbout_triggered()
 {
        dialogs::About dlg(device_manager_.context(), this);
        dlg.exec();
 }
 
-void MainWindow::sticky_scrolling_changed(bool state)
-{
-       action_view_sticky_scrolling_->setChecked(state);
-}
-
-void MainWindow::always_zoom_to_fit_changed(bool state)
-{
-       action_view_zoom_fit_->setChecked(state);
-}
-
-void MainWindow::add_decoder(srd_decoder *decoder)
-{
-#ifdef ENABLE_DECODE
-       assert(decoder);
-       session_.add_decoder(decoder);
-#else
-       (void)decoder;
-#endif
-}
-
-void MainWindow::capture_state_changed(int state)
-{
-       main_bar_->set_capture_state((pv::Session::capture_state)state);
-}
-
-void MainWindow::device_selected()
-{
-       // Set the title to include the device/file name
-       const shared_ptr<devices::Device> device = session_.device();
-
-       if (!device) {
-               main_bar_->reset_device_selector();
-               return;
-       }
-
-       const string display_name = device->display_name(device_manager_);
-       setWindowTitle(tr("%1 - PulseView").arg(display_name.c_str()));
-}
-
 } // namespace pv
index 618a10e0fb51b1097a2e3b91216dd0c46a5778ad..485b7b6df18243e8a7aa2085290639b86e6268e5 100644 (file)
@@ -25,8 +25,6 @@
 #include <map>
 #include <memory>
 
-#include <glibmm/variant.h>
-
 #include <QMainWindow>
 
 #include "session.hpp"
@@ -36,11 +34,6 @@ struct srd_decoder;
 
 class QVBoxLayout;
 
-namespace sigrok {
-class InputFormat;
-class OutputFormat;
-}
-
 namespace pv {
 
 class DeviceManager;
@@ -64,19 +57,6 @@ class MainWindow : public QMainWindow
 {
        Q_OBJECT
 
-private:
-       /**
-        * Name of the setting used to remember the directory
-        * containing the last file that was opened.
-        */
-       static const char *SettingOpenDirectory;
-
-       /**
-        * Name of the setting used to remember the directory
-        * containing the last file that was saved.
-        */
-       static const char *SettingSaveDirectory;
-
 public:
        explicit MainWindow(DeviceManager &device_manager,
                std::string open_file_name = std::string(),
@@ -85,109 +65,36 @@ public:
 
        ~MainWindow();
 
-       QAction* action_open() const;
-       QAction* action_save_as() const;
-       QAction* action_save_selection_as() const;
-       QAction* action_connect() const;
-       QAction* action_quit() const;
-       QAction* action_view_zoom_in() const;
-       QAction* action_view_zoom_out() const;
-       QAction* action_view_zoom_fit() const;
-       QAction* action_view_zoom_one_to_one() const;
        QAction* action_view_sticky_scrolling() const;
        QAction* action_view_coloured_bg() const;
-       QAction* action_view_show_cursors() const;
        QAction* action_about() const;
 
-#ifdef ENABLE_DECODE
-       QMenu* menu_decoder_add() const;
-#endif
-
        std::shared_ptr<pv::view::View> get_active_view() const;
 
        std::shared_ptr<pv::view::View> add_view(const QString &title,
                view::ViewType type, Session &session);
 
-       void run_stop();
-
-       void select_device(std::shared_ptr<devices::Device> device);
-
-public Q_SLOTS:
-       void export_file(std::shared_ptr<sigrok::OutputFormat> format,
-               bool selection_only = false);
-       void import_file(std::shared_ptr<sigrok::InputFormat> format);
-
 private:
        void setup_ui();
 
-       void select_init_device();
-
-       void load_init_file(const std::string &file_name,
-               const std::string &format);
-
        void save_ui_settings();
 
        void restore_ui_settings();
 
-       void session_error(const QString text, const QString info_text);
-
-       /**
-        * Updates the device list in the toolbar
-        */
-       void update_device_list();      
-
-       void load_file(QString file_name,
-               std::shared_ptr<sigrok::InputFormat> format = nullptr,
-               const std::map<std::string, Glib::VariantBase> &options =
-                       std::map<std::string, Glib::VariantBase>());
-
-       void save_selection_to_file();
-
 private:
        void closeEvent(QCloseEvent *event);
 
-       void keyReleaseEvent(QKeyEvent *event);
-
        virtual QMenu* createPopupMenu();
 
        virtual bool restoreState(const QByteArray &state, int version = 0);
 
 private Q_SLOTS:
-       void show_session_error(
-               const QString text, const QString info_text);
-
-       void on_actionOpen_triggered();
-       void on_actionSaveAs_triggered();
-       void on_actionSaveSelectionAs_triggered();
-       void on_actionQuit_triggered();
-
-       void on_actionConnect_triggered();
-
-       void on_actionViewZoomIn_triggered();
-
-       void on_actionViewZoomOut_triggered();
-
-       void on_actionViewZoomFit_triggered();
-
-       void on_actionViewZoomOneToOne_triggered();
-
        void on_actionViewStickyScrolling_triggered();
 
        void on_actionViewColouredBg_triggered();
 
-       void on_actionViewShowCursors_triggered();
-
        void on_actionAbout_triggered();
 
-       void add_decoder(srd_decoder *decoder);
-
-       void capture_state_changed(int state);
-       void device_selected();
-
-       void sticky_scrolling_changed(bool state);
-
-       void always_zoom_to_fit_changed(bool state);
-
 private:
        DeviceManager &device_manager_;
 
@@ -196,25 +103,11 @@ private:
        std::map< std::shared_ptr<QDockWidget>,
                std::shared_ptr<pv::view::View> > view_docks_;
 
-       toolbars::MainBar *main_bar_;
-
-       QAction *const action_open_;
-       QAction *const action_save_as_;
-       QAction *const action_save_selection_as_;
-       QAction *const action_connect_;
-       QAction *const action_quit_;
-       QAction *const action_view_zoom_in_;
-       QAction *const action_view_zoom_out_;
-       QAction *const action_view_zoom_fit_;
-       QAction *const action_view_zoom_one_to_one_;
+       std::string open_file_name_, open_file_format_;
+
        QAction *const action_view_sticky_scrolling_;
        QAction *const action_view_coloured_bg_;
-       QAction *const action_view_show_cursors_;
        QAction *const action_about_;
-
-#ifdef ENABLE_DECODE
-       QMenu *const menu_decoders_add_;
-#endif
 };
 
 } // namespace pv
index 74cb82dc99aae5b847ca5660bc56d953e931d168..fd428ed0ebacd0b5ebe628c2b9893c51101ef8b1 100644 (file)
@@ -45,6 +45,8 @@
 #include "devices/hardwaredevice.hpp"
 #include "devices/sessionfile.hpp"
 
+#include "toolbars/mainbar.hpp"
+
 #include "view/analogsignal.hpp"
 #include "view/decodetrace.hpp"
 #include "view/logicsignal.hpp"
@@ -131,6 +133,21 @@ shared_ptr<devices::Device> Session::device() const
        return device_;
 }
 
+std::shared_ptr<pv::view::View> Session::main_view() const
+{
+       return main_view_;
+}
+
+void Session::set_main_bar(std::shared_ptr<pv::toolbars::MainBar> main_bar)
+{
+       main_bar_ = main_bar;
+}
+
+shared_ptr<pv::toolbars::MainBar> Session::main_bar() const
+{
+       return main_bar_;
+}
+
 void Session::set_device(shared_ptr<devices::Device> device)
 {
        assert(device);
@@ -248,12 +265,23 @@ void Session::stop_capture()
 
 void Session::register_view(std::shared_ptr<pv::view::View> view)
 {
+       if (views_.empty()) {
+               main_view_ = view;
+       }
+
        views_.insert(view);
 }
 
 void Session::deregister_view(std::shared_ptr<pv::view::View> view)
 {
        views_.erase(view);
+
+       if (views_.empty()) {
+               main_view_.reset();
+
+               // Without a view there can be no main bar
+               main_bar_.reset();
+       }
 }
 
 double Session::get_samplerate() const
index a4fc352d1969fd527951820bbf75e37f8b2b991c..d6cfdd302c97815daec25bcf19b62e12008c96e2 100644 (file)
@@ -72,6 +72,10 @@ namespace devices {
 class Device;
 }
 
+namespace toolbars {
+class MainBar;
+}
+
 namespace view {
 class View;
 }
@@ -100,6 +104,12 @@ public:
 
        std::shared_ptr<devices::Device> device() const;
 
+       std::shared_ptr<pv::view::View> main_view() const;
+
+       void set_main_bar(std::shared_ptr<pv::toolbars::MainBar> main_bar);
+
+       std::shared_ptr<pv::toolbars::MainBar> main_bar() const;
+
        /**
         * Sets device instance that will be used in the next capture session.
         */
@@ -159,6 +169,9 @@ private:
        std::shared_ptr<devices::Device> device_;
 
        std::unordered_set< std::shared_ptr<pv::view::View> > views_;
+       std::shared_ptr<pv::view::View> main_view_;
+
+       std::shared_ptr<pv::toolbars::MainBar> main_bar_;
 
        mutable std::mutex sampling_mutex_; //!< Protects access to capture_state_.
        capture_state capture_state_;
index cca4d9d5bab2d38cae0af4eb65de58718ef7d38f..a76ffbaa5c4c684c36addbca278a416ff9e1bf04 100644 (file)
 
 #include <QAction>
 #include <QDebug>
+#include <QFileDialog>
 #include <QHelpEvent>
 #include <QMenu>
+#include <QMessageBox>
+#include <QSettings>
 #include <QToolTip>
 
 #include "mainbar.hpp"
 
+#include <boost/algorithm/string/join.hpp>
+
 #include <pv/devicemanager.hpp>
 #include <pv/devices/hardwaredevice.hpp>
+#include <pv/devices/inputfile.hpp>
+#include <pv/devices/sessionfile.hpp>
+#include <pv/dialogs/connect.hpp>
+#include <pv/dialogs/inputoutputoptions.hpp>
+#include <pv/dialogs/storeprogress.hpp>
 #include <pv/mainwindow.hpp>
 #include <pv/popups/deviceoptions.hpp>
 #include <pv/popups/channels.hpp>
 #include <pv/util.hpp>
+#include <pv/view/view.hpp>
 #include <pv/widgets/exportmenu.hpp>
 #include <pv/widgets/importmenu.hpp>
+#ifdef ENABLE_DECODE
+#include <pv/widgets/decodermenu.hpp>
+#endif
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
 
 using std::back_inserter;
+using std::cerr;
 using std::copy;
+using std::endl;
 using std::list;
 using std::map;
 using std::max;
 using std::min;
+using std::pair;
 using std::shared_ptr;
 using std::string;
 using std::vector;
@@ -56,6 +73,9 @@ using sigrok::Capability;
 using sigrok::ConfigKey;
 using sigrok::Error;
 using sigrok::InputFormat;
+using sigrok::OutputFormat;
+
+using boost::algorithm::join;
 
 namespace pv {
 namespace toolbars {
@@ -64,12 +84,24 @@ const uint64_t MainBar::MinSampleCount = 100ULL;
 const uint64_t MainBar::MaxSampleCount = 1000000000000ULL;
 const uint64_t MainBar::DefaultSampleCount = 1000000;
 
-MainBar::MainBar(Session &session, MainWindow &main_window) :
+const char *MainBar::SettingOpenDirectory = "MainWindow/OpenDirectory";
+const char *MainBar::SettingSaveDirectory = "MainWindow/SaveDirectory";
+
+MainBar::MainBar(Session &session, MainWindow &main_window,
+       string open_file_name, string open_file_format) :
        QToolBar("Sampling Bar", &main_window),
+       action_open_(new QAction(this)),
+       action_save_as_(new QAction(this)),
+       action_save_selection_as_(new QAction(this)),
+       action_connect_(new QAction(this)),
+       action_view_zoom_in_(new QAction(this)),
+       action_view_zoom_out_(new QAction(this)),
+       action_view_zoom_fit_(new QAction(this)),
+       action_view_zoom_one_to_one_(new QAction(this)),
+       action_view_show_cursors_(new QAction(this)),
        session_(session),
-       main_window_(main_window),
-       device_selector_(this, session.device_manager(),
-               main_window.action_connect()),
+       device_selector_(&main_window, session.device_manager(),
+               action_connect_),
        configure_button_(this),
        configure_button_action_(nullptr),
        channels_button_(this),
@@ -85,6 +117,9 @@ MainBar::MainBar(Session &session, MainWindow &main_window) :
        run_stop_button_(this),
        run_stop_button_action_(nullptr),
        menu_button_(this)
+#ifdef ENABLE_DECODE
+       , menu_decoders_add_(new pv::widgets::DecoderMenu(this, true))
+#endif
 {
        setObjectName(QString::fromUtf8("MainBar"));
 
@@ -92,38 +127,111 @@ MainBar::MainBar(Session &session, MainWindow &main_window) :
        setFloatable(false);
        setContextMenuPolicy(Qt::PreventContextMenu);
 
+       // Actions
+       action_open_->setText(tr("&Open..."));
+       action_open_->setIcon(QIcon::fromTheme("document-open",
+               QIcon(":/icons/document-open.png")));
+       action_open_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
+       action_open_->setObjectName(QString::fromUtf8("actionOpen"));
+
+       action_save_as_->setText(tr("&Save As..."));
+       action_save_as_->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+       action_save_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+       action_save_as_->setObjectName(QString::fromUtf8("actionSaveAs"));
+
+       action_save_selection_as_->setText(tr("Save Selected &Range As..."));
+       action_save_selection_as_->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+       action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
+       action_save_selection_as_->setObjectName(QString::fromUtf8("actionSaveSelectionAs"));
+
+       widgets::ExportMenu *menu_file_export = new widgets::ExportMenu(this,
+               session.device_manager().context());
+       menu_file_export->setTitle(tr("&Export"));
+       connect(menu_file_export,
+               SIGNAL(format_selected(std::shared_ptr<sigrok::OutputFormat>)),
+               this, SLOT(export_file(std::shared_ptr<sigrok::OutputFormat>)));
+
+       widgets::ImportMenu *menu_file_import = new widgets::ImportMenu(this,
+               session.device_manager().context());
+       menu_file_import->setTitle(tr("&Import"));
+       connect(menu_file_import,
+               SIGNAL(format_selected(std::shared_ptr<sigrok::InputFormat>)),
+               this, SLOT(import_file(std::shared_ptr<sigrok::InputFormat>)));
+
+       action_connect_->setText(tr("&Connect to Device..."));
+       action_connect_->setObjectName(QString::fromUtf8("actionConnect"));
+
+       action_view_zoom_in_->setText(tr("Zoom &In"));
+       action_view_zoom_in_->setIcon(QIcon::fromTheme("zoom-in",
+               QIcon(":/icons/zoom-in.png")));
+       // simply using Qt::Key_Plus shows no + in the menu
+       action_view_zoom_in_->setShortcut(QKeySequence::ZoomIn);
+       action_view_zoom_in_->setObjectName(
+               QString::fromUtf8("actionViewZoomIn"));
+
+       action_view_zoom_out_->setText(tr("Zoom &Out"));
+       action_view_zoom_out_->setIcon(QIcon::fromTheme("zoom-out",
+               QIcon(":/icons/zoom-out.png")));
+       action_view_zoom_out_->setShortcut(QKeySequence::ZoomOut);
+       action_view_zoom_out_->setObjectName(
+               QString::fromUtf8("actionViewZoomOut"));
+
+       action_view_zoom_fit_->setCheckable(true);
+       action_view_zoom_fit_->setText(tr("Zoom to &Fit"));
+       action_view_zoom_fit_->setIcon(QIcon::fromTheme("zoom-fit",
+               QIcon(":/icons/zoom-fit.png")));
+       action_view_zoom_fit_->setShortcut(QKeySequence(Qt::Key_F));
+       action_view_zoom_fit_->setObjectName(
+               QString::fromUtf8("actionViewZoomFit"));
+
+       action_view_zoom_one_to_one_->setText(tr("Zoom to O&ne-to-One"));
+       action_view_zoom_one_to_one_->setIcon(QIcon::fromTheme("zoom-original",
+               QIcon(":/icons/zoom-original.png")));
+       action_view_zoom_one_to_one_->setShortcut(QKeySequence(Qt::Key_O));
+       action_view_zoom_one_to_one_->setObjectName(
+               QString::fromUtf8("actionViewZoomOneToOne"));
+
+       action_view_show_cursors_->setCheckable(true);
+       action_view_show_cursors_->setIcon(QIcon::fromTheme("show-cursors",
+               QIcon(":/icons/show-cursors.svg")));
+       action_view_show_cursors_->setShortcut(QKeySequence(Qt::Key_C));
+       action_view_show_cursors_->setObjectName(
+               QString::fromUtf8("actionViewShowCursors"));
+       action_view_show_cursors_->setText(tr("Show &Cursors"));
+
        // Open button
        QToolButton *const open_button = new QToolButton(this);
 
        widgets::ImportMenu *import_menu = new widgets::ImportMenu(this,
-               session.device_manager().context(),
-               main_window.action_open());
+               session.device_manager().context(), action_open_);
        connect(import_menu,
                SIGNAL(format_selected(std::shared_ptr<sigrok::InputFormat>)),
-               &main_window_,
+               &main_window,
                SLOT(import_file(std::shared_ptr<sigrok::InputFormat>)));
 
        open_button->setMenu(import_menu);
-       open_button->setDefaultAction(main_window.action_open());
+       open_button->setDefaultAction(action_open_);
        open_button->setPopupMode(QToolButton::MenuButtonPopup);
 
        // Save button
        QToolButton *const save_button = new QToolButton(this);
 
        vector<QAction *> open_actions;
-       open_actions.push_back(main_window.action_save_as());
-       open_actions.push_back(main_window.action_save_selection_as());
+       open_actions.push_back(action_save_as_);
+       open_actions.push_back(action_save_selection_as_);
 
        widgets::ExportMenu *export_menu = new widgets::ExportMenu(this,
                session.device_manager().context(),
                open_actions);
        connect(export_menu,
                SIGNAL(format_selected(std::shared_ptr<sigrok::OutputFormat>)),
-               &main_window_,
+               &main_window,
                SLOT(export_file(std::shared_ptr<sigrok::OutputFormat>)));
 
        save_button->setMenu(export_menu);
-       save_button->setDefaultAction(main_window.action_save_as());
+       save_button->setDefaultAction(action_save_as_);
        save_button->setPopupMode(QToolButton::MenuButtonPopup);
 
        // Device selector menu
@@ -132,47 +240,27 @@ MainBar::MainBar(Session &session, MainWindow &main_window) :
 
        // 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*)));
+
        QToolButton *add_decoder_button = new QToolButton(this);
        add_decoder_button->setIcon(QIcon::fromTheme("add-decoder",
                QIcon(":/icons/add-decoder.svg")));
        add_decoder_button->setPopupMode(QToolButton::InstantPopup);
-       add_decoder_button->setMenu(main_window_.menu_decoder_add());
+       add_decoder_button->setMenu(menu_decoders_add_);
 #endif
 
-       // Setup the burger menu
-       QMenu *const menu = new QMenu(this);
-
-       QMenu *const menu_view = new QMenu;
-       menu_view->setTitle(tr("&View"));
-       menu_view->addAction(main_window.action_view_sticky_scrolling());
-       menu_view->addSeparator();
-       menu_view->addAction(main_window.action_view_coloured_bg());
-
-       QMenu *const menu_help = new QMenu;
-       menu_help->setTitle(tr("&Help"));
-       menu_help->addAction(main_window.action_about());
-
-       menu->addAction(menu_view->menuAction());
-       menu->addSeparator();
-       menu->addAction(menu_help->menuAction());
-       menu->addSeparator();
-       menu->addAction(main_window.action_quit());
-
-       menu_button_.setMenu(menu);
-       menu_button_.setPopupMode(QToolButton::InstantPopup);
-       menu_button_.setIcon(QIcon::fromTheme("menu",
-               QIcon(":/icons/menu.svg")));
-
        // Setup the toolbar
        addWidget(open_button);
        addWidget(save_button);
        addSeparator();
-       addAction(main_window.action_view_zoom_in());
-       addAction(main_window.action_view_zoom_out());
-       addAction(main_window.action_view_zoom_fit());
-       addAction(main_window.action_view_zoom_one_to_one());
+       addAction(action_view_zoom_in_);
+       addAction(action_view_zoom_out_);
+       addAction(action_view_zoom_fit_);
+       addAction(action_view_zoom_one_to_one_);
        addSeparator();
-       addAction(main_window.action_view_show_cursors());
+       addAction(action_view_show_cursors_);
        addSeparator();
 
        connect(&run_stop_button_, SIGNAL(clicked()),
@@ -213,6 +301,25 @@ MainBar::MainBar(Session &session, MainWindow &main_window) :
 
        sample_count_.installEventFilter(this);
        sample_rate_.installEventFilter(this);
+
+       QMetaObject::connectSlotsByName(this);
+
+       // Setup session_ events
+       connect(&session_, SIGNAL(capture_state_changed(int)), this,
+               SLOT(capture_state_changed(int)));
+       connect(&session_, SIGNAL(device_selected()), this,
+               SLOT(device_selected()));
+
+       // Figure out which file/device to use
+       if (open_file_name.empty())
+               select_init_device();
+       else
+               load_init_file(open_file_name, open_file_format);
+}
+
+Session &MainBar::session(void) const
+{
+       return session_;
 }
 
 void MainBar::update_device_list()
@@ -253,6 +360,191 @@ void MainBar::reset_device_selector()
        device_selector_.reset();
 }
 
+QAction* MainBar::action_open() const
+{
+       return action_open_;
+}
+
+QAction* MainBar::action_save_as() const
+{
+       return action_save_as_;
+}
+
+QAction* MainBar::action_save_selection_as() const
+{
+       return action_save_selection_as_;
+}
+
+QAction* MainBar::action_connect() const
+{
+       return action_connect_;
+}
+
+QAction* MainBar::action_view_zoom_in() const
+{
+       return action_view_zoom_in_;
+}
+
+QAction* MainBar::action_view_zoom_out() const
+{
+       return action_view_zoom_out_;
+}
+
+QAction* MainBar::action_view_zoom_fit() const
+{
+       return action_view_zoom_fit_;
+}
+
+QAction* MainBar::action_view_zoom_one_to_one() const
+{
+       return action_view_zoom_one_to_one_;
+}
+
+QAction* MainBar::action_view_show_cursors() const
+{
+       return action_view_show_cursors_;
+}
+
+void MainBar::run_stop()
+{
+       switch (session_.get_capture_state()) {
+       case Session::Stopped:
+               session_.start_capture([&](QString message) {
+                       session_error("Capture failed", message); });
+               break;
+       case Session::AwaitingTrigger:
+       case Session::Running:
+               session_.stop_capture();
+               break;
+       }
+}
+
+void MainBar::select_device(shared_ptr<devices::Device> device)
+{
+       try {
+               if (device)
+                       session_.set_device(device);
+               else
+                       session_.set_default_device();
+       } catch (const QString &e) {
+               QMessageBox msg(this);
+               msg.setText(e);
+               msg.setInformativeText(tr("Failed to Select Device"));
+               msg.setStandardButtons(QMessageBox::Ok);
+               msg.setIcon(QMessageBox::Warning);
+               msg.exec();
+       }
+}
+
+void MainBar::select_init_device()
+{
+       QSettings settings;
+       map<string, string> dev_info;
+       list<string> key_list;
+       shared_ptr<devices::HardwareDevice> device;
+
+       DeviceManager& device_manager = session_.device_manager();
+
+       // Re-select last used device if possible but only if it's not demo
+       settings.beginGroup("Device");
+       key_list.push_back("vendor");
+       key_list.push_back("model");
+       key_list.push_back("version");
+       key_list.push_back("serial_num");
+       key_list.push_back("connection_id");
+
+       for (string key : key_list) {
+               const QString k = QString::fromStdString(key);
+               if (!settings.contains(k))
+                       continue;
+
+               const string value = settings.value(k).toString().toStdString();
+               if (!value.empty())
+                       dev_info.insert(std::make_pair(key, value));
+       }
+
+       if (dev_info.count("model") > 0)
+               if (dev_info.at("model").find("Demo device") == std::string::npos)
+                       device = device_manager.find_device_from_info(dev_info);
+
+       // When we can't find a device similar to the one we used last
+       // time and there is at least one device aside from demo, use it
+       if (!device) {
+               for (shared_ptr<devices::HardwareDevice> dev : device_manager.devices()) {
+                       dev_info = device_manager.get_device_info(dev);
+
+                       if (dev_info.count("model") > 0)
+                               if (dev_info.at("model").find("Demo device") == std::string::npos) {
+                                       device = dev;
+                                       break;
+                               }
+               }
+       }
+
+       select_device(device);
+       update_device_list();
+
+       settings.endGroup();
+}
+
+void MainBar::load_file(QString file_name,
+       std::shared_ptr<sigrok::InputFormat> format,
+       const std::map<std::string, Glib::VariantBase> &options)
+{
+       DeviceManager& device_manager = session_.device_manager();
+
+       const QString errorMessage(
+               QString("Failed to load file %1").arg(file_name));
+
+       try {
+               if (format)
+                       session_.set_device(shared_ptr<devices::Device>(
+                               new devices::InputFile(
+                                       device_manager.context(),
+                                       file_name.toStdString(),
+                                       format, options)));
+               else
+                       session_.set_device(shared_ptr<devices::Device>(
+                               new devices::SessionFile(
+                                       device_manager.context(),
+                                       file_name.toStdString())));
+       } catch (Error e) {
+               show_session_error(tr("Failed to load ") + file_name, e.what());
+               session_.set_default_device();
+               update_device_list();
+               return;
+       }
+
+       update_device_list();
+
+       session_.start_capture([&, errorMessage](QString infoMessage) {
+               session_error(errorMessage, infoMessage); });
+}
+
+void MainBar::load_init_file(const std::string &file_name,
+       const std::string &format)
+{
+       shared_ptr<InputFormat> input_format;
+
+       DeviceManager& device_manager = session_.device_manager();
+
+       if (!format.empty()) {
+               const map<string, shared_ptr<InputFormat> > formats =
+                       device_manager.context()->input_formats();
+               const auto iter = find_if(formats.begin(), formats.end(),
+                       [&](const pair<string, shared_ptr<InputFormat> > f) {
+                               return f.first == format; });
+               if (iter == formats.end()) {
+                       cerr << "Unexpected input format: " << format << endl;
+                       return;
+               }
+
+               input_format = (*iter).second;
+       }
+
+       load_file(QString::fromStdString(file_name), input_format);
+}
+
 void MainBar::update_sample_rate_selector()
 {
        Glib::VariantContainerBase gvar_dict;
@@ -453,6 +745,36 @@ void MainBar::update_device_config_widgets()
        update_sample_rate_selector();
 }
 
+void MainBar::commit_sample_rate()
+{
+       uint64_t sample_rate = 0;
+
+       const shared_ptr<devices::Device> device =
+               device_selector_.selected_device();
+       if (!device)
+               return;
+
+       const shared_ptr<sigrok::Device> sr_dev = device->device();
+
+       sample_rate = sample_rate_.value();
+       if (sample_rate == 0)
+               return;
+
+       try {
+               sr_dev->config_set(ConfigKey::SAMPLERATE,
+                       Glib::Variant<guint64>::create(sample_rate));
+               update_sample_rate_selector();
+       } catch (Error error) {
+               qDebug() << "Failed to configure samplerate.";
+               return;
+       }
+
+       // Devices with built-in memory might impose limits on certain
+       // configurations, so let's check what sample count the driver
+       // lets us use now.
+       update_sample_count_selector();
+}
+
 void MainBar::commit_sample_count()
 {
        uint64_t sample_count = 0;
@@ -482,43 +804,163 @@ void MainBar::commit_sample_count()
        update_sample_rate_selector();
 }
 
-void MainBar::commit_sample_rate()
+void MainBar::session_error(const QString text, const QString info_text)
 {
-       uint64_t sample_rate = 0;
+       QMetaObject::invokeMethod(this, "show_session_error",
+               Qt::QueuedConnection, Q_ARG(QString, text),
+               Q_ARG(QString, info_text));
+}
 
-       const shared_ptr<devices::Device> device =
-               device_selector_.selected_device();
-       if (!device)
-               return;
+void MainBar::show_session_error(const QString text, const QString info_text)
+{
+       QMessageBox msg(this);
+       msg.setText(text);
+       msg.setInformativeText(info_text);
+       msg.setStandardButtons(QMessageBox::Ok);
+       msg.setIcon(QMessageBox::Warning);
+       msg.exec();
+}
 
-       const shared_ptr<sigrok::Device> sr_dev = device->device();
+void MainBar::capture_state_changed(int state)
+{
+       set_capture_state((pv::Session::capture_state)state);
+}
 
-       sample_rate = sample_rate_.value();
-       if (sample_rate == 0)
+void MainBar::add_decoder(srd_decoder *decoder)
+{
+#ifdef ENABLE_DECODE
+       assert(decoder);
+       session_.add_decoder(decoder);
+#else
+       (void)decoder;
+#endif
+}
+
+void MainBar::export_file(shared_ptr<OutputFormat> format,
+       bool selection_only)
+{
+       using pv::dialogs::StoreProgress;
+
+       // Stop any currently running capture session
+       session_.stop_capture();
+
+       QSettings settings;
+       const QString dir = settings.value(SettingSaveDirectory).toString();
+
+       std::pair<uint64_t, uint64_t> sample_range;
+
+       // Selection only? Verify that the cursors are active and fetch their values
+       if (selection_only) {
+               if (!session_.main_view()->cursors()->enabled()) {
+                       show_session_error(tr("Missing Cursors"), tr("You need to set the " \
+                                       "cursors before you can save the data enclosed by them " \
+                                       "to a session file (e.g. using ALT-V - Show Cursors)."));
+                       return;
+               }
+
+               const double samplerate = session_.get_samplerate();
+
+               const pv::util::Timestamp& start_time = session_.main_view()->cursors()->first()->time();
+               const pv::util::Timestamp& end_time = session_.main_view()->cursors()->second()->time();
+
+               const uint64_t start_sample =
+                       std::max((double)0, start_time.convert_to<double>() * samplerate);
+               const uint64_t end_sample = end_time.convert_to<double>() * samplerate;
+
+               sample_range = std::make_pair(start_sample, end_sample);
+       } else {
+               sample_range = std::make_pair(0, 0);
+       }
+
+       // Construct the filter
+       const vector<string> exts = format->extensions();
+       QString filter = tr("%1 files ").arg(
+               QString::fromStdString(format->description()));
+
+       if (exts.empty())
+               filter += "(*.*)";
+       else
+               filter += QString("(*.%1);;%2 (*.*)").arg(
+                       QString::fromStdString(join(exts, ", *.")),
+                       tr("All Files"));
+
+       // Show the file dialog
+       const QString file_name = QFileDialog::getSaveFileName(
+               this, tr("Save File"), dir, filter);
+
+       if (file_name.isEmpty())
                return;
 
-       try {
-               sr_dev->config_set(ConfigKey::SAMPLERATE,
-                       Glib::Variant<guint64>::create(sample_rate));
-               update_sample_rate_selector();
-       } catch (Error error) {
-               qDebug() << "Failed to configure samplerate.";
+       const QString abs_path = QFileInfo(file_name).absolutePath();
+       settings.setValue(SettingSaveDirectory, abs_path);
+
+       // Show the options dialog
+       map<string, Glib::VariantBase> options;
+       if (!format->options().empty()) {
+               dialogs::InputOutputOptions dlg(
+                       tr("Export %1").arg(QString::fromStdString(
+                               format->description())),
+                       format->options(), this);
+               if (!dlg.exec())
+                       return;
+               options = dlg.options();
+       }
+
+       StoreProgress *dlg = new StoreProgress(file_name, format, options,
+               sample_range, session_, this);
+       dlg->run();
+}
+
+void MainBar::import_file(shared_ptr<InputFormat> format)
+{
+       assert(format);
+
+       QSettings settings;
+       const QString dir = settings.value(SettingOpenDirectory).toString();
+
+       // Construct the filter
+       const vector<string> exts = format->extensions();
+       const QString filter = exts.empty() ? "" :
+               tr("%1 files (*.%2)").arg(
+                       QString::fromStdString(format->description()),
+                       QString::fromStdString(join(exts, ", *.")));
+
+       // Show the file dialog
+       const QString file_name = QFileDialog::getOpenFileName(
+               this, tr("Import File"), dir, tr(
+                       "%1 files (*.*);;All Files (*.*)").arg(
+                       QString::fromStdString(format->description())));
+
+       if (file_name.isEmpty())
                return;
+
+       // Show the options dialog
+       map<string, Glib::VariantBase> options;
+       if (!format->options().empty()) {
+               dialogs::InputOutputOptions dlg(
+                       tr("Import %1").arg(QString::fromStdString(
+                               format->description())),
+                       format->options(), this);
+               if (!dlg.exec())
+                       return;
+               options = dlg.options();
        }
 
-       // Devices with built-in memory might impose limits on certain
-       // configurations, so let's check what sample count the driver
-       // lets us use now.
-       update_sample_count_selector();
+       load_file(file_name, format, options);
+
+       const QString abs_path = QFileInfo(file_name).absolutePath();
+       settings.setValue(SettingOpenDirectory, abs_path);
 }
 
 void MainBar::on_device_selected()
 {
        shared_ptr<devices::Device> device = device_selector_.selected_device();
-       if (!device)
+       if (!device) {
+               reset_device_selector();
                return;
+       }
 
-       main_window_.select_device(device);
+       select_device(device);
 
        update_device_config_widgets();
 }
@@ -539,7 +981,7 @@ void MainBar::on_run_stop()
 {
        commit_sample_count();
        commit_sample_rate();   
-       main_window_.run_stop();
+       run_stop();
 }
 
 void MainBar::on_config_changed()
@@ -548,6 +990,79 @@ void MainBar::on_config_changed()
        commit_sample_rate();   
 }
 
+void MainBar::on_actionOpen_triggered()
+{
+       QSettings settings;
+       const QString dir = settings.value(SettingOpenDirectory).toString();
+
+       // Show the dialog
+       const QString file_name = QFileDialog::getOpenFileName(
+               this, tr("Open File"), dir, tr(
+                       "Sigrok Sessions (*.sr);;"
+                       "All Files (*.*)"));
+
+       if (!file_name.isEmpty()) {
+               load_file(file_name);
+
+               const QString abs_path = QFileInfo(file_name).absolutePath();
+               settings.setValue(SettingOpenDirectory, abs_path);
+       }
+}
+
+void MainBar::on_actionSaveAs_triggered()
+{
+       export_file(session_.device_manager().context()->output_formats()["srzip"]);
+}
+
+void MainBar::on_actionSaveSelectionAs_triggered()
+{
+       export_file(session_.device_manager().context()->output_formats()["srzip"], true);
+}
+
+void MainBar::on_actionConnect_triggered()
+{
+       // Stop any currently running capture session
+       session_.stop_capture();
+
+       dialogs::Connect dlg(this, session_.device_manager());
+
+       // If the user selected a device, select it in the device list. Select the
+       // current device otherwise.
+       if (dlg.exec())
+               select_device(dlg.get_selected_device());
+
+       update_device_list();
+}
+
+void MainBar::on_actionViewZoomIn_triggered()
+{
+       session_.main_view()->zoom(1);
+}
+
+void MainBar::on_actionViewZoomOut_triggered()
+{
+       session_.main_view()->zoom(-1);
+}
+
+void MainBar::on_actionViewZoomFit_triggered()
+{
+       session_.main_view()->zoom_fit(action_view_zoom_fit_->isChecked());
+}
+
+void MainBar::on_actionViewZoomOneToOne_triggered()
+{
+       session_.main_view()->zoom_one_to_one();
+}
+
+void MainBar::on_actionViewShowCursors_triggered()
+{
+       const bool show = !session_.main_view()->cursors_shown();
+       if (show)
+               session_.main_view()->centre_cursors();
+
+       session_.main_view()->show_cursors(show);
+}
+
 bool MainBar::eventFilter(QObject *watched, QEvent *event)
 {
        if (sample_count_supported_ && (watched == &sample_count_ ||
index 9c4b1cd4b0e4a9f9484d00736109b75a8f8cf8ee..088efa2f73cb6a8597cd6b4ea517bbbe40409a39 100644 (file)
@@ -26,6 +26,8 @@
 #include <list>
 #include <memory>
 
+#include <glibmm/variant.h>
+
 #include <QComboBox>
 #include <QDoubleSpinBox>
 #include <QMenu>
@@ -40,6 +42,7 @@
 namespace sigrok {
 class Device;
 class InputFormat;
+class OutputFormat;
 }
 
 Q_DECLARE_METATYPE(std::shared_ptr<sigrok::Device>)
@@ -62,8 +65,24 @@ private:
        static const uint64_t MaxSampleCount;
        static const uint64_t DefaultSampleCount;
 
+       /**
+        * Name of the setting used to remember the directory
+        * containing the last file that was opened.
+        */
+       static const char *SettingOpenDirectory;
+
+       /**
+        * Name of the setting used to remember the directory
+        * containing the last file that was saved.
+        */
+       static const char *SettingSaveDirectory;
+
 public:
-       MainBar(Session &session, pv::MainWindow &main_window);
+       MainBar(Session &session, pv::MainWindow &main_window,
+               std::string open_file_name = std::string(),
+               std::string open_file_format = std::string());
+
+       Session &session(void) const;
 
        void update_device_list();
 
@@ -71,7 +90,34 @@ public:
 
        void reset_device_selector();
 
+       QAction* action_open() const;
+       QAction* action_save_as() const;
+       QAction* action_save_selection_as() const;
+       QAction* action_connect() const;
+       QAction* action_quit() const;
+       QAction* action_view_zoom_in() const;
+       QAction* action_view_zoom_out() const;
+       QAction* action_view_zoom_fit() const;
+       QAction* action_view_zoom_one_to_one() const;
+       QAction* action_view_show_cursors() const;
+
 private:
+       void run_stop();
+
+       void select_device(std::shared_ptr<devices::Device> device);
+
+       void select_init_device();
+
+       void load_file(QString file_name,
+               std::shared_ptr<sigrok::InputFormat> format = nullptr,
+               const std::map<std::string, Glib::VariantBase> &options =
+                       std::map<std::string, Glib::VariantBase>());
+
+       void load_init_file(const std::string &file_name,
+               const std::string &format);
+
+       void save_selection_to_file();
+
        void update_sample_rate_selector();
        void update_sample_rate_selector_value();
        void update_sample_count_selector();
@@ -79,7 +125,29 @@ private:
        void commit_sample_rate();
        void commit_sample_count();
 
+       void session_error(const QString text, const QString info_text);
+
+       QAction *const action_open_;
+       QAction *const action_save_as_;
+       QAction *const action_save_selection_as_;
+       QAction *const action_connect_;
+       QAction *const action_view_zoom_in_;
+       QAction *const action_view_zoom_out_;
+       QAction *const action_view_zoom_fit_;
+       QAction *const action_view_zoom_one_to_one_;
+       QAction *const action_view_show_cursors_;
+
 private Q_SLOTS:
+       void show_session_error(const QString text, const QString info_text);
+
+       void capture_state_changed(int state);
+
+       void add_decoder(srd_decoder *decoder);
+
+       void export_file(std::shared_ptr<sigrok::OutputFormat> format,
+               bool selection_only = false);
+       void import_file(std::shared_ptr<sigrok::InputFormat> format);
+
        void on_device_selected();
        void on_sample_count_changed();
        void on_sample_rate_changed();
@@ -87,12 +155,27 @@ private Q_SLOTS:
 
        void on_config_changed();
 
+       void on_actionOpen_triggered();
+       void on_actionSaveAs_triggered();
+       void on_actionSaveSelectionAs_triggered();
+
+       void on_actionConnect_triggered();
+
+       void on_actionViewZoomIn_triggered();
+
+       void on_actionViewZoomOut_triggered();
+
+       void on_actionViewZoomFit_triggered();
+
+       void on_actionViewZoomOneToOne_triggered();
+
+       void on_actionViewShowCursors_triggered();
+
 protected:
        bool eventFilter(QObject *watched, QEvent *event);
 
 private:
        Session &session_;
-       MainWindow &main_window_;
 
        pv::widgets::DeviceToolButton device_selector_;
 
@@ -116,6 +199,10 @@ private:
        QAction *run_stop_button_action_;
 
        QToolButton menu_button_;
+
+#ifdef ENABLE_DECODE
+       QMenu *const menu_decoders_add_;
+#endif
 };
 
 } // namespace toolbars