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