]> sigrok.org Git - pulseview.git/blob - pv/view/cursorheader.cpp
efc9205e06f05a3885b8bc11802420b6725abd54
[pulseview.git] / pv / view / cursorheader.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 "cursorheader.hpp"
22
23 #include "ruler.hpp"
24 #include "view.hpp"
25
26 #include <QApplication>
27 #include <QFontMetrics>
28 #include <QMouseEvent>
29
30 #include <pv/widgets/popup.hpp>
31
32 using std::shared_ptr;
33 using std::vector;
34
35 namespace pv {
36 namespace view {
37
38 const int CursorHeader::Padding = 20;
39 const int CursorHeader::BaselineOffset = 5;
40
41 int CursorHeader::calculateTextHeight()
42 {
43         QFontMetrics fm(font());
44         return fm.boundingRect(0, 0, INT_MAX, INT_MAX,
45                 Qt::AlignLeft | Qt::AlignTop, "8").height();
46 }
47
48 CursorHeader::CursorHeader(View &parent) :
49         MarginWidget(parent),
50         textHeight_(calculateTextHeight())
51 {
52 }
53
54 QSize CursorHeader::sizeHint() const
55 {
56         return QSize(0, textHeight_ + Padding + BaselineOffset);
57 }
58
59 void CursorHeader::clear_selection()
60 {
61         const vector< shared_ptr<TimeItem> > items(view_.time_items());
62         for (auto &i : items)
63                 i->select(false);
64         update();
65 }
66
67 void CursorHeader::paintEvent(QPaintEvent*)
68 {
69         QPainter p(this);
70         p.setRenderHint(QPainter::Antialiasing);
71
72         // The cursor labels are not drawn with the arrows exactly on the
73         // bottom line of the widget, because then the selection shadow
74         // would be clipped away.
75         const QRect r = rect().adjusted(0, 0, 0, -BaselineOffset);
76
77         // Draw the items
78         const vector< shared_ptr<TimeItem> > items(view_.time_items());
79         for (auto &m : items)
80                 m->paint_label(p, r);
81 }
82
83 void CursorHeader::mouseMoveEvent(QMouseEvent *e)
84 {
85         mouse_point_ = e->pos();
86
87         if (!(e->buttons() & Qt::LeftButton))
88                 return;
89
90         if ((e->pos() - mouse_down_point_).manhattanLength() <
91                 QApplication::startDragDistance())
92                 return;
93
94         // Do the drag
95         dragging_ = true;
96
97         const int delta = e->pos().x() - mouse_down_point_.x();
98         const vector< shared_ptr<TimeItem> > items(view_.time_items());
99         for (auto &i : items)
100                 if (i->dragging())
101                         i->set_time(view_.offset() +
102                                 (i->drag_point().x() + delta - 0.5) *
103                                 view_.scale());
104 }
105
106 void CursorHeader::mousePressEvent(QMouseEvent *e)
107 {
108         if (e->buttons() & Qt::LeftButton) {
109                 mouse_down_point_ = e->pos();
110
111                 mouse_down_item_.reset();
112
113                 clear_selection();
114
115                 const vector< shared_ptr<TimeItem> > items(view_.time_items());
116                 for (auto i = items.rbegin(); i != items.rend(); i++)
117                         if ((*i)->label_rect(rect()).contains(e->pos())) {
118                                 mouse_down_item_ = (*i);
119                                 break;
120                         }
121
122                 if (mouse_down_item_) {
123                         mouse_down_item_->select();
124                         mouse_down_item_->drag();
125                 }
126
127                 selection_changed();
128         }
129 }
130
131 void CursorHeader::mouseReleaseEvent(QMouseEvent *)
132 {
133         using pv::widgets::Popup;
134
135         if (!dragging_ && mouse_down_item_) {
136                 Popup *const p = mouse_down_item_->create_popup(&view_);
137                 if (p) {
138                         const QPoint arrpos(mouse_down_item_->get_x(),
139                                 height() - BaselineOffset);
140                         p->set_position(mapToGlobal(arrpos), Popup::Bottom);
141                         p->show();
142                 }
143         }
144
145         dragging_ = false;
146         mouse_down_item_.reset();
147
148         const vector< shared_ptr<TimeItem> > items(view_.time_items());
149         for (auto &i : items)
150                 i->drag_release();
151 }
152
153 void CursorHeader::leaveEvent(QEvent*)
154 {
155         mouse_point_ = QPoint(-1, -1);
156         update();
157 }
158
159 void CursorHeader::mouseDoubleClickEvent(QMouseEvent *e)
160 {
161         view_.add_flag(view_.offset() + ((double)e->x() + 0.5) * view_.scale());
162 }
163
164 void CursorHeader::keyPressEvent(QKeyEvent *e)
165 {
166         assert(e);
167
168         if (e->key() == Qt::Key_Delete)
169         {
170                 const vector< shared_ptr<TimeItem> > items(view_.time_items());
171                 for (auto &i : items)
172                         if (i->selected())
173                                 i->delete_pressed();
174         }
175 }
176
177 } // namespace view
178 } // namespace pv