From: Soeren Apel Date: Fri, 26 Aug 2016 20:29:40 +0000 (+0200) Subject: Make the first view own the toolbar instead of the main window X-Git-Url: http://git.code-monkey.de/?p=pulseview.git;a=commitdiff_plain;h=0f8f8c180b32413177f3940ea6f216d1cbadf09b Make the first view own the toolbar instead of the main window 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. --- diff --git a/pv/mainwindow.cpp b/pv/mainwindow.cpp index b3377f7..f846325 100644 --- a/pv/mainwindow.cpp +++ b/pv/mainwindow.cpp @@ -27,66 +27,30 @@ #include #include -#include - #include #include -#include #include -#include -#include -#include -#include +#include #include -#include -#include #include -#include - #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 #include #include -#include #include -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"); 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 dock = entry.first; + + // Remove view from the dock widget's QMainWindow + QMainWindow *dock_main = dynamic_cast(dock->widget()); + dock_main->setCentralWidget(0); + + // Remove the QMainWindow dock->setWidget(0); + const std::shared_ptr 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 MainWindow::get_active_view() const { // If there's only one view, use it... @@ -242,15 +141,21 @@ shared_ptr MainWindow::add_view(const QString &title, { shared_ptr v; - if (type == pv::view::TraceView) - v = make_shared(session, this); - - if (v) { + if (type == pv::view::TraceView) { shared_ptr dock = make_shared(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(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 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 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 format, - bool selection_only) -{ - using pv::dialogs::StoreProgress; - - // Make sure there's a view selected to pull the data from - shared_ptr 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 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 main_bar = session.main_bar(); + if (!main_bar) { + main_bar = make_shared(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() * samplerate); - const uint64_t end_sample = end_time.convert_to() * samplerate; - - sample_range = std::make_pair(start_sample, end_sample); - } else { - sample_range = std::make_pair(0, 0); - } - - // Construct the filter - const vector 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 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 format) -{ - assert(format); - - QSettings settings; - const QString dir = settings.value(SettingOpenDirectory).toString(); - - // Construct the filter - const vector 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 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)), - this, SLOT(export_file(std::shared_ptr))); - - 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)), - this, SLOT(import_file(std::shared_ptr))); - - 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 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 dev_info; - list key_list; - shared_ptr 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 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 input_format; - - if (!format.empty()) { - const map > formats = - device_manager_.context()->input_formats(); - const auto iter = find_if(formats.begin(), formats.end(), - [&](const pair > 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 format, - const std::map &options) -{ - const QString errorMessage( - QString("Failed to load file %1").arg(file_name)); - - try { - if (format) - session_.set_device(shared_ptr( - new devices::InputFile( - device_manager_.context(), - file_name.toStdString(), - format, options))); - else - session_.set_device(shared_ptr( - 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 view = get_active_view(); - if (view) - view->zoom(1); -} - -void MainWindow::on_actionViewZoomOut_triggered() -{ - shared_ptr view = get_active_view(); - if (view) - view->zoom(-1); -} - -void MainWindow::on_actionViewZoomFit_triggered() -{ - shared_ptr view = get_active_view(); - if (view) - view->zoom_fit(action_view_zoom_fit_->isChecked()); -} - -void MainWindow::on_actionViewZoomOneToOne_triggered() -{ - shared_ptr view = get_active_view(); - if (view) - view->zoom_one_to_one(); -} - void MainWindow::on_actionViewStickyScrolling_triggered() { shared_ptr 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 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 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 diff --git a/pv/mainwindow.hpp b/pv/mainwindow.hpp index 618a10e..485b7b6 100644 --- a/pv/mainwindow.hpp +++ b/pv/mainwindow.hpp @@ -25,8 +25,6 @@ #include #include -#include - #include #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 get_active_view() const; std::shared_ptr add_view(const QString &title, view::ViewType type, Session &session); - void run_stop(); - - void select_device(std::shared_ptr device); - -public Q_SLOTS: - void export_file(std::shared_ptr format, - bool selection_only = false); - void import_file(std::shared_ptr 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 format = nullptr, - const std::map &options = - std::map()); - - 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, std::shared_ptr > 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 diff --git a/pv/session.cpp b/pv/session.cpp index 74cb82d..fd428ed 100644 --- a/pv/session.cpp +++ b/pv/session.cpp @@ -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 Session::device() const return device_; } +std::shared_ptr Session::main_view() const +{ + return main_view_; +} + +void Session::set_main_bar(std::shared_ptr main_bar) +{ + main_bar_ = main_bar; +} + +shared_ptr Session::main_bar() const +{ + return main_bar_; +} + void Session::set_device(shared_ptr device) { assert(device); @@ -248,12 +265,23 @@ void Session::stop_capture() void Session::register_view(std::shared_ptr view) { + if (views_.empty()) { + main_view_ = view; + } + views_.insert(view); } void Session::deregister_view(std::shared_ptr 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 diff --git a/pv/session.hpp b/pv/session.hpp index a4fc352..d6cfdd3 100644 --- a/pv/session.hpp +++ b/pv/session.hpp @@ -72,6 +72,10 @@ namespace devices { class Device; } +namespace toolbars { +class MainBar; +} + namespace view { class View; } @@ -100,6 +104,12 @@ public: std::shared_ptr device() const; + std::shared_ptr main_view() const; + + void set_main_bar(std::shared_ptr main_bar); + + std::shared_ptr main_bar() const; + /** * Sets device instance that will be used in the next capture session. */ @@ -159,6 +169,9 @@ private: std::shared_ptr device_; std::unordered_set< std::shared_ptr > views_; + std::shared_ptr main_view_; + + std::shared_ptr main_bar_; mutable std::mutex sampling_mutex_; //!< Protects access to capture_state_. capture_state capture_state_; diff --git a/pv/toolbars/mainbar.cpp b/pv/toolbars/mainbar.cpp index cca4d9d..a76ffba 100644 --- a/pv/toolbars/mainbar.cpp +++ b/pv/toolbars/mainbar.cpp @@ -25,29 +25,46 @@ #include #include +#include #include #include +#include +#include #include #include "mainbar.hpp" +#include + #include #include +#include +#include +#include +#include +#include #include #include #include #include +#include #include #include +#ifdef ENABLE_DECODE +#include +#endif #include 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)), + this, SLOT(export_file(std::shared_ptr))); + + 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)), + this, SLOT(import_file(std::shared_ptr))); + + 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)), - &main_window_, + &main_window, SLOT(import_file(std::shared_ptr))); 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 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)), - &main_window_, + &main_window, SLOT(export_file(std::shared_ptr))); 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 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 dev_info; + list key_list; + shared_ptr 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 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 format, + const std::map &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( + new devices::InputFile( + device_manager.context(), + file_name.toStdString(), + format, options))); + else + session_.set_device(shared_ptr( + 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 input_format; + + DeviceManager& device_manager = session_.device_manager(); + + if (!format.empty()) { + const map > formats = + device_manager.context()->input_formats(); + const auto iter = find_if(formats.begin(), formats.end(), + [&](const pair > 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 device = + device_selector_.selected_device(); + if (!device) + return; + + const shared_ptr sr_dev = device->device(); + + sample_rate = sample_rate_.value(); + if (sample_rate == 0) + return; + + try { + sr_dev->config_set(ConfigKey::SAMPLERATE, + Glib::Variant::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 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 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 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 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() * samplerate); + const uint64_t end_sample = end_time.convert_to() * samplerate; + + sample_range = std::make_pair(start_sample, end_sample); + } else { + sample_range = std::make_pair(0, 0); + } + + // Construct the filter + const vector 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::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 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 format) +{ + assert(format); + + QSettings settings; + const QString dir = settings.value(SettingOpenDirectory).toString(); + + // Construct the filter + const vector 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 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 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_ || diff --git a/pv/toolbars/mainbar.hpp b/pv/toolbars/mainbar.hpp index 9c4b1cd..088efa2 100644 --- a/pv/toolbars/mainbar.hpp +++ b/pv/toolbars/mainbar.hpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include #include @@ -40,6 +42,7 @@ namespace sigrok { class Device; class InputFormat; +class OutputFormat; } Q_DECLARE_METATYPE(std::shared_ptr) @@ -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 device); + + void select_init_device(); + + void load_file(QString file_name, + std::shared_ptr format = nullptr, + const std::map &options = + std::map()); + + 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 format, + bool selection_only = false); + void import_file(std::shared_ptr 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