]> sigrok.org Git - pulseview.git/blob - pv/view/viewport.cpp
ee30965d603e12ee29994c49855ad71188977600
[pulseview.git] / pv / view / viewport.cpp
1 /*
2  * This file is part of the sigrok 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 "view.h"
22 #include "viewport.h"
23
24 #include "../../sigsession.h"
25 #include "../../signal.h"
26
27 #include "../../extdef.h"
28
29 #include <QMouseEvent>
30 #include <QTextStream>
31
32 #include <math.h>
33
34 #include <boost/foreach.hpp>
35
36 using namespace boost;
37 using namespace std;
38
39 namespace pv {
40 namespace view {
41
42 const int Viewport::MinorTickSubdivision = 4;
43 const int Viewport::ScaleUnits[3] = {1, 2, 5};
44
45 const QString Viewport::SIPrefixes[9] =
46         {"f", "p", "n", QChar(0x03BC), "m", "", "k", "M", "G"};
47 const int Viewport::FirstSIPrefixPower = -15;
48
49 Viewport::Viewport(View &parent) :
50         QGLWidget(&parent),
51         _view(parent)
52 {
53         setMouseTracking(true);
54         setAutoFillBackground(false);
55 }
56
57 int Viewport::get_total_height() const
58 {
59         int height = 0;
60         BOOST_FOREACH(const shared_ptr<Signal> s,
61                 _view.session().get_signals()) {
62                 assert(s);
63                 height += View::SignalHeight;
64         }
65
66         return height;
67 }
68
69 void Viewport::initializeGL()
70 {
71 }
72
73 void Viewport::resizeGL(int width, int height)
74 {
75         setup_viewport(width, height);
76 }
77
78 void Viewport::paintEvent(QPaintEvent *event)
79 {
80         int offset;
81
82         const vector< shared_ptr<Signal> > &sigs =
83                 _view.session().get_signals();
84
85         // Prepare for OpenGL rendering
86         makeCurrent();
87         glMatrixMode(GL_MODELVIEW);
88         glPushMatrix();
89
90         setup_viewport(width(), height());
91
92         qglClearColor(Qt::white);
93         glClear(GL_COLOR_BUFFER_BIT);
94
95         // Plot the signal
96         glEnable(GL_SCISSOR_TEST);
97         glScissor(0, 0, width(), height());
98         offset = View::RulerHeight - _view.v_offset();
99         BOOST_FOREACH(const shared_ptr<Signal> s, sigs)
100         {
101                 assert(s);
102
103                 const QRect signal_rect(0, offset,
104                         width(), View::SignalHeight);
105
106                 s->paint(*this, signal_rect, _view.scale(), _view.offset());
107
108                 offset += View::SignalHeight;
109         }
110
111         glDisable(GL_SCISSOR_TEST);
112
113         // Prepare for QPainter rendering
114         glMatrixMode(GL_MODELVIEW);
115         glPopMatrix();
116
117         QPainter painter(this);
118         painter.setRenderHint(QPainter::Antialiasing);
119
120         // Paint the ruler
121         paint_ruler(painter);
122
123         painter.end();
124 }
125
126 void Viewport::mousePressEvent(QMouseEvent *event)
127 {
128         assert(event);
129
130         _mouse_down_point = event->pos();
131         _mouse_down_offset = _view.offset();
132 }
133
134 void Viewport::mouseMoveEvent(QMouseEvent *event)
135 {
136         assert(event);
137
138         if(event->buttons() & Qt::LeftButton)
139         {
140                 _view.set_scale_offset(_view.scale(),
141                         _mouse_down_offset +
142                         (_mouse_down_point - event->pos()).x() *
143                         _view.scale());
144         }
145 }
146
147 void Viewport::mouseReleaseEvent(QMouseEvent *event)
148 {
149         assert(event);
150 }
151
152 void Viewport::wheelEvent(QWheelEvent *event)
153 {
154         assert(event);
155         _view.zoom(event->delta() / 120, event->x());
156 }
157
158 void Viewport::setup_viewport(int width, int height)
159 {
160         glViewport(0, 0, (GLint)width, (GLint)height);
161         glMatrixMode(GL_PROJECTION);
162         glLoadIdentity();
163         glOrtho(0, width, height, 0, -1, 1);
164         glMatrixMode(GL_MODELVIEW);
165 }
166
167 void Viewport::paint_ruler(QPainter &p)
168 {
169         const double MinSpacing = 80;
170
171         const double min_period = _view.scale() * MinSpacing;
172
173         const int order = (int)floorf(log10f(min_period));
174         const double order_decimal = pow(10, order);
175
176         int unit = 0;
177         double tick_period = 0.0f;
178
179         do
180         {
181                 tick_period = order_decimal * ScaleUnits[unit++];
182         } while(tick_period < min_period && unit < countof(ScaleUnits));
183
184         const int prefix = (order - FirstSIPrefixPower) / 3;
185         assert(prefix >= 0);
186         assert(prefix < countof(SIPrefixes));
187
188         const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX,
189                 Qt::AlignLeft | Qt::AlignTop, "8").height();
190
191         // Draw the tick marks
192         p.setPen(Qt::black);
193
194         const double minor_tick_period = tick_period / MinorTickSubdivision;
195         const double first_major_division =
196                 floor(_view.offset() / tick_period);
197         const double first_minor_division =
198                 ceil(_view.offset() / minor_tick_period);
199         const double t0 = first_major_division * tick_period;
200
201         int division = (int)round(first_minor_division -
202                 first_major_division * MinorTickSubdivision);
203         while(1)
204         {
205                 const double t = t0 + division * minor_tick_period;
206                 const double x = (t - _view.offset()) / _view.scale();
207
208                 if(x >= width())
209                         break;
210
211                 if(division % MinorTickSubdivision == 0)
212                 {
213                         // Draw a major tick
214                         QString s;
215                         QTextStream ts(&s);
216                         ts << (t / order_decimal) << SIPrefixes[prefix] << "s";
217                         p.drawText(x, 0, 0, text_height, Qt::AlignCenter | Qt::AlignTop |
218                                 Qt::TextDontClip, s);
219                         p.drawLine(x, text_height, x, View::RulerHeight);
220                 }
221                 else
222                 {
223                         // Draw a minor tick
224                         p.drawLine(x,
225                                 (text_height + View::RulerHeight) / 2, x,
226                                 View::RulerHeight);
227                 }
228
229                 division++;
230         }
231 }
232
233 } // namespace view
234 } // namespace pv