]> sigrok.org Git - pulseview.git/blob - pv/view/header.cpp
f8f5b6380514ac2604730ce0a687c0c469fff04a
[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 <boost/foreach.hpp>
30
31 #include <QApplication>
32 #include <QMenu>
33 #include <QMouseEvent>
34 #include <QPainter>
35 #include <QRect>
36
37 #include <pv/widgets/popup.h>
38
39 using namespace boost;
40 using namespace std;
41
42 namespace pv {
43 namespace view {
44
45 Header::Header(View &parent) :
46         MarginWidget(parent),
47         _dragging(false)
48 {
49         setMouseTracking(true);
50
51         connect(&_view.session(), SIGNAL(signals_changed()),
52                 this, SLOT(on_signals_changed()));
53
54         connect(&_view, SIGNAL(signals_moved()),
55                 this, SLOT(on_signals_moved()));
56 }
57
58 shared_ptr<Trace> Header::get_mouse_over_trace(const QPoint &pt)
59 {
60         const int w = width();
61         const vector< shared_ptr<Trace> > traces(_view.get_traces());
62
63         BOOST_FOREACH(const shared_ptr<Trace> t, traces)
64         {
65                 assert(t);
66                 if (t->pt_in_label_rect(0, w, pt))
67                         return t;
68         }
69
70         return shared_ptr<Trace>();
71 }
72
73 void Header::clear_selection()
74 {
75         const vector< shared_ptr<Trace> > traces(_view.get_traces());
76         BOOST_FOREACH(const shared_ptr<Trace> t, traces) {
77                 assert(t);
78                 t->select(false);
79         }
80
81         update();
82 }
83
84 void Header::paintEvent(QPaintEvent*)
85 {
86         const int w = width();
87         const vector< shared_ptr<Trace> > traces(_view.get_traces());
88
89         QPainter painter(this);
90         painter.setRenderHint(QPainter::Antialiasing);
91
92         const bool dragging = !_drag_traces.empty();
93         BOOST_FOREACH(const shared_ptr<Trace> t, traces)
94         {
95                 assert(t);
96
97                 const bool highlight = !dragging && t->pt_in_label_rect(
98                         0, w, _mouse_point);
99                 t->paint_label(painter, w, highlight);
100         }
101
102         painter.end();
103 }
104
105 void Header::mousePressEvent(QMouseEvent *event)
106 {
107         assert(event);
108
109         const vector< shared_ptr<Trace> > traces(_view.get_traces());
110
111         if (event->button() & Qt::LeftButton) {
112                 _mouse_down_point = event->pos();
113
114                 // Save the offsets of any signals which will be dragged
115                 BOOST_FOREACH(const shared_ptr<Trace> t, traces)
116                         if (t->selected())
117                                 _drag_traces.push_back(
118                                         make_pair(t, t->get_v_offset()));
119         }
120
121         // Select the signal if it has been clicked
122         const shared_ptr<Trace> mouse_over_trace =
123                 get_mouse_over_trace(event->pos());
124         if (mouse_over_trace) {
125                 if (mouse_over_trace->selected())
126                         mouse_over_trace->select(false);
127                 else {
128                         mouse_over_trace->select(true);
129
130                         if (~QApplication::keyboardModifiers() &
131                                 Qt::ControlModifier)
132                                 _drag_traces.clear();
133
134                         // Add the signal to the drag list
135                         if (event->button() & Qt::LeftButton)
136                                 _drag_traces.push_back(
137                                         make_pair(mouse_over_trace,
138                                         mouse_over_trace->get_v_offset()));
139                 }
140         }
141
142         if (~QApplication::keyboardModifiers() & Qt::ControlModifier) {
143                 // Unselect all other signals because the Ctrl is not
144                 // pressed
145                 BOOST_FOREACH(const shared_ptr<Trace> t, traces)
146                         if (t != mouse_over_trace)
147                                 t->select(false);
148         }
149
150         selection_changed();
151         update();
152 }
153
154 void Header::mouseReleaseEvent(QMouseEvent *event)
155 {
156         using pv::widgets::Popup;
157
158         assert(event);
159         if (event->button() == Qt::LeftButton) {
160                 if (_dragging)
161                         _view.normalize_layout();
162                 else
163                 {
164                         const shared_ptr<Trace> mouse_over_trace =
165                                 get_mouse_over_trace(event->pos());
166                         if (mouse_over_trace) {
167                                 Popup *const p =
168                                         mouse_over_trace->create_popup(&_view);
169                                 p->set_position(mapToGlobal(QPoint(width(),
170                                         mouse_over_trace->get_y())),
171                                         Popup::Right);
172                                 p->show();
173                         }
174                 }
175
176                 _dragging = false;
177                 _drag_traces.clear();
178         }
179 }
180
181 void Header::mouseMoveEvent(QMouseEvent *event)
182 {
183         assert(event);
184         _mouse_point = event->pos();
185
186         if (!(event->buttons() & Qt::LeftButton))
187                 return;
188
189         if ((event->pos() - _mouse_down_point).manhattanLength() <
190                 QApplication::startDragDistance())
191                 return;
192
193         // Move the signals if we are dragging
194         if (!_drag_traces.empty())
195         {
196                 _dragging = true;
197
198                 const int delta = event->pos().y() - _mouse_down_point.y();
199
200                 for (std::list<std::pair<boost::weak_ptr<Trace>,
201                         int> >::iterator i = _drag_traces.begin();
202                         i != _drag_traces.end(); i++) {
203                         const boost::shared_ptr<Trace> trace((*i).first);
204                         if (trace) {
205                                 const int y = (*i).second + delta;
206                                 const int y_snap =
207                                         ((y + View::SignalSnapGridSize / 2) /
208                                                 View::SignalSnapGridSize) *
209                                                 View::SignalSnapGridSize;
210                                 trace->set_v_offset(y_snap);
211
212                                 // Ensure the trace is selected
213                                 trace->select();
214                         }
215                         
216                 }
217
218                 signals_moved();
219         }
220
221         update();
222 }
223
224 void Header::leaveEvent(QEvent*)
225 {
226         _mouse_point = QPoint(-1, -1);
227         update();
228 }
229
230 void Header::contextMenuEvent(QContextMenuEvent *event)
231 {
232         const shared_ptr<Trace> t = get_mouse_over_trace(_mouse_point);
233
234         if (t)
235                 t->create_context_menu(this)->exec(event->globalPos());
236 }
237
238 void Header::on_signals_changed()
239 {
240         const vector< shared_ptr<Trace> > traces(_view.get_traces());
241         BOOST_FOREACH(shared_ptr<Trace> t, traces) {
242                 assert(t);
243                 connect(t.get(), SIGNAL(text_changed()), this, SLOT(update()));
244         }
245 }
246
247 void Header::on_signals_moved()
248 {
249         update();
250 }
251
252
253 } // namespace view
254 } // namespace pv