]> sigrok.org Git - pulseview.git/blob - pv/view/view.cpp
Session: Renamed files to match class name
[pulseview.git] / pv / view / view.cpp
1 /*
2  * This file is part of the PulseView project.
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
21 #ifdef ENABLE_DECODE
22 #include <libsigrokdecode/libsigrokdecode.h>
23 #endif
24
25 #include <cassert>
26 #include <climits>
27 #include <cmath>
28 #include <mutex>
29 #include <unordered_set>
30
31 #include <QEvent>
32 #include <QMouseEvent>
33 #include <QScrollBar>
34
35 #include <libsigrok/libsigrok.hpp>
36
37 #include "cursorheader.hpp"
38 #include "decodetrace.hpp"
39 #include "header.hpp"
40 #include "logicsignal.hpp"
41 #include "ruler.hpp"
42 #include "signal.hpp"
43 #include "tracegroup.hpp"
44 #include "view.hpp"
45 #include "viewport.hpp"
46
47 #include "pv/session.hpp"
48 #include "pv/data/logic.hpp"
49 #include "pv/data/logicsnapshot.hpp"
50
51 using boost::shared_lock;
52 using boost::shared_mutex;
53 using pv::data::SignalData;
54 using std::back_inserter;
55 using std::deque;
56 using std::dynamic_pointer_cast;
57 using std::list;
58 using std::lock_guard;
59 using std::max;
60 using std::make_pair;
61 using std::min;
62 using std::pair;
63 using std::set;
64 using std::shared_ptr;
65 using std::unordered_map;
66 using std::unordered_set;
67 using std::vector;
68 using std::weak_ptr;
69
70 namespace pv {
71 namespace view {
72
73 const double View::MaxScale = 1e9;
74 const double View::MinScale = 1e-15;
75
76 const int View::MaxScrollValue = INT_MAX / 2;
77
78 const QColor View::CursorAreaColour(220, 231, 243);
79
80 const QSizeF View::LabelPadding(4, 0);
81
82 View::View(Session &session, QWidget *parent) :
83         QAbstractScrollArea(parent),
84         session_(session),
85         viewport_(new Viewport(*this)),
86         ruler_(new Ruler(*this)),
87         cursorheader_(new CursorHeader(*this)),
88         header_(new Header(*this)),
89         scale_(1e-6),
90         offset_(0),
91         v_offset_(0),
92         updating_scroll_(false),
93         show_cursors_(false),
94         cursors_(*this),
95         hover_point_(-1, -1)
96 {
97         connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
98                 this, SLOT(h_scroll_value_changed(int)));
99         connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
100                 this, SLOT(v_scroll_value_changed(int)));
101
102         connect(&session_, SIGNAL(signals_changed()),
103                 this, SLOT(signals_changed()));
104         connect(&session_, SIGNAL(capture_state_changed(int)),
105                 this, SLOT(data_updated()));
106         connect(&session_, SIGNAL(data_received()),
107                 this, SLOT(data_updated()));
108         connect(&session_, SIGNAL(frame_ended()),
109                 this, SLOT(data_updated()));
110
111         connect(cursors_.first().get(), SIGNAL(time_changed()),
112                 this, SLOT(marker_time_changed()));
113         connect(cursors_.second().get(), SIGNAL(time_changed()),
114                 this, SLOT(marker_time_changed()));
115
116         connect(header_, SIGNAL(signals_moved()),
117                 this, SLOT(on_signals_moved()));
118
119         connect(header_, SIGNAL(selection_changed()),
120                 cursorheader_, SLOT(clear_selection()));
121         connect(cursorheader_, SIGNAL(selection_changed()),
122                 header_, SLOT(clear_selection()));
123
124         connect(header_, SIGNAL(selection_changed()),
125                 this, SIGNAL(selection_changed()));
126         connect(cursorheader_, SIGNAL(selection_changed()),
127                 this, SIGNAL(selection_changed()));
128
129         connect(this, SIGNAL(hover_point_changed()),
130                 this, SLOT(on_hover_point_changed()));
131
132         connect(&lazy_event_handler_, SIGNAL(timeout()),
133                 this, SLOT(process_sticky_events()));
134         lazy_event_handler_.setSingleShot(true);
135
136         setViewport(viewport_);
137
138         viewport_->installEventFilter(this);
139         ruler_->installEventFilter(this);
140         cursorheader_->installEventFilter(this);
141         header_->installEventFilter(this);
142
143         // Trigger the initial event manually. The default device has signals
144         // which were created before this object came into being
145         signals_changed();
146
147         // make sure the transparent widgets are on the top
148         cursorheader_->raise();
149         header_->raise();
150 }
151
152 Session& View::session()
153 {
154         return session_;
155 }
156
157 const Session& View::session() const
158 {
159         return session_;
160 }
161
162 View* View::view()
163 {
164         return this;
165 }
166
167 const View* View::view() const
168 {
169         return this;
170 }
171
172 Viewport* View::viewport()
173 {
174         return viewport_;
175 }
176
177 const Viewport* View::viewport() const
178 {
179         return viewport_;
180 }
181
182 double View::scale() const
183 {
184         return scale_;
185 }
186
187 double View::offset() const
188 {
189         return offset_;
190 }
191
192 int View::owner_visual_v_offset() const
193 {
194         return -v_offset_;
195 }
196
197 unsigned int View::depth() const
198 {
199         return 0;
200 }
201
202 void View::zoom(double steps)
203 {
204         zoom(steps, viewport_->width() / 2);
205 }
206
207 void View::zoom(double steps, int offset)
208 {
209         set_zoom(scale_ * pow(3.0/2.0, -steps), offset);
210 }
211
212 void View::zoom_fit()
213 {
214         const pair<double, double> extents = get_time_extents();
215         const double delta = extents.second - extents.first;
216         if (delta < 1e-12)
217                 return;
218
219         assert(viewport_);
220         const int w = viewport_->width();
221         if (w <= 0)
222                 return;
223
224         const double scale = max(min(delta / w, MaxScale), MinScale);
225         set_scale_offset(scale, extents.first);
226 }
227
228 void View::zoom_one_to_one()
229 {
230         using pv::data::SignalData;
231
232         // Make a set of all the visible data objects
233         set< shared_ptr<SignalData> > visible_data = get_visible_data();
234         if (visible_data.empty())
235                 return;
236
237         double samplerate = 0.0;
238         for (const shared_ptr<SignalData> d : visible_data) {
239                 assert(d);
240                 samplerate = max(samplerate, d->samplerate());
241         }
242
243         if (samplerate == 0.0)
244                 return;
245
246         assert(viewport_);
247         const int w = viewport_->width();
248         if (w <= 0)
249                 return;
250
251         set_zoom(1.0 / samplerate, w / 2);
252 }
253
254 void View::set_scale_offset(double scale, double offset)
255 {
256         scale_ = scale;
257         offset_ = offset;
258
259         update_scroll();
260         ruler_->update();
261         cursorheader_->update();
262         viewport_->update();
263         scale_offset_changed();
264 }
265
266 set< shared_ptr<SignalData> > View::get_visible_data() const
267 {
268         shared_lock<shared_mutex> lock(session().signals_mutex());
269         const vector< shared_ptr<Signal> > &sigs(session().signals());
270
271         // Make a set of all the visible data objects
272         set< shared_ptr<SignalData> > visible_data;
273         for (const shared_ptr<Signal> sig : sigs)
274                 if (sig->enabled())
275                         visible_data.insert(sig->data());
276
277         return visible_data;
278 }
279
280 pair<double, double> View::get_time_extents() const
281 {
282         const set< shared_ptr<SignalData> > visible_data = get_visible_data();
283         if (visible_data.empty())
284                 return make_pair(0.0, 0.0);
285
286         double left_time = DBL_MAX, right_time = DBL_MIN;
287         for (const shared_ptr<SignalData> d : visible_data)
288         {
289                 const double start_time = d->get_start_time();
290                 double samplerate = d->samplerate();
291                 samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
292
293                 left_time = min(left_time, start_time);
294                 right_time = max(right_time, start_time +
295                         d->get_max_sample_count() / samplerate);
296         }
297
298         assert(left_time < right_time);
299         return make_pair(left_time, right_time);
300 }
301
302 bool View::cursors_shown() const
303 {
304         return show_cursors_;
305 }
306
307 void View::show_cursors(bool show)
308 {
309         show_cursors_ = show;
310         cursorheader_->update();
311         viewport_->update();
312 }
313
314 void View::centre_cursors()
315 {
316         const double time_width = scale_ * viewport_->width();
317         cursors_.first()->set_time(offset_ + time_width * 0.4);
318         cursors_.second()->set_time(offset_ + time_width * 0.6);
319         cursorheader_->update();
320         viewport_->update();
321 }
322
323 CursorPair& View::cursors()
324 {
325         return cursors_;
326 }
327
328 const CursorPair& View::cursors() const
329 {
330         return cursors_;
331 }
332
333 const QPoint& View::hover_point() const
334 {
335         return hover_point_;
336 }
337
338 void View::update_viewport()
339 {
340         assert(viewport_);
341         viewport_->update();
342         header_->update();
343 }
344
345 void View::restack_all_row_items()
346 {
347         // Make a set of owners
348         unordered_set< RowItemOwner* > owners;
349         for (const auto &r : *this)
350                 owners.insert(r->owner());
351
352         // Make a list that is sorted from deepest first
353         vector< RowItemOwner* > sorted_owners(owners.begin(), owners.end());
354         sort(sorted_owners.begin(), sorted_owners.end(),
355                 [](const RowItemOwner* a, const RowItemOwner *b) {
356                         return a->depth() > b->depth(); });
357
358         // Restack the items recursively
359         for (auto &o : sorted_owners)
360                 o->restack_items();
361
362         // Animate the items to their destination
363         for (const auto &r : *this)
364                 r->animate_to_layout_v_offset();
365 }
366
367 void View::get_scroll_layout(double &length, double &offset) const
368 {
369         const pair<double, double> extents = get_time_extents();
370         length = (extents.second - extents.first) / scale_;
371         offset = offset_ / scale_;
372 }
373
374 void View::set_zoom(double scale, int offset)
375 {
376         const double cursor_offset = offset_ + scale_ * offset;
377         const double new_scale = max(min(scale, MaxScale), MinScale);
378         const double new_offset = cursor_offset - new_scale * offset;
379         set_scale_offset(new_scale, new_offset);
380 }
381
382 void View::update_scroll()
383 {
384         assert(viewport_);
385
386         const QSize areaSize = viewport_->size();
387
388         // Set the horizontal scroll bar
389         double length = 0, offset = 0;
390         get_scroll_layout(length, offset);
391         length = max(length - areaSize.width(), 0.0);
392
393         horizontalScrollBar()->setPageStep(areaSize.width() / 2);
394
395         updating_scroll_ = true;
396
397         if (length < MaxScrollValue) {
398                 horizontalScrollBar()->setRange(0, length);
399                 horizontalScrollBar()->setSliderPosition(offset);
400         } else {
401                 horizontalScrollBar()->setRange(0, MaxScrollValue);
402                 horizontalScrollBar()->setSliderPosition(
403                         offset_ * MaxScrollValue / (scale_ * length));
404         }
405
406         updating_scroll_ = false;
407
408         // Set the vertical scrollbar
409         verticalScrollBar()->setPageStep(areaSize.height());
410
411         const pair<int, int> extents = v_extents();
412         const int extra_scroll_height = (extents.second - extents.first) / 4;
413         verticalScrollBar()->setRange(extents.first - extra_scroll_height,
414                 extents.first + extra_scroll_height);
415 }
416
417 void View::update_layout()
418 {
419         setViewportMargins(
420                 header_->sizeHint().width() - pv::view::Header::BaselineOffset,
421                 ruler_->sizeHint().height(), 0, 0);
422         ruler_->setGeometry(viewport_->x(), 0,
423                 viewport_->width(), viewport_->y());
424         cursorheader_->setGeometry(
425                 viewport_->x(),
426                 ruler_->sizeHint().height() - cursorheader_->sizeHint().height() / 2,
427                 viewport_->width(), cursorheader_->sizeHint().height());
428         header_->setGeometry(0, viewport_->y(),
429                 header_->sizeHint().width(), viewport_->height());
430         update_scroll();
431 }
432
433 void View::paint_label(QPainter &p, int right, bool hover)
434 {
435         (void)p;
436         (void)right;
437         (void)hover;
438 }
439
440 QRectF View::label_rect(int right)
441 {
442         (void)right;
443         return QRectF();
444 }
445
446 bool View::add_channels_to_owner(
447         const vector< shared_ptr<sigrok::Channel> > &channels,
448         RowItemOwner *owner, int &offset,
449         unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
450                 &signal_map,
451         std::function<bool (shared_ptr<RowItem>)> filter_func)
452 {
453         bool any_added = false;
454
455         assert(owner);
456
457         for (const auto &channel : channels)
458         {
459                 const auto iter = signal_map.find(channel);
460                 if (iter == signal_map.end() ||
461                         (filter_func && !filter_func((*iter).second)))
462                         continue;
463
464                 shared_ptr<RowItem> row_item = (*iter).second;
465                 owner->add_child_item(row_item);
466                 apply_offset(row_item, offset);
467                 signal_map.erase(iter);
468
469                 any_added = true;
470         }
471
472         return any_added;
473 }
474
475 void View::apply_offset(shared_ptr<RowItem> row_item, int &offset) {
476         assert(row_item);
477         const pair<int, int> extents = row_item->v_extents();
478         if (row_item->enabled())
479                 offset += -extents.first;
480         row_item->force_to_v_offset(offset);
481         if (row_item->enabled())
482                 offset += extents.second;
483 }
484
485 bool View::eventFilter(QObject *object, QEvent *event)
486 {
487         const QEvent::Type type = event->type();
488         if (type == QEvent::MouseMove) {
489
490                 const QMouseEvent *const mouse_event = (QMouseEvent*)event;
491                 if (object == viewport_)
492                         hover_point_ = mouse_event->pos();
493                 else if (object == ruler_ || object == cursorheader_)
494                         hover_point_ = QPoint(mouse_event->x(), 0);
495                 else if (object == header_)
496                         hover_point_ = QPoint(0, mouse_event->y());
497                 else
498                         hover_point_ = QPoint(-1, -1);
499
500                 hover_point_changed();
501
502         } else if (type == QEvent::Leave) {
503                 hover_point_ = QPoint(-1, -1);
504                 hover_point_changed();
505         }
506
507         return QObject::eventFilter(object, event);
508 }
509
510 bool View::viewportEvent(QEvent *e)
511 {
512         switch(e->type()) {
513         case QEvent::Paint:
514         case QEvent::MouseButtonPress:
515         case QEvent::MouseButtonRelease:
516         case QEvent::MouseButtonDblClick:
517         case QEvent::MouseMove:
518         case QEvent::Wheel:
519         case QEvent::TouchBegin:
520         case QEvent::TouchUpdate:
521         case QEvent::TouchEnd:
522                 return false;
523
524         default:
525                 return QAbstractScrollArea::viewportEvent(e);
526         }
527 }
528
529 void View::resizeEvent(QResizeEvent*)
530 {
531         update_layout();
532 }
533
534 void View::appearance_changed(bool label, bool content)
535 {
536         if (label)
537                 header_->update();
538         if (content)
539                 viewport_->update();
540 }
541
542 void View::extents_changed(bool horz, bool vert)
543 {
544         sticky_events_ |=
545                 (horz ? SelectableItemHExtentsChanged : 0) |
546                 (vert ? SelectableItemVExtentsChanged : 0);
547         lazy_event_handler_.start();
548 }
549
550 void View::h_scroll_value_changed(int value)
551 {
552         if (updating_scroll_)
553                 return;
554
555         const int range = horizontalScrollBar()->maximum();
556         if (range < MaxScrollValue)
557                 offset_ = scale_ * value;
558         else {
559                 double length = 0, offset;
560                 get_scroll_layout(length, offset);
561                 offset_ = scale_ * length * value / MaxScrollValue;
562         }
563
564         ruler_->update();
565         cursorheader_->update();
566         viewport_->update();
567 }
568
569 void View::v_scroll_value_changed(int value)
570 {
571         v_offset_ = value;
572         header_->update();
573         viewport_->update();
574 }
575
576 void View::signals_changed()
577 {
578         int offset = 0;
579
580         // Populate the traces
581         clear_child_items();
582
583         shared_ptr<sigrok::Device> device = session_.device();
584         assert(device);
585
586         // Collect a set of signals
587         unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
588                 signal_map;
589
590         shared_lock<shared_mutex> lock(session_.signals_mutex());
591         const vector< shared_ptr<Signal> > &sigs(session_.signals());
592
593         for (const shared_ptr<Signal> &sig : sigs)
594                 signal_map[sig->channel()] = sig;
595
596         // Populate channel groups
597         for (auto entry : device->channel_groups())
598         {
599                 const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
600
601                 if (group->channels().size() <= 1)
602                         continue;
603
604                 shared_ptr<TraceGroup> trace_group(new TraceGroup());
605                 int child_offset = 0;
606                 if (add_channels_to_owner(group->channels(),
607                         trace_group.get(), child_offset, signal_map))
608                 {
609                         add_child_item(trace_group);
610                         apply_offset(trace_group, offset);
611                 }
612         }
613
614         // Add the remaining logic channels
615         shared_ptr<TraceGroup> logic_trace_group(new TraceGroup());
616         int child_offset = 0;
617
618         if (add_channels_to_owner(device->channels(),
619                 logic_trace_group.get(), child_offset, signal_map,
620                 [](shared_ptr<RowItem> r) -> bool {
621                         return dynamic_pointer_cast<LogicSignal>(r) != nullptr;
622                         }))
623
624         {
625                 add_child_item(logic_trace_group);
626                 apply_offset(logic_trace_group, offset);
627         }
628
629         // Add the remaining channels
630         add_channels_to_owner(device->channels(), this, offset, signal_map);
631         assert(signal_map.empty());
632
633         // Add decode signals
634 #ifdef ENABLE_DECODE
635         const vector< shared_ptr<DecodeTrace> > decode_sigs(
636                 session().get_decode_signals());
637         for (auto s : decode_sigs) {
638                 add_child_item(s);
639                 apply_offset(s, offset);
640         }
641 #endif
642
643         update_layout();
644 }
645
646 void View::data_updated()
647 {
648         // Update the scroll bars
649         update_scroll();
650
651         // Repaint the view
652         viewport_->update();
653 }
654
655 void View::marker_time_changed()
656 {
657         cursorheader_->update();
658         viewport_->update();
659 }
660
661 void View::on_signals_moved()
662 {
663         update_scroll();
664         signals_moved();
665 }
666
667 void View::process_sticky_events()
668 {
669         if (sticky_events_ & SelectableItemHExtentsChanged)
670                 update_layout();
671         if (sticky_events_ & SelectableItemVExtentsChanged)
672                 restack_all_row_items();
673
674         // Clear the sticky events
675         sticky_events_ = 0;
676 }
677
678 void View::on_hover_point_changed()
679 {
680         for (shared_ptr<RowItem> r : *this)
681                 r->hover_point_changed();
682 }
683
684 } // namespace view
685 } // namespace pv