PulseView  unreleased development snapshot
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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <algorithm>
21 #include <cassert>
22 #include <cmath>
23 #include <limits>
24 
25 #include "signal.hpp"
26 #include "view.hpp"
27 #include "viewitempaintparams.hpp"
28 #include "viewport.hpp"
29 
30 #include <pv/session.hpp>
31 
32 #include <QMouseEvent>
33 #include <QScreen>
34 #include <QWindow>
35 
36 #include <QDebug>
37 
38 using std::abs;
39 using std::back_inserter;
40 using std::copy;
41 using std::dynamic_pointer_cast;
42 using std::none_of; // NOLINT. Used in assert()s.
43 using std::shared_ptr;
44 using std::stable_sort;
45 using std::vector;
46 
47 namespace pv {
48 namespace views {
49 namespace trace {
50 
52  ViewWidget(parent),
53  pinch_zoom_active_(false)
54 {
55  setAutoFillBackground(true);
56  setBackgroundRole(QPalette::Base);
57 
58  // Set up settings and event handlers
59  GlobalSettings settings;
61 
63 }
64 
66 {
68 }
69 
70 shared_ptr<ViewItem> Viewport::get_mouse_over_item(const QPoint &pt)
71 {
72  const ViewItemPaintParams pp(rect(), view_.scale(), view_.offset());
73  const vector< shared_ptr<ViewItem> > items(this->items());
74  for (auto i = items.rbegin(); i != items.rend(); i++)
75  if ((*i)->enabled() && (*i)->hit_box_rect(pp).contains(pt))
76  return *i;
77  return nullptr;
78 }
79 
80 void Viewport::item_hover(const shared_ptr<ViewItem> &item, QPoint pos)
81 {
82  if (item && item->is_draggable(pos))
83  setCursor(dynamic_pointer_cast<ViewItem>(item) ?
84  Qt::SizeHorCursor : Qt::SizeVerCursor);
85  else
86  unsetCursor();
87 }
88 
90 {
92 
95 }
96 
97 void Viewport::drag_by(const QPoint &delta)
98 {
99  if (drag_offset_ == boost::none)
100  return;
101 
103  (*drag_offset_ - delta.x() * view_.scale()));
104 
106  view_.set_v_offset(-drag_v_offset_ - delta.y());
107 }
108 
110 {
111  drag_offset_ = boost::none;
112 }
113 
114 vector< shared_ptr<ViewItem> > Viewport::items()
115 {
116  vector< shared_ptr<ViewItem> > items;
117  const vector< shared_ptr<ViewItem> > view_items(
119  copy(view_items.begin(), view_items.end(), back_inserter(items));
120  const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
121  copy(time_items.begin(), time_items.end(), back_inserter(items));
122  return items;
123 }
124 
125 bool Viewport::touch_event(QTouchEvent *event)
126 {
127 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
128  QList<QEventPoint> touchPoints = event->points();
129 #else
130  QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
131 #endif
132 
133  if (touchPoints.count() != 2) {
134  pinch_zoom_active_ = false;
135  return false;
136  }
137 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
138  if (event->device()->type() == QInputDevice::DeviceType::TouchPad) {
139  return false;
140  }
141 
142  const QEventPoint &touchPoint0 = touchPoints.first();
143  const QEventPoint &touchPoint1 = touchPoints.last();
144 
145  if (!pinch_zoom_active_ ||
146  (event->touchPointStates() & QEventPoint::Pressed)) {
147  pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.position().x()).convert_to<double>();
148  pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.position().x()).convert_to<double>();
149  pinch_zoom_active_ = true;
150  }
151 
152  double w = touchPoint1.position().x() - touchPoint0.position().x();
153  if (abs(w) >= 1.0) {
154  const double scale =
155  fabs((pinch_offset1_ - pinch_offset0_) / w);
156  double offset = pinch_offset0_ - touchPoint0.position().x() * scale;
157  if (scale > 0)
158  view_.set_scale_offset(scale, offset);
159  }
160 #else
161  if (event->device()->type() == QTouchDevice::TouchPad) {
162  return false;
163  }
164 
165  const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
166  const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
167 
168  if (!pinch_zoom_active_ ||
169  (event->touchPointStates() & Qt::TouchPointPressed)) {
170  pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.pos().x()).convert_to<double>();
171  pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.pos().x()).convert_to<double>();
172  pinch_zoom_active_ = true;
173  }
174 
175  double w = touchPoint1.pos().x() - touchPoint0.pos().x();
176  if (abs(w) >= 1.0) {
177  const double scale =
178  fabs((pinch_offset1_ - pinch_offset0_) / w);
179  double offset = pinch_offset0_ - touchPoint0.pos().x() * scale;
180  if (scale > 0)
181  view_.set_scale_offset(scale, offset);
182  }
183 #endif
184 
185  if (event->touchPointStates() & Qt::TouchPointReleased) {
186  pinch_zoom_active_ = false;
187 
188  if (touchPoint0.state() & Qt::TouchPointReleased) {
189  // Primary touch released
190  drag_release();
191  } else {
192  // Update the mouse down fields so that continued
193  // dragging with the primary touch will work correctly
194 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
195  mouse_down_point_ = touchPoint0.position().toPoint();
196 #else
197  mouse_down_point_ = touchPoint0.pos().toPoint();
198 #endif
199  drag();
200  }
201  }
202 
203  return true;
204 }
205 
206 void Viewport::paintEvent(QPaintEvent*)
207 {
208  typedef void (ViewItem::*LayerPaintFunc)(
209  QPainter &p, ViewItemPaintParams &pp);
210  LayerPaintFunc layer_paint_funcs[] = {
212  &ViewItem::paint_fore, nullptr};
213 
214  vector< shared_ptr<ViewItem> > row_items(view_.list_by_type<ViewItem>());
215  assert(none_of(row_items.begin(), row_items.end(),
216  [](const shared_ptr<ViewItem> &r) { return !r; }));
217 
218  stable_sort(row_items.begin(), row_items.end(),
219  [](const shared_ptr<ViewItem> &a, const shared_ptr<ViewItem> &b) {
220  return a->drag_point(QRect()).y() < b->drag_point(QRect()).y(); });
221 
222  const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
223  assert(none_of(time_items.begin(), time_items.end(),
224  [](const shared_ptr<TimeItem> &t) { return !t; }));
225 
226  QPainter p(this);
227 
228  // Disable antialiasing for high-DPI displays
229  bool use_antialiasing =
230  window()->windowHandle()->screen()->devicePixelRatio() < 2.0;
231  p.setRenderHint(QPainter::Antialiasing, use_antialiasing);
232 
233  for (LayerPaintFunc *paint_func = layer_paint_funcs;
234  *paint_func; paint_func++) {
235  ViewItemPaintParams time_pp(rect(), view_.scale(), view_.offset());
236  for (const shared_ptr<TimeItem>& t : time_items)
237  (t.get()->*(*paint_func))(p, time_pp);
238 
239  ViewItemPaintParams row_pp(rect(), view_.scale(), view_.offset());
240  for (const shared_ptr<ViewItem>& r : row_items)
241  (r.get()->*(*paint_func))(p, row_pp);
242  }
243 
244  p.end();
245 }
246 
248 {
249  assert(event);
250 
251 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
252  if (event->buttons() & Qt::LeftButton)
253  view_.zoom(2.0, event->position().x());
254  else if (event->buttons() & Qt::RightButton)
255  view_.zoom(-2.0, event->position().x());
256 #else
257  if (event->buttons() & Qt::LeftButton)
258  view_.zoom(2.0, event->x());
259  else if (event->buttons() & Qt::RightButton)
260  view_.zoom(-2.0, event->x());
261 #endif
262 }
263 
264 void Viewport::wheelEvent(QWheelEvent *event)
265 {
266  assert(event);
267 
268 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
269  int delta = (event->angleDelta().x() != 0) ? event->angleDelta().x() : event->angleDelta().y();
270 #else
271  int delta = event->delta();
272 #endif
273 
274 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
275  if (event->angleDelta().y() != 0) {
276 #else
277  if (event->orientation() == Qt::Vertical) {
278 #endif
279  if (event->modifiers() & Qt::ControlModifier) {
280  // Vertical scrolling with the control key pressed
281  // is intrepretted as vertical scrolling
283  (delta * height()) / (8 * 120));
284  } else if (event->modifiers() & Qt::ShiftModifier) {
285  // Vertical scrolling with the shift key pressed
286  // acts as horizontal scrolling like in Gimp
287  // and Inkscape.
289  - delta * view_.scale() + view_.offset());
290  } else {
291  // Vertical scrolling is interpreted as zooming in/out
292 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
293  view_.zoom(delta / 120.0, event->position().x());
294 #else
295  view_.zoom(delta / 120.0, event->x());
296 #endif
297  }
298 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
299  } else if (event->angleDelta().x() != 0) {
300 #else
301  } else if (event->orientation() == Qt::Horizontal) {
302 #endif
303  // Horizontal scrolling is interpreted as moving left/right
305  delta * view_.scale() + view_.offset());
306  }
307 }
308 
309 void Viewport::on_setting_changed(const QString &key, const QVariant &value)
310 {
312  allow_vertical_dragging_ = value.toBool();
313 }
314 
315 } // namespace trace
316 } // namespace views
317 } // namespace pv
void item_hover(const shared_ptr< ViewItem > &item, QPoint pos)
Definition: viewport.cpp:80
void mouseDoubleClickEvent(QMouseEvent *event)
Definition: viewport.cpp:247
vector< shared_ptr< T > > list_by_type()
void drag_by(const QPoint &delta)
Definition: viewport.cpp:97
x y t t *t x y t t t x y t t t x *y t *t t x *y t *t t x y t t t x y t t t t(t+t)") define_sfop3(16
void set_v_offset(int offset)
Definition: view.cpp:657
const pv::util::Timestamp & offset() const
Definition: view.cpp:605
T value(details::expression_node< T > *n)
Definition: exprtk.hpp:12358
shared_ptr< ViewItem > get_mouse_over_item(const QPoint &pt)
Definition: viewport.cpp:70
void paintEvent(QPaintEvent *event)
Definition: viewport.cpp:206
vector< shared_ptr< TimeItem > > time_items() const
Definition: view.cpp:560
static void remove_change_handler(GlobalSettingsInterface *cb)
void set_scale_offset(double scale, const pv::util::Timestamp &offset)
Definition: view.cpp:889
boost::optional< pv::util::Timestamp > drag_offset_
Definition: viewport.hpp:107
pv::views::trace::View & view_
Definition: viewwidget.hpp:148
void wheelEvent(QWheelEvent *event)
Definition: viewport.cpp:264
static const QString Key_View_AllowVerticalDragging
void on_setting_changed(const QString &key, const QVariant &value)
Definition: viewport.cpp:309
vector< shared_ptr< ViewItem > > items()
Definition: viewport.cpp:114
int owner_visual_v_offset() const
Definition: view.cpp:652
void zoom(double steps)
Definition: view.cpp:824
virtual void paint_back(QPainter &p, ViewItemPaintParams &pp)
Definition: viewitem.cpp:128
Viewport(View &parent)
Definition: viewport.cpp:51
bool event(QEvent *event)
Definition: viewwidget.cpp:234
virtual void paint_mid(QPainter &p, ViewItemPaintParams &pp)
Definition: viewitem.cpp:134
virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp)
Definition: viewitem.cpp:140
static void add_change_handler(GlobalSettingsInterface *cb)
double scale() const
Definition: view.cpp:577
bool touch_event(QTouchEvent *event)
Definition: viewport.cpp:125