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