]> sigrok.org Git - pulseview.git/blame - pv/view/view.cpp
CursorPair: Moved in ViewportFillColour
[pulseview.git] / pv / view / view.cpp
CommitLineData
adb4b10c 1/*
b3f22de0 2 * This file is part of the PulseView project.
adb4b10c
JH
3 *
4 * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
269528f5 21#ifdef ENABLE_DECODE
4e5a4405 22#include <libsigrokdecode/libsigrokdecode.h>
269528f5 23#endif
4e5a4405 24
361c560e
JH
25#include <extdef.h>
26
c3a740dd
JH
27#include <cassert>
28#include <climits>
29#include <cmath>
30#include <mutex>
7ff0145f 31#include <unordered_set>
adb4b10c 32
361c560e 33#include <QApplication>
adb4b10c 34#include <QEvent>
361c560e 35#include <QFontMetrics>
cbd80f64 36#include <QMouseEvent>
adb4b10c
JH
37#include <QScrollBar>
38
448a72cf
JH
39#include <libsigrok/libsigrok.hpp>
40
2acdb232
JH
41#include "decodetrace.hpp"
42#include "header.hpp"
43#include "logicsignal.hpp"
44#include "ruler.hpp"
45#include "signal.hpp"
46#include "tracegroup.hpp"
47#include "view.hpp"
48#include "viewport.hpp"
adb4b10c 49
f65cd27b 50#include "pv/session.hpp"
2acdb232 51#include "pv/data/logic.hpp"
f3d66e52 52#include "pv/data/logicsegment.hpp"
361c560e 53#include "pv/util.hpp"
adb4b10c 54
aca64cac
JH
55using boost::shared_lock;
56using boost::shared_mutex;
361c560e 57
1bc6525b 58using pv::data::SignalData;
f3d66e52 59using pv::data::Segment;
361c560e
JH
60using pv::util::format_time;
61
819f4c25 62using std::deque;
448a72cf 63using std::dynamic_pointer_cast;
819f4c25 64using std::list;
c3a740dd 65using std::lock_guard;
819f4c25 66using std::max;
1bc6525b 67using std::make_pair;
819f4c25 68using std::min;
1bc6525b 69using std::pair;
819f4c25 70using std::set;
f9abf97e 71using std::shared_ptr;
448a72cf 72using std::unordered_map;
7ff0145f 73using std::unordered_set;
819f4c25 74using std::vector;
f9abf97e 75using std::weak_ptr;
adb4b10c 76
cdf7bea7
JH
77namespace pv {
78namespace view {
adb4b10c 79
cdf7bea7
JH
80const double View::MaxScale = 1e9;
81const double View::MinScale = 1e-15;
adb4b10c 82
f25770e2
JH
83const int View::MaxScrollValue = INT_MAX / 2;
84
361c560e
JH
85const int View::ScaleUnits[3] = {1, 2, 5};
86
2e04f9bd
JH
87const QSizeF View::LabelPadding(4, 0);
88
2b81ae46 89View::View(Session &session, QWidget *parent) :
adb4b10c 90 QAbstractScrollArea(parent),
8dbbc7f0
JH
91 session_(session),
92 viewport_(new Viewport(*this)),
93 ruler_(new Ruler(*this)),
8dbbc7f0
JH
94 header_(new Header(*this)),
95 scale_(1e-6),
96 offset_(0),
97 v_offset_(0),
98 updating_scroll_(false),
361c560e
JH
99 tick_period_(0.0),
100 tick_prefix_(0),
8dbbc7f0 101 show_cursors_(false),
5c5ce757 102 cursors_(new CursorPair(*this)),
8914fe79 103 next_flag_text_('A'),
8dbbc7f0 104 hover_point_(-1, -1)
adb4b10c 105{
b16907d3
JH
106 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
107 this, SLOT(h_scroll_value_changed(int)));
adb4b10c
JH
108 connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
109 this, SLOT(v_scroll_value_changed(int)));
69dd2b03 110
8dbbc7f0 111 connect(&session_, SIGNAL(signals_changed()),
69dd2b03 112 this, SLOT(signals_changed()));
8dbbc7f0 113 connect(&session_, SIGNAL(capture_state_changed(int)),
50f97924 114 this, SLOT(data_updated()));
8dbbc7f0 115 connect(&session_, SIGNAL(data_received()),
1f374035 116 this, SLOT(data_updated()));
8dbbc7f0 117 connect(&session_, SIGNAL(frame_ended()),
adb4b10c 118 this, SLOT(data_updated()));
1d8dca91 119
8dbbc7f0 120 connect(header_, SIGNAL(signals_moved()),
07204819 121 this, SLOT(on_signals_moved()));
54401bbb 122
8dbbc7f0 123 connect(header_, SIGNAL(selection_changed()),
819e2e95
JH
124 ruler_, SLOT(clear_selection()));
125 connect(ruler_, SIGNAL(selection_changed()),
8dbbc7f0 126 header_, SLOT(clear_selection()));
17348f85 127
8dbbc7f0 128 connect(header_, SIGNAL(selection_changed()),
8b454527 129 this, SIGNAL(selection_changed()));
819e2e95 130 connect(ruler_, SIGNAL(selection_changed()),
8b454527
JH
131 this, SIGNAL(selection_changed()));
132
33c62f44
JH
133 connect(this, SIGNAL(hover_point_changed()),
134 this, SLOT(on_hover_point_changed()));
135
8dbbc7f0 136 connect(&lazy_event_handler_, SIGNAL(timeout()),
32218d3e 137 this, SLOT(process_sticky_events()));
8dbbc7f0 138 lazy_event_handler_.setSingleShot(true);
32218d3e 139
8dbbc7f0 140 setViewport(viewport_);
cbd80f64 141
8dbbc7f0
JH
142 viewport_->installEventFilter(this);
143 ruler_->installEventFilter(this);
8dbbc7f0 144 header_->installEventFilter(this);
d873f4d6 145
9f46d905
JH
146 // Trigger the initial event manually. The default device has signals
147 // which were created before this object came into being
d873f4d6 148 signals_changed();
84a0d458 149
512bfc56 150 // make sure the transparent widgets are on the top
819e2e95 151 ruler_->raise();
8dbbc7f0 152 header_->raise();
361c560e
JH
153
154 // Update the zoom state
155 calculate_tick_spacing();
adb4b10c
JH
156}
157
2b81ae46 158Session& View::session()
1d19ef83 159{
8dbbc7f0 160 return session_;
1d19ef83
JH
161}
162
2b81ae46 163const Session& View::session() const
38eeddea 164{
8dbbc7f0 165 return session_;
38eeddea
JH
166}
167
eae6e30a
JH
168View* View::view()
169{
170 return this;
171}
172
173const View* View::view() const
174{
175 return this;
176}
177
2ae445ba
SA
178Viewport* View::viewport()
179{
8dbbc7f0 180 return viewport_;
2ae445ba
SA
181}
182
183const Viewport* View::viewport() const
184{
8dbbc7f0 185 return viewport_;
2ae445ba
SA
186}
187
2496bf45
JH
188vector< shared_ptr<TimeItem> > View::time_items() const
189{
8914fe79
JH
190 const vector<shared_ptr<Flag>> f(flags());
191 vector<shared_ptr<TimeItem>> items(f.begin(), f.end());
5a3e53f6 192 items.push_back(cursors_);
5c5ce757
JH
193 items.push_back(cursors_->first());
194 items.push_back(cursors_->second());
2496bf45
JH
195 return items;
196}
197
cdf7bea7 198double View::scale() const
adb4b10c 199{
8dbbc7f0 200 return scale_;
adb4b10c
JH
201}
202
cdf7bea7 203double View::offset() const
adb4b10c 204{
8dbbc7f0 205 return offset_;
adb4b10c
JH
206}
207
7ff0145f 208int View::owner_visual_v_offset() const
adb4b10c 209{
8dbbc7f0 210 return -v_offset_;
adb4b10c
JH
211}
212
3e769a37
JH
213unsigned int View::depth() const
214{
215 return 0;
216}
217
361c560e
JH
218unsigned int View::tick_prefix() const
219{
220 return tick_prefix_;
221}
222
223double View::tick_period() const
224{
225 return tick_period_;
226}
227
cdf7bea7 228void View::zoom(double steps)
adb4b10c 229{
8dbbc7f0 230 zoom(steps, viewport_->width() / 2);
adb4b10c
JH
231}
232
17c0f398
JH
233void View::zoom(double steps, int offset)
234{
8dbbc7f0 235 set_zoom(scale_ * pow(3.0/2.0, -steps), offset);
17c0f398
JH
236}
237
ca46b534
JH
238void View::zoom_fit()
239{
1bc6525b
JH
240 const pair<double, double> extents = get_time_extents();
241 const double delta = extents.second - extents.first;
242 if (delta < 1e-12)
ca46b534
JH
243 return;
244
8dbbc7f0
JH
245 assert(viewport_);
246 const int w = viewport_->width();
ca46b534
JH
247 if (w <= 0)
248 return;
249
78311127
JH
250 const double scale = max(min(delta / w, MaxScale), MinScale);
251 set_scale_offset(scale, extents.first);
ca46b534
JH
252}
253
d1e7d82c
JH
254void View::zoom_one_to_one()
255{
256 using pv::data::SignalData;
257
d1e7d82c 258 // Make a set of all the visible data objects
0fc664a9 259 set< shared_ptr<SignalData> > visible_data = get_visible_data();
d1e7d82c
JH
260 if (visible_data.empty())
261 return;
262
263 double samplerate = 0.0;
d9aecf1f 264 for (const shared_ptr<SignalData> d : visible_data) {
d1e7d82c 265 assert(d);
f3d66e52
JH
266 const vector< shared_ptr<Segment> > segments =
267 d->segments();
268 for (const shared_ptr<Segment> &s : segments)
ff008de6 269 samplerate = max(samplerate, s->samplerate());
d1e7d82c
JH
270 }
271
272 if (samplerate == 0.0)
273 return;
274
8dbbc7f0
JH
275 assert(viewport_);
276 const int w = viewport_->width();
d1e7d82c
JH
277 if (w <= 0)
278 return;
279
280 set_zoom(1.0 / samplerate, w / 2);
281}
282
cdf7bea7 283void View::set_scale_offset(double scale, double offset)
adb4b10c 284{
8dbbc7f0
JH
285 scale_ = scale;
286 offset_ = offset;
ccdd3ef5 287
361c560e
JH
288 calculate_tick_spacing();
289
adb4b10c 290 update_scroll();
8dbbc7f0 291 ruler_->update();
8dbbc7f0 292 viewport_->update();
e0fc5810 293 scale_offset_changed();
adb4b10c
JH
294}
295
1bc6525b
JH
296set< shared_ptr<SignalData> > View::get_visible_data() const
297{
aca64cac 298 shared_lock<shared_mutex> lock(session().signals_mutex());
c3a740dd 299 const vector< shared_ptr<Signal> > &sigs(session().signals());
1bc6525b
JH
300
301 // Make a set of all the visible data objects
302 set< shared_ptr<SignalData> > visible_data;
d9aecf1f 303 for (const shared_ptr<Signal> sig : sigs)
1bc6525b
JH
304 if (sig->enabled())
305 visible_data.insert(sig->data());
306
307 return visible_data;
308}
309
310pair<double, double> View::get_time_extents() const
311{
1bc6525b 312 double left_time = DBL_MAX, right_time = DBL_MIN;
7f4038d6 313 const set< shared_ptr<SignalData> > visible_data = get_visible_data();
d9aecf1f 314 for (const shared_ptr<SignalData> d : visible_data)
1bc6525b 315 {
f3d66e52
JH
316 const vector< shared_ptr<Segment> > segments =
317 d->segments();
318 for (const shared_ptr<Segment> &s : segments) {
ff008de6
JH
319 double samplerate = s->samplerate();
320 samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
321
7f4038d6
JH
322 const double start_time = s->start_time();
323 left_time = min(left_time, start_time);
324 right_time = max(right_time, start_time +
325 d->get_max_sample_count() / samplerate);
326 }
1bc6525b
JH
327 }
328
7f4038d6
JH
329 if (left_time == DBL_MAX && right_time == DBL_MIN)
330 return make_pair(0.0, 0.0);
331
1bc6525b
JH
332 assert(left_time < right_time);
333 return make_pair(left_time, right_time);
334}
335
f76af637
JH
336bool View::cursors_shown() const
337{
8dbbc7f0 338 return show_cursors_;
f76af637
JH
339}
340
341void View::show_cursors(bool show)
342{
8dbbc7f0 343 show_cursors_ = show;
819e2e95 344 ruler_->update();
8dbbc7f0 345 viewport_->update();
f76af637
JH
346}
347
b4d91e56
JH
348void View::centre_cursors()
349{
8dbbc7f0 350 const double time_width = scale_ * viewport_->width();
5c5ce757
JH
351 cursors_->first()->set_time(offset_ + time_width * 0.4);
352 cursors_->second()->set_time(offset_ + time_width * 0.6);
819e2e95 353 ruler_->update();
8dbbc7f0 354 viewport_->update();
b4d91e56
JH
355}
356
5c5ce757 357std::shared_ptr<CursorPair> View::cursors() const
58864c5c 358{
8dbbc7f0 359 return cursors_;
58864c5c
JH
360}
361
8914fe79
JH
362void View::add_flag(double time)
363{
364 flags_.push_back(shared_ptr<Flag>(new Flag(*this, time,
365 QString("%1").arg(next_flag_text_))));
366 next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' :
367 (next_flag_text_ + 1);
368 time_item_appearance_changed(true, true);
369}
370
371void View::remove_flag(std::shared_ptr<Flag> flag)
372{
373 flags_.remove(flag);
374 time_item_appearance_changed(true, true);
375}
376
377vector< std::shared_ptr<Flag> > View::flags() const
378{
379 vector< std::shared_ptr<Flag> > flags(flags_.begin(), flags_.end());
380 stable_sort(flags.begin(), flags.end(),
381 [](const shared_ptr<Flag> &a, const shared_ptr<Flag> &b) {
382 return a->time() < b->time();
383 });
384
385 return flags;
386}
387
cbd80f64
JH
388const QPoint& View::hover_point() const
389{
8dbbc7f0 390 return hover_point_;
cbd80f64
JH
391}
392
9cef9567
JH
393void View::update_viewport()
394{
8dbbc7f0
JH
395 assert(viewport_);
396 viewport_->update();
397 header_->update();
7ff0145f
JH
398}
399
400void View::restack_all_row_items()
401{
402 // Make a set of owners
403 unordered_set< RowItemOwner* > owners;
404 for (const auto &r : *this)
405 owners.insert(r->owner());
406
407 // Make a list that is sorted from deepest first
408 vector< RowItemOwner* > sorted_owners(owners.begin(), owners.end());
409 sort(sorted_owners.begin(), sorted_owners.end(),
410 [](const RowItemOwner* a, const RowItemOwner *b) {
411 return a->depth() > b->depth(); });
412
413 // Restack the items recursively
414 for (auto &o : sorted_owners)
415 o->restack_items();
416
417 // Animate the items to their destination
418 for (const auto &r : *this)
419 r->animate_to_layout_v_offset();
9cef9567
JH
420}
421
f25770e2
JH
422void View::get_scroll_layout(double &length, double &offset) const
423{
1bc6525b 424 const pair<double, double> extents = get_time_extents();
8dbbc7f0
JH
425 length = (extents.second - extents.first) / scale_;
426 offset = offset_ / scale_;
f25770e2
JH
427}
428
d1e7d82c
JH
429void View::set_zoom(double scale, int offset)
430{
8dbbc7f0 431 const double cursor_offset = offset_ + scale_ * offset;
d1e7d82c
JH
432 const double new_scale = max(min(scale, MaxScale), MinScale);
433 const double new_offset = cursor_offset - new_scale * offset;
434 set_scale_offset(new_scale, new_offset);
435}
436
361c560e
JH
437void View::calculate_tick_spacing()
438{
439 const double SpacingIncrement = 32.0f;
440 const double MinValueSpacing = 32.0f;
441
442 double min_width = SpacingIncrement, typical_width;
443
444 QFontMetrics m(QApplication::font());
445
446 do {
447 const double min_period = scale_ * min_width;
448
449 const int order = (int)floorf(log10f(min_period));
450 const double order_decimal = pow(10.0, order);
451
452 unsigned int unit = 0;
453
454 do {
455 tick_period_ = order_decimal * ScaleUnits[unit++];
456 } while (tick_period_ < min_period &&
457 unit < countof(ScaleUnits));
458
459 tick_prefix_ = (order - pv::util::FirstSIPrefixPower) / 3;
460
461 typical_width = m.boundingRect(0, 0, INT_MAX, INT_MAX,
462 Qt::AlignLeft | Qt::AlignTop,
463 format_time(offset_, tick_prefix_)).width() +
464 MinValueSpacing;
465
466 min_width += SpacingIncrement;
467
468 } while(typical_width > tick_period_ / scale_);
469}
470
cdf7bea7 471void View::update_scroll()
adb4b10c 472{
8dbbc7f0 473 assert(viewport_);
adb4b10c 474
8dbbc7f0 475 const QSize areaSize = viewport_->size();
adb4b10c
JH
476
477 // Set the horizontal scroll bar
478 double length = 0, offset = 0;
f25770e2
JH
479 get_scroll_layout(length, offset);
480 length = max(length - areaSize.width(), 0.0);
adb4b10c 481
d7aae647
SA
482 int major_tick_distance = tick_period_ / scale_;
483
b4ef7f2a 484 horizontalScrollBar()->setPageStep(areaSize.width() / 2);
d7aae647 485 horizontalScrollBar()->setSingleStep(major_tick_distance);
f25770e2 486
8dbbc7f0 487 updating_scroll_ = true;
528bd8a1 488
333d5bbc 489 if (length < MaxScrollValue) {
f25770e2
JH
490 horizontalScrollBar()->setRange(0, length);
491 horizontalScrollBar()->setSliderPosition(offset);
492 } else {
493 horizontalScrollBar()->setRange(0, MaxScrollValue);
494 horizontalScrollBar()->setSliderPosition(
8dbbc7f0 495 offset_ * MaxScrollValue / (scale_ * length));
f25770e2 496 }
adb4b10c 497
8dbbc7f0 498 updating_scroll_ = false;
528bd8a1 499
adb4b10c
JH
500 // Set the vertical scrollbar
501 verticalScrollBar()->setPageStep(areaSize.height());
65c34596 502 verticalScrollBar()->setSingleStep(areaSize.height() / 8);
a5d93c27
JH
503
504 const pair<int, int> extents = v_extents();
f1d9ac67
SA
505 verticalScrollBar()->setRange(extents.first - (areaSize.height() / 2),
506 extents.second - (areaSize.height() / 2));
adb4b10c
JH
507}
508
d7c0ca4a
JH
509void View::update_layout()
510{
512bfc56 511 setViewportMargins(
8dbbc7f0
JH
512 header_->sizeHint().width() - pv::view::Header::BaselineOffset,
513 ruler_->sizeHint().height(), 0, 0);
514 ruler_->setGeometry(viewport_->x(), 0,
819e2e95 515 viewport_->width(), ruler_->extended_size_hint().height());
8dbbc7f0 516 header_->setGeometry(0, viewport_->y(),
819e2e95 517 header_->extended_size_hint().width(), viewport_->height());
d7c0ca4a
JH
518 update_scroll();
519}
520
b3f44329 521void View::paint_label(QPainter &p, const QRect &rect, bool hover)
eae6e30a
JH
522{
523 (void)p;
b3f44329 524 (void)rect;
eae6e30a
JH
525 (void)hover;
526}
527
b3f44329 528QRectF View::label_rect(const QRectF &rect)
eae6e30a 529{
b3f44329 530 (void)rect;
eae6e30a
JH
531 return QRectF();
532}
533
448a72cf
JH
534bool View::add_channels_to_owner(
535 const vector< shared_ptr<sigrok::Channel> > &channels,
536 RowItemOwner *owner, int &offset,
537 unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
538 &signal_map,
539 std::function<bool (shared_ptr<RowItem>)> filter_func)
540{
541 bool any_added = false;
542
543 assert(owner);
544
545 for (const auto &channel : channels)
546 {
547 const auto iter = signal_map.find(channel);
548 if (iter == signal_map.end() ||
549 (filter_func && !filter_func((*iter).second)))
550 continue;
551
552 shared_ptr<RowItem> row_item = (*iter).second;
553 owner->add_child_item(row_item);
554 apply_offset(row_item, offset);
555 signal_map.erase(iter);
556
557 any_added = true;
558 }
559
560 return any_added;
561}
562
563void View::apply_offset(shared_ptr<RowItem> row_item, int &offset) {
564 assert(row_item);
565 const pair<int, int> extents = row_item->v_extents();
566 if (row_item->enabled())
567 offset += -extents.first;
568 row_item->force_to_v_offset(offset);
569 if (row_item->enabled())
570 offset += extents.second;
571}
572
cbd80f64
JH
573bool View::eventFilter(QObject *object, QEvent *event)
574{
575 const QEvent::Type type = event->type();
333d5bbc 576 if (type == QEvent::MouseMove) {
cbd80f64
JH
577
578 const QMouseEvent *const mouse_event = (QMouseEvent*)event;
8dbbc7f0
JH
579 if (object == viewport_)
580 hover_point_ = mouse_event->pos();
819e2e95 581 else if (object == ruler_)
8dbbc7f0
JH
582 hover_point_ = QPoint(mouse_event->x(), 0);
583 else if (object == header_)
584 hover_point_ = QPoint(0, mouse_event->y());
cbd80f64 585 else
8dbbc7f0 586 hover_point_ = QPoint(-1, -1);
cbd80f64
JH
587
588 hover_point_changed();
589
333d5bbc 590 } else if (type == QEvent::Leave) {
8dbbc7f0 591 hover_point_ = QPoint(-1, -1);
cbd80f64
JH
592 hover_point_changed();
593 }
594
595 return QObject::eventFilter(object, event);
596}
597
cdf7bea7 598bool View::viewportEvent(QEvent *e)
adb4b10c
JH
599{
600 switch(e->type()) {
601 case QEvent::Paint:
602 case QEvent::MouseButtonPress:
603 case QEvent::MouseButtonRelease:
604 case QEvent::MouseButtonDblClick:
605 case QEvent::MouseMove:
606 case QEvent::Wheel:
4b4f1c0d
MC
607 case QEvent::TouchBegin:
608 case QEvent::TouchUpdate:
609 case QEvent::TouchEnd:
adb4b10c
JH
610 return false;
611
612 default:
613 return QAbstractScrollArea::viewportEvent(e);
614 }
615}
616
e314eca4 617void View::resizeEvent(QResizeEvent*)
adb4b10c 618{
d7c0ca4a 619 update_layout();
adb4b10c
JH
620}
621
6e2c3c85 622void View::row_item_appearance_changed(bool label, bool content)
32218d3e
JH
623{
624 if (label)
8dbbc7f0 625 header_->update();
32218d3e 626 if (content)
8dbbc7f0 627 viewport_->update();
32218d3e
JH
628}
629
98cfe4e8
JH
630void View::time_item_appearance_changed(bool label, bool content)
631{
632 if (label)
819e2e95 633 ruler_->update();
98cfe4e8
JH
634 if (content)
635 viewport_->update();
636}
637
32218d3e
JH
638void View::extents_changed(bool horz, bool vert)
639{
8dbbc7f0 640 sticky_events_ |=
76fea660
JH
641 (horz ? RowItemHExtentsChanged : 0) |
642 (vert ? RowItemVExtentsChanged : 0);
8dbbc7f0 643 lazy_event_handler_.start();
32218d3e
JH
644}
645
b16907d3 646void View::h_scroll_value_changed(int value)
adb4b10c 647{
8dbbc7f0 648 if (updating_scroll_)
528bd8a1
JH
649 return;
650
f25770e2 651 const int range = horizontalScrollBar()->maximum();
333d5bbc 652 if (range < MaxScrollValue)
8dbbc7f0 653 offset_ = scale_ * value;
f25770e2
JH
654 else {
655 double length = 0, offset;
656 get_scroll_layout(length, offset);
8dbbc7f0 657 offset_ = scale_ * length * value / MaxScrollValue;
f25770e2
JH
658 }
659
8dbbc7f0 660 ruler_->update();
8dbbc7f0 661 viewport_->update();
adb4b10c
JH
662}
663
cdf7bea7 664void View::v_scroll_value_changed(int value)
adb4b10c 665{
8dbbc7f0
JH
666 v_offset_ = value;
667 header_->update();
668 viewport_->update();
adb4b10c
JH
669}
670
69dd2b03
JH
671void View::signals_changed()
672{
448a72cf
JH
673 int offset = 0;
674
68b21a71
JH
675 // Populate the traces
676 clear_child_items();
677
8dbbc7f0 678 shared_ptr<sigrok::Device> device = session_.device();
448a72cf
JH
679 assert(device);
680
681 // Collect a set of signals
682 unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
683 signal_map;
684
8dbbc7f0
JH
685 shared_lock<shared_mutex> lock(session_.signals_mutex());
686 const vector< shared_ptr<Signal> > &sigs(session_.signals());
448a72cf
JH
687
688 for (const shared_ptr<Signal> &sig : sigs)
689 signal_map[sig->channel()] = sig;
690
691 // Populate channel groups
692 for (auto entry : device->channel_groups())
693 {
694 const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
695
696 if (group->channels().size() <= 1)
697 continue;
698
699 shared_ptr<TraceGroup> trace_group(new TraceGroup());
700 int child_offset = 0;
701 if (add_channels_to_owner(group->channels(),
702 trace_group.get(), child_offset, signal_map))
703 {
704 add_child_item(trace_group);
705 apply_offset(trace_group, offset);
706 }
707 }
708
709 // Add the remaining logic channels
710 shared_ptr<TraceGroup> logic_trace_group(new TraceGroup());
711 int child_offset = 0;
712
713 if (add_channels_to_owner(device->channels(),
714 logic_trace_group.get(), child_offset, signal_map,
715 [](shared_ptr<RowItem> r) -> bool {
716 return dynamic_pointer_cast<LogicSignal>(r) != nullptr;
717 }))
68b21a71 718
448a72cf
JH
719 {
720 add_child_item(logic_trace_group);
721 apply_offset(logic_trace_group, offset);
722 }
723
724 // Add the remaining channels
725 add_channels_to_owner(device->channels(), this, offset, signal_map);
726 assert(signal_map.empty());
727
728 // Add decode signals
68b21a71
JH
729#ifdef ENABLE_DECODE
730 const vector< shared_ptr<DecodeTrace> > decode_sigs(
731 session().get_decode_signals());
448a72cf 732 for (auto s : decode_sigs) {
68b21a71 733 add_child_item(s);
448a72cf 734 apply_offset(s, offset);
ef8311a4 735 }
448a72cf 736#endif
ef8311a4 737
a6c1726e 738 update_layout();
69dd2b03
JH
739}
740
cdf7bea7 741void View::data_updated()
adb4b10c 742{
adb4b10c
JH
743 // Update the scroll bars
744 update_scroll();
745
746 // Repaint the view
8dbbc7f0 747 viewport_->update();
adb4b10c 748}
cdf7bea7 749
07204819 750void View::on_signals_moved()
54401bbb 751{
07204819
JH
752 update_scroll();
753 signals_moved();
54401bbb
JH
754}
755
32218d3e 756void View::process_sticky_events()
d7c0ca4a 757{
76fea660 758 if (sticky_events_ & RowItemHExtentsChanged)
32218d3e 759 update_layout();
76fea660 760 if (sticky_events_ & RowItemVExtentsChanged)
7ff0145f 761 restack_all_row_items();
32218d3e
JH
762
763 // Clear the sticky events
8dbbc7f0 764 sticky_events_ = 0;
d7c0ca4a
JH
765}
766
33c62f44
JH
767void View::on_hover_point_changed()
768{
aa59d5c2 769 for (shared_ptr<RowItem> r : *this)
eae6e30a 770 r->hover_point_changed();
33c62f44
JH
771}
772
cdf7bea7
JH
773} // namespace view
774} // namespace pv