]> sigrok.org Git - pulseview.git/blob - pv/view/header.cpp
Added a label context bar action
[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 <QColorDialog>
33 #include <QInputDialog>
34 #include <QMenu>
35 #include <QMouseEvent>
36 #include <QPainter>
37 #include <QRect>
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         _action_set_name(new QAction(tr("Set &Name..."), this)),
48         _action_set_colour(new QAction(tr("Set &Colour..."), this))
49 {
50         setMouseTracking(true);
51
52         connect(_action_set_name, SIGNAL(triggered()),
53                 this, SLOT(on_action_set_name_triggered()));
54         connect(_action_set_colour, SIGNAL(triggered()),
55                 this, SLOT(on_action_set_colour_triggered()));
56
57         connect(&_view.session(), SIGNAL(signals_changed()),
58                 this, SLOT(on_signals_changed()));
59
60         connect(&_view, SIGNAL(signals_moved()),
61                 this, SLOT(on_signals_moved()));
62 }
63
64 boost::shared_ptr<pv::view::Signal> Header::get_mouse_over_signal(
65         const QPoint &pt)
66 {
67         const int w = width();
68         const vector< shared_ptr<Signal> > sigs(
69                 _view.session().get_signals());
70
71         const int v_offset = _view.v_offset();
72         BOOST_FOREACH(const shared_ptr<Signal> s, sigs)
73         {
74                 assert(s);
75
76                 if (s->pt_in_label_rect(s->get_v_offset() - v_offset,
77                         0, w, pt))
78                         return s;
79         }
80
81         return shared_ptr<Signal>();
82 }
83
84 void Header::clear_selection()
85 {
86         const vector< shared_ptr<Signal> > sigs(
87                 _view.session().get_signals());
88         BOOST_FOREACH(const shared_ptr<Signal> s, sigs) {
89                 assert(s);
90                 s->select(false);
91         }
92
93         update();
94 }
95
96 void Header::paintEvent(QPaintEvent*)
97 {
98         const int w = width();
99         const vector< shared_ptr<Signal> > sigs(
100                 _view.session().get_signals());
101
102         QPainter painter(this);
103         painter.setRenderHint(QPainter::Antialiasing);
104
105         const int v_offset = _view.v_offset();
106         const bool dragging = !_drag_sigs.empty();
107         BOOST_FOREACH(const shared_ptr<Signal> s, sigs)
108         {
109                 assert(s);
110
111                 const int y = s->get_v_offset() - v_offset;
112                 const bool highlight = !dragging && s->pt_in_label_rect(
113                         y, 0, w, _mouse_point);
114                 s->paint_label(painter, y, w, highlight);
115         }
116
117         painter.end();
118 }
119
120 void Header::mousePressEvent(QMouseEvent *event)
121 {
122         assert(event);
123
124         const vector< shared_ptr<Signal> > sigs(
125                 _view.session().get_signals());
126
127         if (event->button() & Qt::LeftButton) {
128                 _mouse_down_point = event->pos();
129
130                 // Save the offsets of any signals which will be dragged
131                 BOOST_FOREACH(const shared_ptr<Signal> s, sigs)
132                         if (s->selected())
133                                 _drag_sigs.push_back(
134                                         make_pair(s, s->get_v_offset()));
135         }
136
137         // Select the signal if it has been clicked
138         const shared_ptr<Signal> mouse_over_signal =
139                 get_mouse_over_signal(event->pos());
140         if (mouse_over_signal) {
141                 if (mouse_over_signal->selected())
142                         mouse_over_signal->select(false);
143                 else {
144                         mouse_over_signal->select(true);
145
146                         if (~QApplication::keyboardModifiers() &
147                                 Qt::ControlModifier)
148                                 _drag_sigs.clear();
149
150                         // Add the signal to the drag list
151                         if (event->button() & Qt::LeftButton)
152                                 _drag_sigs.push_back(
153                                         make_pair(mouse_over_signal,
154                                         mouse_over_signal->get_v_offset()));
155                 }
156         }
157
158         if (~QApplication::keyboardModifiers() & Qt::ControlModifier) {
159                 // Unselect all other signals because the Ctrl is not
160                 // pressed
161                 BOOST_FOREACH(const shared_ptr<Signal> s, sigs)
162                         if (s != mouse_over_signal)
163                                 s->select(false);
164         }
165
166         selection_changed();
167         update();
168 }
169
170 void Header::mouseReleaseEvent(QMouseEvent *event)
171 {
172         assert(event);
173         if (event->button() == Qt::LeftButton) {
174                 _drag_sigs.clear();
175                 _view.normalize_layout();
176         }
177 }
178
179 void Header::mouseMoveEvent(QMouseEvent *event)
180 {
181         assert(event);
182         _mouse_point = event->pos();
183
184         // Move the signals if we are dragging
185         if (!_drag_sigs.empty()) {
186                 const int delta = event->pos().y() - _mouse_down_point.y();
187
188                 for (std::list<std::pair<boost::weak_ptr<Signal>,
189                         int> >::iterator i = _drag_sigs.begin();
190                         i != _drag_sigs.end(); i++) {
191                         const boost::shared_ptr<Signal> sig((*i).first);
192                         if (sig) {
193                                 const int y = (*i).second + delta;
194                                 const int y_snap =
195                                         ((y + View::SignalSnapGridSize / 2) /
196                                                 View::SignalSnapGridSize) *
197                                                 View::SignalSnapGridSize;
198                                 sig->set_v_offset(y_snap);
199
200                                 // Ensure the signal is selected
201                                 sig->select();
202                         }
203                         
204                 }
205
206                 signals_moved();
207         }
208
209         update();
210 }
211
212 void Header::leaveEvent(QEvent*)
213 {
214         _mouse_point = QPoint(-1, -1);
215         update();
216 }
217
218 void Header::contextMenuEvent(QContextMenuEvent *event)
219 {
220         const shared_ptr<Signal> s = get_mouse_over_signal(_mouse_point);
221
222         if (!s)
223                 return;
224
225         QMenu menu(this);
226         menu.addAction(_action_set_name);
227         menu.addAction(_action_set_colour);
228
229         _context_signal = s;
230         menu.exec(event->globalPos());
231         _context_signal.reset();
232 }
233
234 void Header::on_action_set_name_triggered()
235 {
236         bool ok = false;
237
238         shared_ptr<view::Signal> context_signal = _context_signal;
239         if (!context_signal)
240                 return;
241
242         const QString new_label = QInputDialog::getText(this, tr("Set Name"),
243                 tr("Name"), QLineEdit::Normal, context_signal->get_name(), &ok);
244
245         if (ok)
246                 context_signal->set_name(new_label);
247 }
248
249 void Header::on_action_set_colour_triggered()
250 {
251         shared_ptr<view::Signal> context_signal = _context_signal;
252         if (!context_signal)
253                 return;
254
255         const QColor new_colour = QColorDialog::getColor(
256                 context_signal->get_colour(), this, tr("Set Colour"));
257
258         if (new_colour.isValid())
259                 context_signal->set_colour(new_colour);
260 }
261
262 void Header::on_signals_changed()
263 {
264         const vector< shared_ptr<Signal> > sigs(_view.session().get_signals());
265         BOOST_FOREACH(shared_ptr<Signal> s, sigs) {
266                 assert(s);
267                 connect(s.get(), SIGNAL(text_changed()), this, SLOT(update()));
268         }
269 }
270
271 void Header::on_signals_moved()
272 {
273         update();
274 }
275
276
277 } // namespace view
278 } // namespace pv