]> sigrok.org Git - pulseview.git/blob - pv/view/header.cpp
Replaced BOOST_FOREACH with C++11 range-based for loops
[pulseview.git] / pv / view / header.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 #include "header.h"
22 #include "view.h"
23
24 #include "signal.h"
25 #include "../sigsession.h"
26
27 #include <assert.h>
28
29 #include <QApplication>
30 #include <QMenu>
31 #include <QMouseEvent>
32 #include <QPainter>
33 #include <QRect>
34
35 #include <pv/widgets/popup.h>
36
37 using boost::shared_ptr;
38 using std::max;
39 using std::make_pair;
40 using std::pair;
41 using std::vector;
42
43 namespace pv {
44 namespace view {
45
46 const int Header::Padding = 12;
47
48 Header::Header(View &parent) :
49         MarginWidget(parent),
50         _dragging(false)
51 {
52         setFocusPolicy(Qt::ClickFocus);
53         setMouseTracking(true);
54
55         connect(&_view.session(), SIGNAL(signals_changed()),
56                 this, SLOT(on_signals_changed()));
57
58         connect(&_view, SIGNAL(signals_moved()),
59                 this, SLOT(on_signals_moved()));
60
61         // Trigger the initial event manually. The default device has signals
62         // which were created before this object came into being
63         on_signals_changed();
64 }
65
66 QSize Header::sizeHint() const
67 {
68         int max_width = 0;
69
70         const vector< shared_ptr<Trace> > traces(_view.get_traces());
71         for (shared_ptr<Trace> t : traces) {
72                 assert(t);
73
74                 if (t->enabled()) {
75                         max_width = max(max_width, (int)t->get_label_rect(0).width());
76                 }
77         }
78
79         return QSize(max_width + Padding, 0);
80 }
81
82 shared_ptr<Trace> Header::get_mouse_over_trace(const QPoint &pt)
83 {
84         const int w = width();
85         const vector< shared_ptr<Trace> > traces(_view.get_traces());
86
87         for (const shared_ptr<Trace> t : traces)
88         {
89                 assert(t);
90                 if (t->pt_in_label_rect(0, w, pt))
91                         return t;
92         }
93
94         return shared_ptr<Trace>();
95 }
96
97 void Header::clear_selection()
98 {
99         const vector< shared_ptr<Trace> > traces(_view.get_traces());
100         for (const shared_ptr<Trace> t : traces) {
101                 assert(t);
102                 t->select(false);
103         }
104
105         update();
106 }
107
108 void Header::paintEvent(QPaintEvent*)
109 {
110         const int w = width();
111         const vector< shared_ptr<Trace> > traces(_view.get_traces());
112
113         QPainter painter(this);
114         painter.setRenderHint(QPainter::Antialiasing);
115
116         const bool dragging = !_drag_traces.empty();
117         for (const shared_ptr<Trace> t : traces)
118         {
119                 assert(t);
120
121                 const bool highlight = !dragging && t->pt_in_label_rect(
122                         0, w, _mouse_point);
123                 t->paint_label(painter, w, highlight);
124         }
125
126         painter.end();
127 }
128
129 void Header::mousePressEvent(QMouseEvent *event)
130 {
131         assert(event);
132
133         const vector< shared_ptr<Trace> > traces(_view.get_traces());
134
135         if (event->button() & Qt::LeftButton) {
136                 _mouse_down_point = event->pos();
137
138                 // Save the offsets of any signals which will be dragged
139                 for (const shared_ptr<Trace> t : traces)
140                         if (t->selected())
141                                 _drag_traces.push_back(
142                                         make_pair(t, t->get_v_offset()));
143         }
144
145         // Select the signal if it has been clicked
146         const shared_ptr<Trace> mouse_over_trace =
147                 get_mouse_over_trace(event->pos());
148         if (mouse_over_trace) {
149                 if (mouse_over_trace->selected())
150                         mouse_over_trace->select(false);
151                 else {
152                         mouse_over_trace->select(true);
153
154                         if (~QApplication::keyboardModifiers() &
155                                 Qt::ControlModifier)
156                                 _drag_traces.clear();
157
158                         // Add the signal to the drag list
159                         if (event->button() & Qt::LeftButton)
160                                 _drag_traces.push_back(
161                                         make_pair(mouse_over_trace,
162                                         mouse_over_trace->get_v_offset()));
163                 }
164         }
165
166         if (~QApplication::keyboardModifiers() & Qt::ControlModifier) {
167                 // Unselect all other signals because the Ctrl is not
168                 // pressed
169                 for (const shared_ptr<Trace> t : traces)
170                         if (t != mouse_over_trace)
171                                 t->select(false);
172         }
173
174         selection_changed();
175         update();
176 }
177
178 void Header::mouseReleaseEvent(QMouseEvent *event)
179 {
180         using pv::widgets::Popup;
181
182         assert(event);
183         if (event->button() == Qt::LeftButton) {
184                 if (_dragging)
185                         _view.normalize_layout();
186                 else
187                 {
188                         const shared_ptr<Trace> mouse_over_trace =
189                                 get_mouse_over_trace(event->pos());
190                         if (mouse_over_trace) {
191                                 Popup *const p =
192                                         mouse_over_trace->create_popup(&_view);
193                                 p->set_position(mapToGlobal(QPoint(width(),
194                                         mouse_over_trace->get_y())),
195                                         Popup::Right);
196                                 p->show();
197                         }
198                 }
199
200                 _dragging = false;
201                 _drag_traces.clear();
202         }
203 }
204
205 void Header::mouseMoveEvent(QMouseEvent *event)
206 {
207         assert(event);
208         _mouse_point = event->pos();
209
210         if (!(event->buttons() & Qt::LeftButton))
211                 return;
212
213         if ((event->pos() - _mouse_down_point).manhattanLength() <
214                 QApplication::startDragDistance())
215                 return;
216
217         // Move the signals if we are dragging
218         if (!_drag_traces.empty())
219         {
220                 _dragging = true;
221
222                 const int delta = event->pos().y() - _mouse_down_point.y();
223
224                 for (auto i = _drag_traces.begin(); i != _drag_traces.end(); i++) {
225                         const boost::shared_ptr<Trace> trace((*i).first);
226                         if (trace) {
227                                 const int y = (*i).second + delta;
228                                 const int y_snap =
229                                         ((y + View::SignalSnapGridSize / 2) /
230                                                 View::SignalSnapGridSize) *
231                                                 View::SignalSnapGridSize;
232                                 trace->set_v_offset(y_snap);
233
234                                 // Ensure the trace is selected
235                                 trace->select();
236                         }
237                         
238                 }
239
240                 signals_moved();
241         }
242
243         update();
244 }
245
246 void Header::leaveEvent(QEvent*)
247 {
248         _mouse_point = QPoint(-1, -1);
249         update();
250 }
251
252 void Header::contextMenuEvent(QContextMenuEvent *event)
253 {
254         const shared_ptr<Trace> t = get_mouse_over_trace(_mouse_point);
255
256         if (t)
257                 t->create_context_menu(this)->exec(event->globalPos());
258 }
259
260 void Header::keyPressEvent(QKeyEvent *e)
261 {
262         assert(e);
263
264         switch (e->key())
265         {
266         case Qt::Key_Delete:
267         {
268                 const vector< shared_ptr<Trace> > traces(_view.get_traces());
269                 for (const shared_ptr<Trace> t : traces)
270                         if (t->selected())
271                                 t->delete_pressed();    
272                 break;
273         }
274         }
275 }
276
277 void Header::on_signals_changed()
278 {
279         const vector< shared_ptr<Trace> > traces(_view.get_traces());
280         for (shared_ptr<Trace> t : traces) {
281                 assert(t);
282                 connect(t.get(), SIGNAL(visibility_changed()),
283                         this, SLOT(on_trace_changed()));
284                 connect(t.get(), SIGNAL(text_changed()),
285                         this, SLOT(on_trace_changed()));
286                 connect(t.get(), SIGNAL(colour_changed()),
287                         this, SLOT(update()));
288         }
289 }
290
291 void Header::on_signals_moved()
292 {
293         update();
294 }
295
296 void Header::on_trace_changed()
297 {
298         update();
299         geometry_updated();
300 }
301
302 } // namespace view
303 } // namespace pv