PulseView  0.3.0
A Qt-based sigrok GUI
viewport.cpp
Go to the documentation of this file.
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 #include <cassert>
22 #include <cmath>
23 #include <algorithm>
24 #include <limits>
25 
26 #include "signal.hpp"
27 #include "view.hpp"
28 #include "viewitempaintparams.hpp"
29 #include "viewport.hpp"
30 
31 #include <pv/session.hpp>
32 
33 #include <QMouseEvent>
34 
35 using std::abs;
36 using std::back_inserter;
37 using std::copy;
38 using std::dynamic_pointer_cast;
39 using std::max;
40 using std::min;
41 using std::none_of;
42 using std::numeric_limits;
43 using std::shared_ptr;
44 using std::stable_sort;
45 using std::vector;
46 
47 namespace pv {
48 namespace view {
49 
51  ViewWidget(parent),
52  pinch_zoom_active_(false)
53 {
54  setAutoFillBackground(true);
55  setBackgroundRole(QPalette::Base);
56 }
57 
58 shared_ptr<ViewItem> Viewport::get_mouse_over_item(const QPoint &pt)
59 {
60  const ViewItemPaintParams pp(rect(), view_.scale(), view_.offset());
61  const vector< shared_ptr<ViewItem> > items(this->items());
62  for (auto i = items.rbegin(); i != items.rend(); i++)
63  if ((*i)->enabled() &&
64  (*i)->hit_box_rect(pp).contains(pt))
65  return *i;
66  return nullptr;
67 }
68 
69 void Viewport::item_hover(const shared_ptr<ViewItem> &item)
70 {
71  if (item && item->is_draggable())
72  setCursor(dynamic_pointer_cast<RowItem>(item) ?
73  Qt::SizeVerCursor : Qt::SizeHorCursor);
74  else
75  unsetCursor();
76 }
77 
79 {
82 }
83 
84 void Viewport::drag_by(const QPoint &delta)
85 {
86  if (drag_offset_ == boost::none)
87  return;
88 
90  (*drag_offset_ - delta.x() * view_.scale()));
91 
92  view_.set_v_offset(-drag_v_offset_ - delta.y());
93 }
94 
96 {
97  drag_offset_ = boost::none;
98 }
99 
100 vector< shared_ptr<ViewItem> > Viewport::items()
101 {
102  vector< shared_ptr<ViewItem> > items;
103  const std::vector< shared_ptr<ViewItem> > view_items(
105  copy(view_items.begin(), view_items.end(), back_inserter(items));
106  const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
107  copy(time_items.begin(), time_items.end(), back_inserter(items));
108  return items;
109 }
110 
111 bool Viewport::touch_event(QTouchEvent *event)
112 {
113  QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
114 
115  if (touchPoints.count() != 2) {
116  pinch_zoom_active_ = false;
117  return false;
118  }
119 
120  const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
121  const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
122 
123  if (!pinch_zoom_active_ ||
124  (event->touchPointStates() & Qt::TouchPointPressed)) {
125  pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.pos().x()).convert_to<double>();
126  pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.pos().x()).convert_to<double>();
127  pinch_zoom_active_ = true;
128  }
129 
130  double w = touchPoint1.pos().x() - touchPoint0.pos().x();
131  if (abs(w) >= 1.0) {
132  const double scale =
133  fabs((pinch_offset1_ - pinch_offset0_) / w);
134  double offset = pinch_offset0_ - touchPoint0.pos().x() * scale;
135  if (scale > 0)
136  view_.set_scale_offset(scale, offset);
137  }
138 
139  if (event->touchPointStates() & Qt::TouchPointReleased) {
140  pinch_zoom_active_ = false;
141 
142  if (touchPoint0.state() & Qt::TouchPointReleased) {
143  // Primary touch released
144  drag_release();
145  } else {
146  // Update the mouse down fields so that continued
147  // dragging with the primary touch will work correctly
148  mouse_down_point_ = touchPoint0.pos().toPoint();
149  drag();
150  }
151  }
152 
153  return true;
154 }
155 
156 void Viewport::paintEvent(QPaintEvent*)
157 {
158  vector< shared_ptr<RowItem> > row_items(view_.list_by_type<RowItem>());
159  assert(none_of(row_items.begin(), row_items.end(),
160  [](const shared_ptr<RowItem> &r) { return !r; }));
161 
162  stable_sort(row_items.begin(), row_items.end(),
163  [](const shared_ptr<RowItem> &a, const shared_ptr<RowItem> &b) {
164  return a->point(QRect()).y() < b->point(QRect()).y(); });
165 
166  const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
167  assert(none_of(time_items.begin(), time_items.end(),
168  [](const shared_ptr<TimeItem> &t) { return !t; }));
169 
170  QPainter p(this);
171  p.setRenderHint(QPainter::Antialiasing);
172 
173  const ViewItemPaintParams pp(rect(), view_.scale(), view_.offset());
174 
175  for (const shared_ptr<TimeItem> t : time_items)
176  t->paint_back(p, pp);
177  for (const shared_ptr<RowItem> r : row_items)
178  r->paint_back(p, pp);
179 
180  for (const shared_ptr<TimeItem> t : time_items)
181  t->paint_mid(p, pp);
182  for (const shared_ptr<RowItem> r : row_items)
183  r->paint_mid(p, pp);
184 
185  for (const shared_ptr<RowItem> r : row_items)
186  r->paint_fore(p, pp);
187 
188  p.setRenderHint(QPainter::Antialiasing, false);
189  for (const shared_ptr<TimeItem> t : time_items)
190  t->paint_fore(p, pp);
191 
192  p.end();
193 }
194 
195 void Viewport::mouseDoubleClickEvent(QMouseEvent *event)
196 {
197  assert(event);
198 
199  if (event->buttons() & Qt::LeftButton)
200  view_.zoom(2.0, event->x());
201  else if (event->buttons() & Qt::RightButton)
202  view_.zoom(-2.0, event->x());
203 }
204 
205 void Viewport::wheelEvent(QWheelEvent *e)
206 {
207  assert(e);
208 
209  if (e->orientation() == Qt::Vertical) {
210  if (e->modifiers() & Qt::ControlModifier) {
211  // Vertical scrolling with the control key pressed
212  // is intrepretted as vertical scrolling
214  (e->delta() * height()) / (8 * 120));
215  } else {
216  // Vertical scrolling is interpreted as zooming in/out
217  view_.zoom(e->delta() / 120, e->x());
218  }
219  } else if (e->orientation() == Qt::Horizontal) {
220  // Horizontal scrolling is interpreted as moving left/right
222  e->delta() * view_.scale() + view_.offset());
223  }
224 }
225 
226 } // namespace view
227 } // namespace pv
int owner_visual_v_offset() const
Definition: view.cpp:246
void item_hover(const std::shared_ptr< pv::view::ViewItem > &item)
Definition: viewport.cpp:69
std::vector< std::shared_ptr< T > > list_by_type()
pv::view::View & view_
Definition: viewwidget.hpp:142
void set_scale_offset(double scale, const pv::util::Timestamp &offset)
Definition: view.cpp:367
std::shared_ptr< pv::view::ViewItem > get_mouse_over_item(const QPoint &pt)
Definition: viewport.cpp:58
void zoom(double steps)
Definition: view.cpp:315
double scale() const
Definition: view.cpp:220
void paintEvent(QPaintEvent *event)
Definition: viewport.cpp:156
void wheelEvent(QWheelEvent *event)
Definition: viewport.cpp:205
void set_v_offset(int offset)
Definition: view.cpp:251
std::vector< std::shared_ptr< TimeItem > > time_items() const
Definition: view.cpp:206
std::vector< std::shared_ptr< pv::view::ViewItem > > items()
Definition: viewport.cpp:100
boost::optional< pv::util::Timestamp > drag_offset_
Definition: viewport.hpp:99
Viewport(View &parent)
Definition: viewport.cpp:50
void drag_by(const QPoint &delta)
Definition: viewport.cpp:84
void mouseDoubleClickEvent(QMouseEvent *event)
Definition: viewport.cpp:195
const pv::util::Timestamp & offset() const
Definition: view.cpp:233
bool touch_event(QTouchEvent *e)
Definition: viewport.cpp:111