Import upstream version 0.99.2
[fmit.git] / src / modules / DialView.cpp
1 #include "DialView.h"
2
3 #include <iostream>
4 using namespace std;
5 #include <qpainter.h>
6 #include <qmenu.h>
7 #include <qtooltip.h>
8 #include <qlabel.h>
9 #include <qwidgetaction.h>
10 #include <qboxlayout.h>
11 #include <Music/Music.h>
12
13 DialView::DialView(QWidget* parent)
14 : QFrame(parent)
15 , View(tr("Dial"), this)
16 {
17         m_error = 0.0f;
18         m_min_error = 0.0f;
19         m_max_error = 0.0f;
20
21         // settings
22         setting_showTolerance = new QAction(tr("Show tolerance"), this);
23         setting_showTolerance->setCheckable(true);
24         setting_showTolerance->setChecked(true);
25         connect(setting_showTolerance, SIGNAL(toggled(bool)), this, SLOT(update()));
26         m_popup_menu.addAction(setting_showTolerance);
27
28         setting_showTrace = new QAction(tr("Show trace"), this);
29         setting_showTrace->setCheckable(true);
30         setting_showTrace->setChecked(true);
31         connect(setting_showTrace, SIGNAL(toggled(bool)), this, SLOT(update()));
32         m_popup_menu.addAction(setting_showTrace);
33
34         setting_useCents = new QAction(tr("Use cents"), this);
35         setting_useCents->setCheckable(true);
36         setting_useCents->setChecked(true);
37         connect(setting_useCents, SIGNAL(toggled(bool)), this, SLOT(update()));
38         m_popup_menu.addAction(setting_useCents);
39
40         QHBoxLayout* scaleActionLayout = new QHBoxLayout(&m_popup_menu);
41
42         QLabel* scaleActionTitle = new QLabel(tr("Scale range"), &m_popup_menu);
43         scaleActionLayout->addWidget(scaleActionTitle);
44
45         setting_spinScale = new QSpinBox(&m_popup_menu);
46         setting_spinScale->setMinimum(5);
47         setting_spinScale->setMaximum(100);
48         setting_spinScale->setSingleStep(1);
49         setting_spinScale->setValue(50);
50         setting_spinScale->setToolTip(tr("Scale range (in cents)"));
51         connect(setting_spinScale, SIGNAL(valueChanged(int)), this, SLOT(update()));
52         scaleActionLayout->addWidget(setting_spinScale);
53
54         QWidget* scaleActionWidget = new QWidget(&m_popup_menu);
55         scaleActionWidget->setLayout(scaleActionLayout);
56
57         QWidgetAction* scaleAction = new QWidgetAction(&m_popup_menu);
58         scaleAction->setDefaultWidget(scaleActionWidget);
59         m_popup_menu.addAction(scaleAction);
60 }
61
62 void DialView::save()
63 {
64         s_settings->setValue("showTolerance", setting_showTolerance->isChecked());
65         s_settings->setValue("showTrace", setting_showTrace->isChecked());
66         s_settings->setValue("useCents", setting_useCents->isChecked());
67         s_settings->setValue("spinScale", setting_spinScale->value());
68 }
69 void DialView::load()
70 {
71         setting_showTolerance->setChecked(s_settings->value("showTolerance", setting_showTolerance->isChecked()).toBool());
72         setting_showTrace->setChecked(s_settings->value("showTrace", setting_showTrace->isChecked()).toBool());
73         setting_useCents->setChecked(s_settings->value("useCents", setting_useCents->isChecked()).toBool());
74         setting_spinScale->setValue(s_settings->value("spinScale", setting_spinScale->value()).toInt());
75 }
76 void DialView::clearSettings()
77 {
78         s_settings->remove("showTolerance");
79         s_settings->remove("showTrace");
80         s_settings->remove("useCents");
81         s_settings->remove("spinScale");
82 }
83
84 void DialView::drawTextTickCent(QPainter& p, int bigw, int bigh, int r)
85 {
86         QString txt;
87 //      double deg = 0.0;
88
89         int bw = bigw/2-p.fontMetrics().height()+p.fontMetrics().height()/2+2;
90         int bh = bigh-p.fontMetrics().height()+p.fontMetrics().height()/2+2;
91
92         // only work within range that is a pure multiple of r
93         int range = int(setting_spinScale->value()/r)*r;
94
95         float scale = 50.0f/setting_spinScale->value()/100.0f;
96         for(int i=-range; i<=range; i+=r)
97         {
98                 txt = QString::number(i);
99                 float angle = (150.0f/180.0f)*i*scale*Math::Pi + Math::Pi/2;
100                 QPoint pos(int(-bw*cos(angle)),int(-bh*sin(angle)));
101
102                 p.translate(pos.x(), pos.y());
103
104                 if(height()>width())    p.rotate(90);
105                 p.drawText(-p.fontMetrics().width(txt)/2,p.fontMetrics().height()/2, txt);
106                 if(height()>width())    p.rotate(-90);
107
108                 p.translate(-pos.x(), -pos.y());
109         }
110 }
111 void DialView::drawTicksCent(QPainter& p, int bigw, int bigh, int r, int ticks_size, int h)
112 {
113         int sw = int(bigw/2.0f-p.fontMetrics().height()-ticks_size);
114         int sh = bigh-p.fontMetrics().height()-ticks_size;
115         int bw = sw + h;
116         int bh = sh + h;
117
118         // only work within range that is a pure multiple of r
119         float range = int(setting_spinScale->value()/r)*r;
120
121         float scale = 50.0f/setting_spinScale->value()/100.0f;
122         for(float i=-range; i<=range; i+=r)
123         {
124                 float angle = (150.0f/180.0f)*i*scale*Math::Pi + Math::Pi/2;
125                 p.drawLine(-QPoint(int(sw*cos(angle)),int(sh*sin(angle))), -QPoint(int(bw*cos(angle)),int(bh*sin(angle))));
126         }
127 }
128 void DialView::drawTextTickFrac(QPainter& p, int bigw, int bigh, int num, int den)
129 {
130         QString txt;
131         double deg = 0.0;
132         if(num==0 || den==0)
133                 txt = "0";
134         else
135         {
136                 deg = num*150/den;
137                 txt = QString::number(den*(50/setting_spinScale->value()));
138         }
139
140         int bw = bigw/2-p.fontMetrics().height()+p.fontMetrics().height()/2+2;
141         int bh = bigh-p.fontMetrics().height()+p.fontMetrics().height()/2+2;
142         float angle = deg/180.0f*Math::Pi + Math::Pi/2;
143         QPoint pos(int(-bw*cos(angle)),int(-bh*sin(angle)));
144
145         p.translate(pos.x(), pos.y());
146
147         if(height()>width())    p.rotate(90);
148         p.drawText(-p.fontMetrics().width(txt)/2,p.fontMetrics().height()/2, txt);
149         if(height()>width())    p.rotate(-90);
150
151         p.translate(-pos.x(), -pos.y());
152 }
153 void DialView::drawTicksFrac(QPainter& p, int bigw, int bigh, float r, int ticks_size, int h)
154 {
155         int sw = bigw/2-p.fontMetrics().height()-ticks_size;
156         int sh = bigh-p.fontMetrics().height()-ticks_size;
157         int bw = sw + h;
158         int bh = sh + h;
159         for(float i=-0.5f+r; i<0.5f; i+=2*r)
160         {
161                 float angle = (150.0f/180.0f)*i*Math::Pi + Math::Pi/2;
162                 p.drawLine(-QPoint(int(sw*cos(angle)),int(sh*sin(angle))), -QPoint(int(bw*cos(angle)),int(bh*sin(angle))));
163         }
164 }
165
166 void DialView::setError(float error)
167 {
168         m_error = error;
169         repaint();
170 }
171
172 void DialView::paintEvent(QPaintEvent* event)
173 {
174         QPainter p(this);
175
176         float human_tol = Music::f2hf(441.0,440.0);                     // TODO
177         float scale;
178         if(setting_useCents->isChecked())
179                 scale = 50.0f/setting_spinScale->value();
180         else
181                 scale = int(50/setting_spinScale->value());
182
183         int bigw = width();
184         int bigh = height();
185
186         QPoint c(width()/2,height());
187         QPoint unity_center = c+QPoint(p.fontMetrics().width("cent"),0);
188         QString unity;
189         if(setting_useCents->isChecked())       unity = "cents";
190         else                                                    unity = "1/x";
191         if(height()>width())
192         {
193                 c = QPoint(width(), height()/2);
194                 bigw = height();
195                 bigh = width();
196                 unity_center = c+QPoint(-p.fontMetrics().width(unity),-p.fontMetrics().height());
197         }
198         int ticks_size = bigh/10;
199
200         // draw unity
201         p.setPen(QColor(0,0,0));
202         p.drawText(unity_center, unity);
203
204         p.translate(c.x(), c.y());
205
206         if(height()>width())
207                 p.rotate(-90);
208
209         // draw pie
210         int pie_width = bigw/2-p.fontMetrics().height()-ticks_size-2;
211         int pie_height = bigh-p.fontMetrics().height()-ticks_size-2;
212         p.setBrush(QColor(123,205,145));
213         QPen pen1 = p.pen();
214         pen1.setColor(QColor(83,165,105));
215 //      pen1.setStyle(Qt::DashLine);
216 //      pen1.setWidth(2);
217         p.setPen(pen1);
218         if(setting_showTolerance->isChecked())
219         {
220             float pie_deg = (150.0f/180.0f)*scale*human_tol*180;
221             p.drawPie(-pie_width,-pie_height,2*pie_width,2*pie_height, int((90-pie_deg)*16),int(2*pie_deg*16));
222         }
223
224         // draw min max
225         if((m_error>-1.0f && m_error<1.0f) && setting_showTrace->isChecked() && m_max_error-m_min_error>0.0)
226         {
227                 int line_width = ticks_size/4;
228                 int mm_height = pie_height - line_width/2;
229                 int mm_width = pie_width - line_width/2;
230                 QPen pen3 = p.pen();
231                 pen3.setColor(QColor(128,128,128));
232                 pen3.setWidth(line_width);
233                 p.setPen(pen3);
234
235                 float mm_degf = (150.0f/180.0f)*scale*180;
236                 p.drawArc(-mm_width,-mm_height,2*mm_width,2*mm_height,int((90-mm_degf*m_min_error)*16),int((-mm_degf*(m_max_error-m_min_error))*16));
237         }
238
239         // draw text marks
240         p.setPen(QColor(0,0,0));
241         if(setting_useCents->isChecked())
242         {
243                 int grad = 10;
244                 if(setting_spinScale->value() <= 25) grad=5;
245                 if(setting_spinScale->value() <= 10) grad=1;
246                 drawTextTickCent(p, bigw, bigh, grad);
247         }
248         else
249         {
250                 drawTextTickFrac(p, bigw, bigh, 0,1);
251                 drawTextTickFrac(p, bigw, bigh, 1,2);
252                 drawTextTickFrac(p, bigw, bigh, -1,2);
253                 drawTextTickFrac(p, bigw, bigh, 1,4);
254                 drawTextTickFrac(p, bigw, bigh, -1,4);
255                 drawTextTickFrac(p, bigw, bigh, 1,8);
256                 drawTextTickFrac(p, bigw, bigh, -1,8);
257                 drawTextTickFrac(p, bigw, bigh, 1,16);
258                 drawTextTickFrac(p, bigw, bigh, -1,16);
259         }
260
261         // draw ticks
262         int sw = bigw/2-p.fontMetrics().height()-ticks_size;
263         int sh = bigh-p.fontMetrics().height()-ticks_size;
264         int bw = sw + ticks_size;
265         int bh = sh + ticks_size;
266         if(setting_useCents->isChecked())
267         {
268                 drawTicksCent(p, bigw, bigh, 10, ticks_size, ticks_size);
269                 drawTicksCent(p, bigw, bigh, 5, ticks_size, int(ticks_size * 0.6));
270                 drawTicksCent(p, bigw, bigh, 1, ticks_size, int(ticks_size * 0.3));
271         }
272         else
273         {
274                 for(float i=-1.0f; i<=1.0f; i+=0.25f)
275                 {
276                         float angle = (150.0f/360.0f)*i*Math::Pi + Math::Pi/2;
277                         p.drawLine(-QPoint(int(sw*cos(angle)),int(sh*sin(angle))), -QPoint(int(bw*cos(angle)),int(bh*sin(angle))));
278                 }
279                 drawTicksFrac(p, bigw, bigh, 0.125f, ticks_size, 2*ticks_size/3);
280                 drawTicksFrac(p, bigw, bigh, 0.0625f, ticks_size, ticks_size/2);
281                 drawTicksFrac(p, bigw, bigh, 0.03125f, ticks_size, ticks_size/3);
282         }
283
284         // draw arrow
285         if(m_error>-1.0f && m_error<1.0f)
286         {
287                 float angle = (150.0f/180.0f)*scale*m_error*Math::Pi + Math::Pi/2;
288                 QPoint final_point = -QPoint(int((pie_width-2)*cos(angle)),int((pie_height-2)*sin(angle)));
289                 QPoint mid_point = 9*final_point/10;
290                 QPen pen2 = p.pen();
291                 pen2.setWidth(3);
292                 pen2.setColor(QColor(64,64,64));
293                 p.setPen(pen2);
294                 p.drawLine(QPoint(0,0), mid_point);
295                 pen2.setWidth(1);
296                 p.setPen(pen2);
297                 p.drawLine(mid_point, final_point);
298         }
299 }