X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fviews%2Ftrace%2Fview.cpp;h=40ecc195cea44e9cd5d204b525c2386c8c618baa;hp=8e2a2b329daa7bfae8ffbe96f507b550de88fa92;hb=e887fe9e6ede73b9fe9ace9c6bfd4f6a096eb3b2;hpb=39173000c5d0d164c52443d16a1dc434d538b198 diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp index 8e2a2b32..40ecc195 100644 --- a/pv/views/trace/view.cpp +++ b/pv/views/trace/view.cpp @@ -31,11 +31,8 @@ #include #include -#include -#include -#include - #include +#include #include #include #include @@ -84,7 +81,6 @@ using std::pair; using std::set; using std::set_difference; using std::shared_ptr; -using std::stringstream; using std::unordered_map; using std::unordered_set; using std::vector; @@ -124,13 +120,14 @@ bool CustomScrollArea::viewportEvent(QEvent *event) } } -View::View(Session &session, bool is_main_view, QWidget *parent) : +View::View(Session &session, bool is_main_view, QMainWindow *parent) : ViewBase(session, is_main_view, parent), // Note: Place defaults in View::reset_view_state(), not here splitter_(new QSplitter()), header_was_shrunk_(false), // The splitter remains unchanged after a reset, so this goes here - sticky_scrolling_(false) // Default setting is set in MainWindow::setup_ui() + sticky_scrolling_(false), // Default setting is set in MainWindow::setup_ui() + scroll_needs_defaults_(true) { QVBoxLayout *root_layout = new QVBoxLayout(this); root_layout->setContentsMargins(0, 0, 0, 0); @@ -205,11 +202,51 @@ View::View(Session &session, bool is_main_view, QWidget *parent) : this, SLOT(process_sticky_events())); lazy_event_handler_.setSingleShot(true); + // Set up local keyboard shortcuts + zoom_in_shortcut_ = new QShortcut(QKeySequence(Qt::Key_Plus), this, + SLOT(on_zoom_in_shortcut_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + zoom_in_shortcut_->setAutoRepeat(false); + + zoom_out_shortcut_ = new QShortcut(QKeySequence(Qt::Key_Minus), this, + SLOT(on_zoom_out_shortcut_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + zoom_out_shortcut_->setAutoRepeat(false); + + zoom_in_shortcut_2_ = new QShortcut(QKeySequence(Qt::Key_Up), this, + SLOT(on_zoom_in_shortcut_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + zoom_out_shortcut_2_ = new QShortcut(QKeySequence(Qt::Key_Down), this, + SLOT(on_zoom_out_shortcut_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + + home_shortcut_ = new QShortcut(QKeySequence(Qt::Key_Home), this, + SLOT(on_scroll_to_start_shortcut_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + home_shortcut_->setAutoRepeat(false); + + end_shortcut_ = new QShortcut(QKeySequence(Qt::Key_End), this, + SLOT(on_scroll_to_end_shortcut_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + end_shortcut_->setAutoRepeat(false); + + grab_ruler_left_shortcut_ = new QShortcut(QKeySequence(Qt::Key_1), this, + nullptr, nullptr, Qt::WidgetWithChildrenShortcut); + connect(grab_ruler_left_shortcut_, &QShortcut::activated, + this, [=]{on_grab_ruler(1);}); + grab_ruler_left_shortcut_->setAutoRepeat(false); + + grab_ruler_right_shortcut_ = new QShortcut(QKeySequence(Qt::Key_2), this, + nullptr, nullptr, Qt::WidgetWithChildrenShortcut); + connect(grab_ruler_right_shortcut_, &QShortcut::activated, + this, [=]{on_grab_ruler(2);}); + grab_ruler_right_shortcut_->setAutoRepeat(false); + + cancel_grab_shortcut_ = new QShortcut(QKeySequence(Qt::Key_Escape), this, + nullptr, nullptr, Qt::WidgetWithChildrenShortcut); + connect(cancel_grab_shortcut_, &QShortcut::activated, + this, [=]{grabbed_widget_ = nullptr;}); + cancel_grab_shortcut_->setAutoRepeat(false); + // Trigger the initial event manually. The default device has signals // which were created before this object came into being signals_changed(); - // make sure the transparent widgets are on the top + // Make sure the transparent widgets are on the top ruler_->raise(); header_->raise(); @@ -221,6 +258,11 @@ View::~View() GlobalSettings::remove_change_handler(this); } +ViewType View::get_type() const +{ + return ViewTypeTrace; +} + void View::reset_view_state() { ViewBase::reset_view_state(); @@ -230,6 +272,8 @@ void View::reset_view_state() scale_ = 1e-3; offset_ = 0; ruler_offset_ = 0; + zero_offset_ = 0; + custom_zero_offset_set_ = false; updating_scroll_ = false; settings_restored_ = false; always_zoom_to_fit_ = false; @@ -242,6 +286,7 @@ void View::reset_view_state() next_flag_text_ = 'A'; trigger_markers_.clear(); hover_widget_ = nullptr; + grabbed_widget_ = nullptr; hover_point_ = QPoint(-1, -1); scroll_needs_defaults_ = true; saved_v_offset_ = 0; @@ -275,9 +320,22 @@ unordered_set< shared_ptr > View::signals() const return signals_; } +shared_ptr View::get_signal_by_signalbase(shared_ptr base) const +{ + shared_ptr ret_val; + + for (const shared_ptr& s : signals_) + if (s->base() == base) { + ret_val = s; + break; + } + + return ret_val; +} + void View::clear_signals() { - ViewBase::clear_signalbases(); + ViewBase::clear_signals(); signals_.clear(); } @@ -296,11 +354,14 @@ void View::add_signal(const shared_ptr signal) #ifdef ENABLE_DECODE void View::clear_decode_signals() { + ViewBase::clear_decode_signals(); decode_traces_.clear(); } void View::add_decode_signal(shared_ptr signal) { + ViewBase::add_decode_signal(signal); + shared_ptr d( new DecodeTrace(session_, signal, decode_traces_.size())); decode_traces_.push_back(d); @@ -320,6 +381,8 @@ void View::remove_decode_signal(shared_ptr signal) signals_changed(); return; } + + ViewBase::remove_decode_signal(signal); } #endif @@ -348,6 +411,11 @@ const Viewport* View::viewport() const return viewport_; } +QAbstractScrollArea* View::scrollarea() const +{ + return scrollarea_; +} + const Ruler* View::ruler() const { return ruler_; @@ -362,12 +430,12 @@ void View::save_settings(QSettings &settings) const settings.setValue("splitter_state", splitter_->saveState()); settings.setValue("segment_display_mode", segment_display_mode_); - { - stringstream ss; - boost::archive::text_oarchive oa(ss); - oa << boost::serialization::make_nvp("offset", offset_); - settings.setValue("offset", QString::fromStdString(ss.str())); - } + GlobalSettings::store_timestamp(settings, "offset", offset_); + + if (custom_zero_offset_set_) + GlobalSettings::store_timestamp(settings, "zero_offset", -zero_offset_); + else + settings.remove("zero_offset"); for (const shared_ptr& signal : signals_) { settings.beginGroup(signal->base()->internal_name()); @@ -385,20 +453,13 @@ void View::restore_settings(QSettings &settings) set_scale(settings.value("scale").toDouble()); if (settings.contains("offset")) { - util::Timestamp offset; - stringstream ss; - ss << settings.value("offset").toString().toStdString(); - - try { - boost::archive::text_iarchive ia(ss); - ia >> boost::serialization::make_nvp("offset", offset); - // This also updates ruler_offset_ - set_offset(offset); - } catch (boost::archive::archive_exception&) { - qDebug() << "Could not restore the view offset"; - } + // This also updates ruler_offset_ + set_offset(GlobalSettings::restore_timestamp(settings, "offset")); } + if (settings.contains("zero_offset")) + set_zero_position(GlobalSettings::restore_timestamp(settings, "zero_offset")); + if (settings.contains("splitter_state")) splitter_->restoreState(settings.value("splitter_state").toByteArray()); @@ -456,16 +517,6 @@ void View::set_scale(double scale) } } -pv::util::Timestamp View::absolute_to_ruler_time(const pv::util::Timestamp& abs_time) const -{ - return abs_time + zero_offset_; -} - -pv::util::Timestamp View::ruler_to_absolute_time(const pv::util::Timestamp& ruler_time) const -{ - return ruler_time - zero_offset_; -} - void View::set_offset(const pv::util::Timestamp& offset, bool force_update) { if ((offset_ != offset) || force_update) { @@ -488,6 +539,7 @@ const Timestamp& View::ruler_offset() const void View::set_zero_position(const pv::util::Timestamp& position) { zero_offset_ = -position; + custom_zero_offset_set_ = true; // Force an immediate update of the offsets set_offset(offset_, true); @@ -498,11 +550,29 @@ void View::reset_zero_position() { zero_offset_ = 0; + // When enabled, the first trigger for this segment is used as the zero position + GlobalSettings settings; + bool trigger_is_zero_time = settings.value(GlobalSettings::Key_View_TriggerIsZeroTime).toBool(); + + if (trigger_is_zero_time) { + vector triggers = session_.get_triggers(current_segment_); + + if (triggers.size() > 0) + zero_offset_ = triggers.front(); + } + + custom_zero_offset_set_ = false; + // Force an immediate update of the offsets set_offset(offset_, true); ruler_->update(); } +pv::util::Timestamp View::zero_offset() const +{ + return zero_offset_; +} + int View::owner_visual_v_offset() const { return -scrollarea_->verticalScrollBar()->sliderPosition(); @@ -515,6 +585,18 @@ void View::set_v_offset(int offset) viewport_->update(); } +void View::set_h_offset(int offset) +{ + scrollarea_->horizontalScrollBar()->setSliderPosition(offset); + header_->update(); + viewport_->update(); +} + +int View::get_h_scrollbar_maximum() const +{ + return scrollarea_->horizontalScrollBar()->maximum(); +} + unsigned int View::depth() const { return 0; @@ -599,12 +681,8 @@ void View::set_current_segment(uint32_t segment_id) for (util::Timestamp timestamp : triggers) trigger_markers_.push_back(make_shared(*this, timestamp)); - // When enabled, the first trigger for this segment is used as the zero position - GlobalSettings settings; - bool trigger_is_zero_time = settings.value(GlobalSettings::Key_View_TriggerIsZeroTime).toBool(); - - if (trigger_is_zero_time && (triggers.size() > 0)) - set_zero_position(triggers.front()); + if (!custom_zero_offset_set_) + reset_zero_position(); viewport_->update(); @@ -768,26 +846,6 @@ pair View::get_time_extents() const return make_pair(*left_time, *right_time); } -void View::enable_show_sampling_points(bool state) -{ - (void)state; - - viewport_->update(); -} - -void View::enable_show_analog_minor_grid(bool state) -{ - (void)state; - - viewport_->update(); -} - -void View::enable_colored_bg(bool state) -{ - colored_bg_ = state; - viewport_->update(); -} - bool View::colored_bg() const { return colored_bg_; @@ -820,7 +878,7 @@ void View::set_cursors(pv::util::Timestamp& first, pv::util::Timestamp& second) viewport_->update(); } -void View::centre_cursors() +void View::center_cursors() { assert(cursors_); @@ -837,15 +895,17 @@ shared_ptr View::cursors() const return cursors_; } -void View::add_flag(const Timestamp& time) +shared_ptr View::add_flag(const Timestamp& time) { - flags_.push_back(make_shared(*this, time, - QString("%1").arg(next_flag_text_))); + shared_ptr flag = + make_shared(*this, time, QString("%1").arg(next_flag_text_)); + flags_.push_back(flag); next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' : (next_flag_text_ + 1); time_item_appearance_changed(true, true); + return flag; } void View::remove_flag(shared_ptr flag) @@ -1005,13 +1065,22 @@ int View::header_width() const void View::on_setting_changed(const QString &key, const QVariant &value) { + GlobalSettings settings; + + if (key == GlobalSettings::Key_View_ColoredBG) { + colored_bg_ = settings.value(GlobalSettings::Key_View_ColoredBG).toBool(); + viewport_->update(); + } + + if ((key == GlobalSettings::Key_View_ShowSamplingPoints) || + (key == GlobalSettings::Key_View_ShowAnalogMinorGrid)) + viewport_->update(); + if (key == GlobalSettings::Key_View_TriggerIsZeroTime) on_settingViewTriggerIsZeroTime_changed(value); - if (key == GlobalSettings::Key_View_SnapDistance) { - GlobalSettings settings; + if (key == GlobalSettings::Key_View_SnapDistance) snap_distance_ = settings.value(GlobalSettings::Key_View_SnapDistance).toInt(); - } } void View::trigger_event(int segment_id, util::Timestamp location) @@ -1020,15 +1089,8 @@ void View::trigger_event(int segment_id, util::Timestamp location) if ((uint32_t)segment_id != current_segment_) return; - // Set zero location if the Key_View_TriggerIsZeroTime setting is set and - // if this is the first trigger for this segment. - GlobalSettings settings; - bool trigger_is_zero_time = settings.value(GlobalSettings::Key_View_TriggerIsZeroTime).toBool(); - - size_t trigger_count = session_.get_triggers(current_segment_).size(); - - if (trigger_is_zero_time && trigger_count == 1) - set_zero_position(location); + if (!custom_zero_offset_set_) + reset_zero_position(); trigger_markers_.push_back(make_shared(*this, location)); } @@ -1338,6 +1400,7 @@ void View::determine_time_unit() bool View::eventFilter(QObject *object, QEvent *event) { const QEvent::Type type = event->type(); + if (type == QEvent::MouseMove) { if (object) @@ -1355,6 +1418,28 @@ bool View::eventFilter(QObject *object, QEvent *event) update_hover_point(); + if (grabbed_widget_) { + int64_t nearest = get_nearest_level_change(hover_point_); + pv::util::Timestamp mouse_time = offset_ + hover_point_.x() * scale_; + + if (nearest == -1) { + grabbed_widget_->set_time(mouse_time); + } else { + grabbed_widget_->set_time(nearest / get_signal_under_mouse_cursor()->base()->get_samplerate()); + } + } + + } else if (type == QEvent::MouseButtonPress) { + grabbed_widget_ = nullptr; + + const QMouseEvent *const mouse_event = (QMouseEvent*)event; + if ((object == viewport_) && (mouse_event->button() & Qt::LeftButton)) { + // Send event to all trace tree items + const vector> trace_tree_items( + list_by_type()); + for (const shared_ptr& r : trace_tree_items) + r->mouse_left_press_event(mouse_event); + } } else if (type == QEvent::Leave) { hover_point_ = QPoint(-1, -1); update_hover_point(); @@ -1481,6 +1566,26 @@ void View::on_splitter_moved() resize_header_to_fit(); } +void View::on_zoom_in_shortcut_triggered() +{ + zoom(1); +} + +void View::on_zoom_out_shortcut_triggered() +{ + zoom(-1); +} + +void View::on_scroll_to_start_shortcut_triggered() +{ + set_h_offset(0); +} + +void View::on_scroll_to_end_shortcut_triggered() +{ + set_h_offset(get_h_scrollbar_maximum()); +} + void View::h_scroll_value_changed(int value) { if (updating_scroll_) @@ -1513,6 +1618,26 @@ void View::v_scroll_value_changed() viewport_->update(); } +void View::on_grab_ruler(int ruler_id) +{ + if (!cursors_shown()) { + center_cursors(); + show_cursors(); + } + + // Release the grabbed widget if its trigger hotkey was pressed twice + if (ruler_id == 1) + grabbed_widget_ = (grabbed_widget_ == cursors_->first().get()) ? + nullptr : cursors_->first().get(); + else + grabbed_widget_ = (grabbed_widget_ == cursors_->second().get()) ? + nullptr : cursors_->second().get(); + + if (grabbed_widget_) + grabbed_widget_->set_time(ruler_->get_absolute_time_from_x_pos( + mapFromGlobal(QCursor::pos()).x() - header_width())); +} + void View::signals_changed() { using sigrok::Channel; @@ -1700,7 +1825,8 @@ void View::capture_state_updated(int state) set_time_unit(util::TimeUnit::Samples); trigger_markers_.clear(); - set_zero_position(0); + if (!custom_zero_offset_set_) + set_zero_position(0); scale_at_acq_start_ = scale_; offset_at_acq_start_ = offset_; @@ -1783,12 +1909,7 @@ void View::on_segment_changed(int segment) void View::on_settingViewTriggerIsZeroTime_changed(const QVariant new_value) { - if (new_value.toBool()) { - // The first trigger for this segment is used as the zero position - vector triggers = session_.get_triggers(current_segment_); - if (triggers.size() > 0) - set_zero_position(triggers.front()); - } else + if (!custom_zero_offset_set_) reset_zero_position(); }