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