Import fmit upstream version 0.97.6
[fmit.git] / src / modules / MicrotonalView.cpp
1 // Copyright 2004 "Gilles Degottex"
2
3 // This file is part of "fmit"
4
5 // "fmit" is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // "fmit" is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19 #include "MicrotonalView.h"
20
21 static const unsigned char g_icon_ji[] = {
22     0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
23     0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20,
24     0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7a, 0x7a, 0xf4, 0x00, 0x00, 0x05,
25     0xc3, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0xb5, 0x97, 0x4b, 0x6c, 0x24,
26     0xc5, 0x19, 0xc7, 0x7f, 0x55, 0xfd, 0x98, 0x5d, 0x7b, 0x1e, 0x1e, 0xdb,
27     0x0c, 0xb3, 0xbb, 0xf6, 0x78, 0xed, 0xf1, 0xda, 0xe0, 0x85, 0x95, 0x80,
28     0x43, 0x88, 0x90, 0x15, 0xb8, 0x70, 0x88, 0xc0, 0x42, 0xe2, 0x86, 0xc4,
29     0x85, 0xa0, 0x58, 0x4a, 0x4e, 0x44, 0x8a, 0x84, 0x72, 0x8a, 0xb4, 0x70,
30     0x08, 0x91, 0x12, 0x5e, 0x97, 0x91, 0x78, 0x48, 0x51, 0x24, 0x94, 0x70,
31     0xca, 0x04, 0x89, 0x03, 0x52, 0x72, 0x18, 0xe5, 0xc4, 0x29, 0x2c, 0x9b,
32     0x08, 0xd6, 0x6b, 0x0f, 0xcc, 0xcb, 0x5e, 0xcf, 0xf8, 0xbd, 0x9e, 0x99,
33     0x6e, 0x4f, 0x17, 0x87, 0xee, 0x9e, 0x99, 0x6e, 0x8f, 0xd7, 0xcb, 0xeb,
34     0x93, 0x4a, 0xdd, 0x35, 0xea, 0xaf, 0xfe, 0xbf, 0xfa, 0x7f, 0x55, 0xd5,
35     0xd3, 0x82, 0x1f, 0x28, 0x16, 0x97, 0x96, 0x47, 0x80, 0x14, 0x70, 0xaf,
36     0x77, 0xed, 0x6f, 0x6d, 0xe0, 0xdd, 0x42, 0x3e, 0x77, 0x3d, 0x9c, 0x27,
37     0xee, 0x72, 0xf0, 0x33, 0xc0, 0x14, 0x30, 0x3d, 0xa0, 0xa5, 0x81, 0x7b,
38     0x00, 0xf3, 0x94, 0x61, 0x1c, 0x20, 0x5b, 0xc8, 0xe7, 0x8a, 0xfd, 0x3f,
39     0xea, 0x21, 0x21, 0x09, 0xfc, 0x04, 0x78, 0x12, 0xb8, 0x14, 0x12, 0x39,
40     0x06, 0x2b, 0x84, 0x20, 0x16, 0x1d, 0x22, 0x1e, 0x1b, 0x26, 0x11, 0x1b,
41     0x26, 0x1e, 0x8b, 0x32, 0x12, 0x8f, 0x12, 0x8f, 0x0d, 0x13, 0x8f, 0x0f,
42     0x93, 0x88, 0x45, 0x11, 0xc0, 0x87, 0xff, 0xfc, 0x17, 0xc5, 0x52, 0x4d,
43     0x02, 0xbf, 0x04, 0x7e, 0x37, 0x10, 0x60, 0x71, 0x69, 0xf9, 0x37, 0xc0,
44     0xcb, 0xde, 0x6c, 0x10, 0x42, 0x30, 0x79, 0x3e, 0xc5, 0xec, 0xcc, 0x24,
45     0xd9, 0x8b, 0x17, 0x18, 0x1f, 0x1d, 0xe9, 0x8a, 0xc4, 0xa3, 0xc3, 0x98,
46     0x67, 0x86, 0x10, 0x7a, 0x84, 0x96, 0xad, 0x68, 0x5a, 0x0e, 0x6d, 0x5b,
47     0xd1, 0xb6, 0x1d, 0x9a, 0x96, 0x43, 0xcb, 0x76, 0x30, 0x34, 0xc9, 0x50,
48     0x44, 0xf2, 0xe8, 0x5c, 0x94, 0xb6, 0x65, 0xf3, 0xe6, 0x3b, 0x7f, 0x07,
49     0x78, 0x2c, 0x3c, 0x09, 0xdd, 0x13, 0xff, 0x2d, 0xf0, 0x1a, 0xc0, 0xc3,
50     0x0f, 0xce, 0xf3, 0xc2, 0x73, 0x4f, 0x73, 0x69, 0x26, 0x83, 0xe5, 0x68,
51     0x34, 0x2d, 0x87, 0x66, 0xbb, 0x43, 0xd3, 0x52, 0x1c, 0x5a, 0x0e, 0xdb,
52     0x6d, 0x87, 0x6a, 0xbd, 0x83, 0xe3, 0x74, 0xe8, 0xa8, 0x43, 0x94, 0x52,
53     0x38, 0x0a, 0x1c, 0xa5, 0x50, 0x0a, 0x1c, 0xc7, 0xef, 0x77, 0x70, 0x1c,
54     0xc5, 0x23, 0x33, 0xc3, 0x3c, 0x70, 0x5f, 0xd6, 0xd7, 0x5b, 0x18, 0x08,
55     0x00, 0x3c, 0x03, 0xf0, 0xb3, 0x9f, 0x3e, 0xc4, 0xd5, 0x97, 0x97, 0x29,
56     0x37, 0x2c, 0xfe, 0x5f, 0xb5, 0xb1, 0x8f, 0xac, 0x9e, 0x00, 0xa0, 0x3c,
57     0x11, 0x1c, 0x9b, 0x8d, 0x5a, 0x8d, 0x6a, 0xa5, 0x42, 0xb5, 0x5a, 0xa1,
58     0x56, 0xad, 0xf0, 0xf3, 0xa5, 0x67, 0x49, 0x9f, 0x9f, 0x70, 0x41, 0x1c,
59     0x17, 0xc8, 0x51, 0x82, 0xa6, 0xe5, 0x90, 0xb9, 0x70, 0xaf, 0xaf, 0x37,
60     0xbe, 0xb8, 0xb4, 0x9c, 0x2a, 0xe4, 0x73, 0xb7, 0xc2, 0x00, 0xa3, 0x00,
61     0x17, 0x33, 0xe7, 0x59, 0xdf, 0xb1, 0x69, 0xec, 0x1f, 0x21, 0x05, 0x98,
62     0x86, 0xc0, 0x51, 0x82, 0x7a, 0xbd, 0xc1, 0xe7, 0xd7, 0x3e, 0xa7, 0x54,
63     0x29, 0x53, 0x2e, 0x95, 0xd9, 0xdc, 0xdc, 0x44, 0x29, 0x05, 0x80, 0x69,
64     0x98, 0x5c, 0x9c, 0x99, 0x21, 0x62, 0x4a, 0x22, 0xba, 0x40, 0x21, 0xe8,
65     0x38, 0x0a, 0xa5, 0x04, 0x8e, 0x82, 0x96, 0xed, 0x90, 0x4a, 0x9c, 0x61,
66     0x6c, 0x34, 0x41, 0x63, 0x6b, 0x17, 0xe0, 0x32, 0x30, 0x18, 0x20, 0x7d,
67     0xcf, 0x28, 0x07, 0xcd, 0x0e, 0xa6, 0x2e, 0x50, 0x4a, 0xa0, 0x70, 0x67,
68     0xb2, 0xb1, 0x5e, 0xe3, 0x1f, 0xf9, 0x3c, 0xa6, 0x61, 0x60, 0xd9, 0x76,
69     0xc0, 0xc2, 0x5f, 0xbc, 0xf8, 0x22, 0x53, 0xd3, 0xd3, 0xae, 0xfd, 0x9e,
70     0x43, 0x4a, 0x09, 0xb7, 0x14, 0x80, 0x7d, 0xe4, 0x82, 0x4e, 0x5d, 0x48,
71     0xfb, 0x00, 0x0b, 0xc0, 0xbf, 0xfd, 0x7c, 0xe9, 0x5d, 0x93, 0x00, 0xe9,
72     0xd4, 0x18, 0xba, 0x26, 0x30, 0x34, 0x81, 0xa1, 0xbb, 0x57, 0x53, 0x13,
73     0x3c, 0x74, 0x65, 0x81, 0x3f, 0xfd, 0xf1, 0x55, 0xde, 0xf8, 0xf3, 0x6b,
74     0x3c, 0x78, 0x39, 0x58, 0xc6, 0x56, 0xf3, 0x36, 0x11, 0x5d, 0x62, 0xea,
75     0x82, 0x88, 0x2e, 0xbb, 0xf7, 0xa6, 0xe1, 0x3a, 0x62, 0x77, 0x5c, 0x80,
76     0xc9, 0x5e, 0x19, 0x2e, 0xf7, 0xe7, 0xfb, 0x00, 0x6d, 0x80, 0xe4, 0x48,
77     0x8c, 0x88, 0xd1, 0x13, 0x37, 0x34, 0x81, 0xae, 0x0b, 0xce, 0x9e, 0x31,
78     0x48, 0xc4, 0x86, 0x31, 0x34, 0xc1, 0xa5, 0xd9, 0x99, 0x00, 0x40, 0xb5,
79     0x5a, 0x71, 0x05, 0x3d, 0x61, 0xa3, 0xef, 0xde, 0xd4, 0x25, 0x1d, 0xc7,
80     0x05, 0xc8, 0x9c, 0x00, 0xe0, 0x97, 0x60, 0x0b, 0x88, 0xb6, 0x5a, 0x16,
81     0xf1, 0xa4, 0x44, 0x29, 0x07, 0x47, 0x81, 0x02, 0xcf, 0xd2, 0x5e, 0xcb,
82     0x4e, 0x4f, 0x06, 0x00, 0xca, 0xa5, 0x12, 0x86, 0x2e, 0x7a, 0xd6, 0xfb,
83     0xbb, 0x41, 0x09, 0x4c, 0x5d, 0x30, 0x16, 0x73, 0x25, 0x32, 0x13, 0x69,
84     0x3f, 0x25, 0x60, 0xa1, 0xef, 0xc0, 0x16, 0xc0, 0xed, 0x66, 0x13, 0x43,
85     0x17, 0x68, 0xd2, 0x73, 0x40, 0xd2, 0x75, 0xc2, 0x77, 0x23, 0x0c, 0xf0,
86     0x75, 0xb9, 0xd2, 0x7b, 0x46, 0x17, 0x18, 0xba, 0x24, 0x76, 0x56, 0x63,
87     0x3a, 0x15, 0xe1, 0xfe, 0x89, 0xb3, 0x8c, 0x0c, 0xeb, 0x61, 0x07, 0xc6,
88     0x17, 0x97, 0x96, 0x53, 0x61, 0x07, 0xb6, 0x01, 0x6e, 0xdf, 0x6e, 0xa2,
89     0x6b, 0x02, 0x5d, 0x73, 0x17, 0x20, 0xfe, 0xcc, 0x7d, 0x27, 0x50, 0x24,
90     0x13, 0x51, 0xc6, 0x46, 0x93, 0x34, 0xb6, 0xb6, 0x01, 0xd8, 0xdf, 0x3f,
91     0x60, 0x7f, 0x6f, 0x87, 0xe4, 0x48, 0x12, 0xd3, 0x10, 0x24, 0x86, 0x74,
92     0x86, 0x22, 0xfe, 0xbc, 0x7a, 0x91, 0x4e, 0x8d, 0x61, 0x9a, 0x06, 0x96,
93     0x65, 0xfb, 0x65, 0xb8, 0x75, 0xdc, 0x81, 0xc3, 0x16, 0x9a, 0x04, 0x5d,
94     0x03, 0x5d, 0x82, 0xa6, 0x09, 0xb7, 0x49, 0x17, 0x4a, 0x97, 0x12, 0x5d,
95     0x1e, 0x77, 0xa1, 0x56, 0xad, 0x92, 0x4e, 0x1a, 0x9c, 0x4b, 0x9a, 0x03,
96     0xc5, 0xc1, 0x3f, 0x59, 0xbb, 0x2e, 0x74, 0xcb, 0x10, 0x2a, 0x41, 0x0b,
97     0x4d, 0x8a, 0x40, 0xd3, 0xa5, 0x40, 0xd7, 0x40, 0x93, 0xa0, 0x69, 0x2e,
98     0xd4, 0x6c, 0x08, 0x60, 0xbd, 0x56, 0x25, 0x62, 0x0c, 0x16, 0xee, 0x8f,
99     0xe9, 0xcc, 0x39, 0xff, 0xb6, 0xbb, 0x10, 0x03, 0x25, 0x38, 0x3c, 0x6c,
100     0x22, 0x05, 0x68, 0x52, 0x74, 0xad, 0x47, 0x29, 0x14, 0xfd, 0x7d, 0x98,
101     0x9b, 0xc9, 0x04, 0x06, 0xfe, 0xf2, 0xe6, 0xd7, 0xa7, 0x8a, 0x03, 0x5c,
102     0x9c, 0x3c, 0x19, 0xa0, 0x0e, 0xb0, 0xb3, 0x77, 0x80, 0x10, 0x02, 0x29,
103     0x14, 0x4a, 0xb8, 0x62, 0x4a, 0xb8, 0x2f, 0x41, 0x1f, 0x40, 0x00, 0x57,
104     0xee, 0x9f, 0x0e, 0x02, 0xac, 0x7e, 0x6b, 0x80, 0x63, 0x25, 0x28, 0x02,
105     0xd4, 0x36, 0xea, 0x08, 0x40, 0x0a, 0x81, 0x26, 0x04, 0x52, 0x0a, 0xa4,
106     0x04, 0x29, 0x40, 0x4a, 0x30, 0x35, 0xc1, 0x50, 0x44, 0x32, 0x3e, 0x1a,
107     0x67, 0x2c, 0x99, 0xe8, 0x0e, 0xbc, 0xd9, 0xd8, 0x61, 0x7b, 0x67, 0xef,
108     0xdb, 0x00, 0x74, 0x77, 0x82, 0x0f, 0xb0, 0x0a, 0x50, 0x5d, 0xaf, 0x23,
109     0x3c, 0x31, 0x21, 0x3c, 0x61, 0x0f, 0xc4, 0xdf, 0x66, 0x7e, 0xcc, 0x65,
110     0x83, 0x65, 0xf8, 0xe2, 0x2e, 0xca, 0x70, 0xe1, 0x5c, 0x0a, 0x5d, 0xd7,
111     0xfc, 0xee, 0xe5, 0x63, 0x00, 0xb5, 0x8d, 0x3a, 0x4a, 0xa9, 0xae, 0x78,
112     0x0f, 0xc2, 0x5d, 0x17, 0xfd, 0x11, 0x06, 0xf8, 0x72, 0xb5, 0x74, 0x2a,
113     0x80, 0xa6, 0xc9, 0xfe, 0xf3, 0x60, 0xa1, 0x0b, 0x50, 0xc8, 0xe7, 0xb6,
114     0x81, 0x1d, 0xcb, 0x3e, 0xa2, 0xb1, 0xb5, 0x8b, 0x40, 0x78, 0x6b, 0xc1,
115     0xbd, 0x86, 0xc5, 0x01, 0xe6, 0x66, 0x82, 0x3b, 0xe1, 0xbb, 0x2e, 0xc4,
116     0xfe, 0xbd, 0xe3, 0x96, 0x61, 0xc3, 0x2d, 0x03, 0x5e, 0x1b, 0xa0, 0x0d,
117     0xc0, 0xfc, 0xec, 0xd4, 0x8f, 0x04, 0xb0, 0xbe, 0x09, 0x3d, 0xfd, 0x13,
118     0x23, 0x35, 0x9e, 0x24, 0x11, 0x8f, 0x76, 0xfb, 0xeb, 0xb7, 0x1a, 0xd4,
119     0xdd, 0xd7, 0xad, 0x7f, 0xda, 0x9d, 0x06, 0xb0, 0x00, 0xc1, 0x3f, 0xa5,
120     0xde, 0x3a, 0x68, 0xdc, 0x79, 0x0a, 0x7d, 0x31, 0x3e, 0x9a, 0x60, 0x77,
121     0xef, 0xa0, 0xdb, 0x7f, 0xfe, 0xd7, 0xbf, 0x47, 0x29, 0xc5, 0xfc, 0xec,
122     0x14, 0x6f, 0xbc, 0xf2, 0xd2, 0xc0, 0x9c, 0x91, 0x44, 0xac, 0x9b, 0xbe,
123     0xb8, 0xb4, 0x9c, 0x3a, 0x06, 0x50, 0xdd, 0xa8, 0x9f, 0x2a, 0xfc, 0xfe,
124     0x07, 0x1f, 0xf1, 0x9f, 0x4f, 0x3f, 0xe3, 0x66, 0xb1, 0xd2, 0xfd, 0xcd,
125     0x34, 0x0d, 0xa6, 0x26, 0xd2, 0xcc, 0x65, 0x33, 0x5c, 0x59, 0xc8, 0x9e,
126     0x98, 0x6b, 0x1a, 0x46, 0xa0, 0xdf, 0x0f, 0xb0, 0x06, 0xbd, 0x12, 0xdc,
127     0x29, 0xfe, 0xfb, 0xbf, 0x1b, 0x44, 0x4c, 0x83, 0x67, 0x9f, 0x7a, 0x82,
128     0xf9, 0x6c, 0x86, 0xb9, 0x6c, 0x86, 0xa9, 0x89, 0x73, 0x68, 0xda, 0x9d,
129     0x8f, 0xe3, 0x8d, 0xcd, 0x2d, 0xde, 0x7e, 0xef, 0x43, 0xbf, 0xdb, 0x2a,
130     0xe4, 0x73, 0xb7, 0xbe, 0x93, 0x03, 0xaf, 0x5f, 0x1d, 0x6c, 0xaf, 0x1f,
131     0x47, 0x47, 0x1d, 0x8a, 0xa5, 0x2a, 0x2b, 0x6b, 0x65, 0x6e, 0xac, 0x95,
132     0xb9, 0xb9, 0x56, 0xe6, 0xc6, 0x5a, 0x89, 0xfd, 0x83, 0xc3, 0xfe, 0xc7,
133     0xfe, 0x0a, 0x41, 0x07, 0xbe, 0x02, 0x3a, 0x8d, 0xad, 0x5d, 0xad, 0x6d,
134     0xd9, 0x44, 0xcc, 0xa0, 0x55, 0x27, 0xc5, 0xee, 0xde, 0x01, 0x2b, 0x45,
135     0x5f, 0xa4, 0xcc, 0xca, 0x5a, 0x99, 0x62, 0xa9, 0x4a, 0xa7, 0xe3, 0x0c,
136     0x7a, 0xbc, 0x09, 0x5c, 0x07, 0xfe, 0x06, 0xbc, 0x09, 0xa1, 0x85, 0xbe,
137     0xb8, 0xb4, 0xbc, 0x0a, 0x4c, 0xbf, 0x7e, 0xf5, 0x25, 0x1e, 0xbe, 0x32,
138     0x1f, 0xc8, 0x74, 0x1c, 0x87, 0xca, 0xfa, 0x26, 0x2b, 0x9e, 0xc8, 0xca,
139     0x5a, 0x89, 0x95, 0xb5, 0x32, 0x9b, 0x8d, 0x9d, 0x93, 0xd8, 0x8a, 0xc0,
140     0x67, 0xa1, 0x76, 0xa3, 0x90, 0xcf, 0x05, 0xc8, 0xf4, 0x50, 0xd2, 0xc7,
141     0xc0, 0xaf, 0xfe, 0xf0, 0xd6, 0x5f, 0x78, 0xf2, 0x89, 0x47, 0xe9, 0x74,
142     0x3a, 0xec, 0xee, 0x1d, 0xb0, 0xfa, 0x55, 0x85, 0x9b, 0xc5, 0x0a, 0xad,
143     0xb6, 0x35, 0x48, 0x68, 0x1f, 0xb8, 0x16, 0x12, 0xba, 0x56, 0xc8, 0xe7,
144     0x4e, 0x7f, 0x39, 0x70, 0xdc, 0x81, 0xf3, 0xc0, 0x27, 0x0c, 0xf8, 0x82,
145     0xc1, 0xfd, 0x36, 0x59, 0x19, 0x30, 0xab, 0x62, 0x21, 0x9f, 0x53, 0x77,
146     0x23, 0x76, 0x2a, 0x80, 0x07, 0x61, 0x02, 0x8f, 0x03, 0x4f, 0x7b, 0xa2,
147     0xbe, 0xd0, 0xf5, 0x42, 0x3e, 0x77, 0x18, 0x7e, 0xfe, 0xfb, 0xc6, 0x37,
148     0xa1, 0xfe, 0x5a, 0x08, 0x89, 0xe7, 0x8c, 0xbc, 0x00, 0x00, 0x00, 0x00,
149     0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
150 };
151
152 #include <cassert>
153 #include <iostream>
154 #include <fstream>
155 #include <sstream>
156 using namespace std;
157 #include <qlayout.h>
158 #include <qpainter.h>
159 #include <qfiledialog.h>
160 #include <qmessagebox.h>
161 #include <qregexp.h>
162 #include <qimage.h>
163 #include <Music/Music.h>
164 using namespace Music;
165
166 // ---------------------- MScale ------------------------------
167
168 void MScale::load_scala(const QString& file_name)
169 {
170         ifstream file(file_name.latin1());
171
172         if(!file.is_open())
173                 throw MicrotonalView::tr("Unkown filename: '")+file_name+"'";
174
175         int lines_red = 0;
176         int num_line = 1;
177
178         char buff[1024];
179         file.getline(buff, 1024);
180         while(file)
181         {
182                 if(buff[0]!='!')
183                 {
184                         if(lines_red==0)
185                         {
186                                 m_name = QString(buff);
187                                 m_name = m_name.remove("\r");
188                                 m_name = m_name.remove("\n");
189                                 m_name = m_name.simplifyWhiteSpace();
190                                 lines_red++;
191                         }
192                         else if(lines_red==1)
193                         {
194                                 // ignore number of lines ...
195                                 lines_red++;
196                         }
197                         else
198                         {
199                                 bool is_ratio = true;
200                                 for(size_t i=0; is_ratio && i<strlen(buff); i++)
201                                         is_ratio = buff[i]!='.';
202
203                                 if(is_ratio)
204                                 {
205                                         int num=0, den=0;
206                                         char slash;
207                                         stringstream in(buff);
208                                         in >> num >> slash >> den;
209                                         if(num<0) throw QString("Line ")+QString::number(num_line)+": Parsed numerator is negative '"+QString::number(num)+"'";
210                                         if(slash!='/') throw QString("Line ")+QString::number(num_line)+": There is no slash between numerator and denominator"+slash+"'";
211                                         if(den<=0) throw QString("Line ")+QString::number(num_line)+": Parsed denominator is not positive'"+QString::number(den)+"'";
212                                         values.push_back(MValue(num, den));
213                                 }
214                                 else
215                                 {
216                                         float cents=0.0;
217                                         stringstream in(buff);
218                                         in >> cents;
219                                         if(cents<=0.0) throw QString("Line ")+QString::number(num_line)+": Parsed cents value is negative '"+QString::number(cents)+"'";
220                                         values.push_back(MValue(cents));
221                                 }
222                         }
223                 }
224                 file.getline(buff, 1024);
225                 num_line++;
226         }
227 }
228
229 MScale::MScale(const QString& name)
230 : m_name(name)
231 {
232 }
233 MScale::MScale(const QString& file_name, FileType type)
234 {
235         m_path = file_name;
236
237         if(type==SCALA) load_scala(file_name);
238         else
239         {
240                 cerr << "MicrotonalView::Scale::Scale unsupported file type '" << type << "'" << endl;
241         }
242 }
243
244 bool MScale::operator != (const MScale& scale)
245 {
246         return m_name!=scale.m_name;
247 }
248
249 // ---------------------- QRoot button -------------------------
250
251 QRoot::QRoot(MicrotonalView* view, int ht)
252 : QPushButton(view, "QRoot:"+QString::number(ht))
253 , m_ht(ht)
254 {
255 //      cerr << "QRoot::QRoot " << m_ht << endl;
256
257 //      if(ht>8) ht+=12;
258
259         connect(this, SIGNAL(clicked()), this, SLOT(clicked2()));
260         connect(this, SIGNAL(rootClicked(int)), view, SLOT(selectRoot(int)));
261         setFlat(true);
262         setText(h2n(ht, GetNotesName(), GetTonality(), false));
263         setToggleButton(true);
264         setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
265         setFixedWidth(35);              // TODO
266         setFocusPolicy(QPushButton::NoFocus);
267 }
268 void QRoot::clicked2()
269 {
270         emit(rootClicked(m_ht));
271 }
272
273 // --------------------------- MicrotonalView ----------------------------
274
275 MicrotonalView::MicrotonalView(QWidget* parent)
276 : QFrame(parent, "MicrotonalView")
277 , View("Microtonal", this)
278 , ui_scale_menu(this)
279 {
280         setting_selectedRoot = -1000;
281         m_AFreq = 440.0f;
282         m_selected_jivalue = NULL;
283         m_tuningFreq = 0.0;
284
285         // settings
286         QImage img;
287         img.loadFromData(g_icon_ji, sizeof(g_icon_ji), "PNG");
288         setting_show->setIconSet(QIconSet(QImage(img)));
289         setting_show->setOn(false);
290         hide();
291
292         m_popup_menu.insertItem(new Title(tr("Root's octave"), &m_popup_menu));
293         setting_octave = new QSpinBox(-3, 8, 1, &m_popup_menu);
294         QToolTip::add(setting_octave, tr("Root's octave"));
295         setting_octave->setValue(3);
296 //      m_old_octave = setting_octave->value();
297 //      connect(setting_octave, SIGNAL(valueChanged(int)), this, SLOT(octaveChanged(int)));
298         connect(setting_octave, SIGNAL(valueChanged(int)), this, SLOT(emitTuningFreqChanged()));
299         m_popup_menu.insertItem(setting_octave);
300
301         setting_keepRootToLeft = new QAction(this);
302         setting_keepRootToLeft->setMenuText(tr("Keep root to left side"));
303         setting_keepRootToLeft->setToggleAction(true);
304         connect(setting_keepRootToLeft, SIGNAL(toggled(bool)), this, SLOT(keepRootToLeft(bool)));
305         setting_keepRootToLeft->addTo(&m_popup_menu);
306
307         setting_loadScale = new QAction(this);
308         setting_loadScale->setMenuText(tr("Load Scala file ..."));
309         setting_loadScale->addTo(&m_popup_menu);
310         connect(setting_loadScale, SIGNAL(activated()), this, SLOT(loadScale()));
311         ui_scale_menu.setCheckable(true);
312         m_popup_menu.insertItem(tr("used scale"), &ui_scale_menu);
313
314         QVBoxLayout* layout = new QVBoxLayout(this, 0, 0, "MicrotonalView:layout");
315         QHBoxLayout* roots_layout = new QHBoxLayout(layout, 0, "MicrotonalView:roots_layout");
316         for(int i=0; i<13; i++)
317         {
318                 m_roots.push_back(new QRoot(this, i+3));
319                 roots_layout->addWidget(m_roots.back());
320                 if(i+1<13)
321                         roots_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
322         }
323         m_roots.back()->setDisabled(true);
324         ui_scale = new QScaleLabel(this);
325         layout->addWidget(ui_scale);
326         ui_ratios = new QRatiosLabel(this);
327         layout->addWidget(ui_ratios);
328
329         setMaximumHeight(ui_scale->maximumHeight()+ui_ratios->maximumHeight()+20);
330
331         load_default_scales();
332
333         refreshScaleList();
334
335         selectRoot(3);
336         selectScale(0);
337 }
338 void MicrotonalView::save()
339 {
340         s_settings->writeEntry("octave", setting_octave->value());
341         s_settings->writeEntry("keepRootToLeft", setting_keepRootToLeft->isOn());
342         QStringList scale_list;
343         for(size_t i=0; i<setting_scales.size(); i++)
344                 if(!setting_scales[i]->getPath().isEmpty())
345                         scale_list.push_back(setting_scales[i]->getPath());
346         s_settings->writeEntry("scales", scale_list);
347         s_settings->writeEntry("selectedScale", setting_selectedScale->getName());
348         s_settings->writeEntry("lastScalesDirectory", setting_lastScalesDirectory);
349         s_settings->writeEntry("selectedRoot", setting_selectedRoot);
350 }
351 void MicrotonalView::load()
352 {
353         setting_octave->setValue(s_settings->readNumEntry("octave", setting_octave->value()));
354         setting_keepRootToLeft->setOn(s_settings->readBoolEntry("keepRootToLeft", setting_keepRootToLeft->isOn()));
355         QStringList scale_list = s_settings->readListEntry("scales");
356         for(QStringList::iterator it=scale_list.begin(); it!=scale_list.end(); ++it)
357         {
358                 try
359                 {
360                         MScale* scale = new MScale(*it, MScale::SCALA);
361                         setting_scales.push_back(scale);
362                 }
363                 catch(QString error){cerr << "MicrotonalView::load " << error << endl;}
364         }
365         refreshScaleList();
366
367         selectScale(s_settings->readEntry("selectedScale", "default"));
368         setting_lastScalesDirectory = s_settings->readEntry("lastScalesDirectory", ".");
369         selectRoot(s_settings->readNumEntry("selectedRoot", m_roots[0]->m_ht));
370 }
371 void MicrotonalView::clearSettings()
372 {
373         s_settings->removeEntry("octave");
374         s_settings->removeEntry("keepRootToLeft");
375         s_settings->removeEntry("scales");
376         s_settings->removeEntry("selectedScale");
377         s_settings->removeEntry("lastScalesDirectory");
378         s_settings->removeEntry("selectedRoot");
379 }
380
381 /*void MicrotonalView::octaveChanged(int value)
382 {
383         if(value==0)
384         {
385                 if(m_old_octave==1)
386                 {
387                         m_old_octave = -1;
388                         setting_octave->setValue(-1);
389                 }
390                 else if(m_old_octave==-1)
391                 {
392                         m_old_octave = 1;
393                         setting_octave->setValue(1);
394                 }
395         }
396         else
397                 m_old_octave = value;
398 }*/
399
400 int MicrotonalView::getOctaveShift()
401 {
402         return 12*(setting_octave->value()-5);
403 }
404
405 QString MicrotonalView::getTuningNoteName()
406 {
407         if(hasTuningFreqSelected())
408         {
409                 if(m_selected_jivalue->is_ratio)
410                         return QString(h2n(setting_selectedRoot+getOctaveShift()))+":"+QString::number(m_selected_jivalue->num)+"/"+QString::number(m_selected_jivalue->den);
411                 else
412                         return QString(h2n(setting_selectedRoot+getOctaveShift()))+":"+QString::number(m_selected_jivalue->cents);
413         }
414
415         return "no tuning note selected";
416 }
417
418 void MicrotonalView::selectScale(int index)
419 {
420         assert(index>=0 && index<int(setting_scales.size()));
421
422         setting_selectedScale = setting_scales[index];
423
424         for(size_t i=0; i<setting_scales.size(); i++)
425                 ui_scale_menu.setItemChecked(i, false);
426         ui_scale_menu.setItemChecked(index, true);
427
428         ui_ratios->repaint();
429 }
430 void MicrotonalView::selectScale(const QString& name)
431 {
432         int index = -1;
433         for(size_t i=0; index==-1 && i<setting_scales.size(); i++)
434         {
435                 if(setting_scales[i]->getName()==name)
436                 {
437                         setting_selectedScale = setting_scales[i];
438                         index = i;
439                 }
440         }
441
442         if(index==-1)
443         {
444                 cerr << "MicrotonalView::selectScale unknown scale '" << name << "'" << endl;
445                 return;
446         }
447
448         for(size_t i=0; i<setting_scales.size(); i++)
449                 ui_scale_menu.setItemChecked(i, false);
450         ui_scale_menu.setItemChecked(index, true);
451
452         ui_ratios->repaint();
453 }
454 int MicrotonalView::getIndex(MScale* scale)
455 {
456         for(size_t i=0; i<setting_scales.size(); i++)
457                 if(setting_scales[i]==scale)
458                         return i;
459
460         return -1;
461 }
462
463 void MicrotonalView::notesNameChanged()
464 {
465         for(int i=0; i<13; i++)
466                 m_roots[i]->setText(h2n(m_roots[i]->m_ht, GetNotesName(), GetTonality(), false));
467 }
468
469 void MicrotonalView::keepRootToLeft(bool keep)
470 {
471         if(keep)
472         {
473                 for(int i=0; i<13; i++)
474                         m_roots[i]->m_ht = (setting_selectedRoot+i)%12;
475                 notesNameChanged();
476                 selectRoot(setting_selectedRoot);
477                 ui_ratios->repaint();
478         }
479         else
480         {
481         }
482 }
483
484 void MicrotonalView::setAFreq(float AFreq)
485 {
486         float AFreq_old = m_AFreq;
487
488         m_AFreq = AFreq;
489
490         if(m_AFreq!=AFreq_old)
491                 emitTuningFreqChanged();
492 }
493
494 void MicrotonalView::updateCursor(float freq)
495 {
496         ui_scale->m_htf = f2hf(freq, m_AFreq);
497         ui_scale->repaint();
498 }
499
500 void MicrotonalView::selectRoot(int ht)
501 {
502 //      cerr << "MicrotonalView::selectRoot " << ht << endl;
503
504         if(ht==setting_selectedRoot)
505         {
506                 for(int i=0; i<13; i++)
507                         m_roots[i]->setOn(m_roots[i]->m_ht==ht);
508         }
509         else
510         {
511                 if(setting_keepRootToLeft->isOn())
512                 {
513                         for(int i=0; i<13; i++)
514                                 m_roots[i]->m_ht = (ht+i)%12;
515                         notesNameChanged();
516                 }
517
518                 for(int i=0; i<13; i++)
519                         m_roots[i]->setOn(m_roots[i]->m_ht==ht);
520
521                 setting_selectedRoot = ht;
522
523                 ui_ratios->repaint();
524                 emitTuningFreqChanged();
525         }
526 }
527 void MicrotonalView::emitTuningFreqChanged()
528 {
529         m_tuningFreq = 0.0;
530         if(m_selected_jivalue!=NULL)
531         {
532                 if(m_selected_jivalue->is_ratio)
533                         m_tuningFreq = h2f(setting_selectedRoot+getOctaveShift(), m_AFreq)*m_selected_jivalue->ratio;
534                 else
535                         m_tuningFreq = h2f(setting_selectedRoot+getOctaveShift()+m_selected_jivalue->cents/100.0f, m_AFreq);
536         }
537
538         emit(tuningFreqChanged(m_tuningFreq));
539 }
540
541 MicrotonalView::QScaleLabel::QScaleLabel(MicrotonalView* view)
542 : QLabel(view, "QScaleLabel")
543 , ui_view(view)
544 {
545         setMaximumHeight(15);
546         m_htf = -1000.0f;
547 }
548 void MicrotonalView::QScaleLabel::drawContents(QPainter* p)
549 {
550         int left = ui_view->m_roots[0]->x() + ui_view->m_roots[0]->width()/2;
551         int right = ui_view->m_roots[12]->x() + ui_view->m_roots[12]->width()/2;
552         int w = right - left;
553         p->setBrush(QColor(255,255,255));
554         p->setPen(QColor(128,128,128));
555         p->drawRect(0, 0, left+1, height());
556         p->drawRect(right, 0, width()-(right+1), height());
557         p->setPen(QColor(0,0,0));
558         p->drawRect(left, 0, w+1, height());
559
560         if(m_htf!=-1000.0)
561         {
562                 float htw = w*(m_htf-ui_view->m_roots[0]->m_ht)/12.0f;
563                 while(htw>=w) htw-=w;
564                 while(htw<0) htw+=w;
565                 int x = int(left+htw);
566                 p->setPen(QColor(0,0,255));
567                 p->setBrush(QColor(191,191,255));
568                 QPointArray arr;
569                 arr.resize(3);
570                 arr.setPoint(0, x-5, 0);
571                 arr.setPoint(1, x+5, 0);
572                 arr.setPoint(2, x, 2*height()/3);
573                 p->drawConvexPolygon(arr);
574                 p->drawLine(x, 2*height()/3, x, height());
575
576                 if(htw<(w/12.0f)/4.0f || -(htw-w)<(w/12.0f)/4.0f)
577                 {
578                         if(htw<(w/12.0f)/4.0f)  x += w;
579                         else                                    x -= w;
580                         arr.setPoint(0, x-5, 0);
581                         arr.setPoint(1, x+5, 0);
582                         arr.setPoint(2, x, 2*height()/3);
583                         p->drawConvexPolygon(arr);
584                         p->drawLine(x, 2*height()/3, x, height());
585                 }
586         }
587 }
588
589 MicrotonalView::QRatiosLabel::QRatiosLabel(MicrotonalView* view)
590 : QLabel(view, "QRatiosLabel")
591 , ui_view(view)
592 {
593         setMaximumHeight(12+3*(2*fontMetrics().height()+10));
594 }
595 void MicrotonalView::QRatiosLabel::drawTicks(QPainter* p, float r, int h)
596 {
597         int left = ui_view->m_roots[0]->x() + ui_view->m_roots[0]->width()/2;
598         int right = ui_view->m_roots[12]->x() + ui_view->m_roots[12]->width()/2;
599         int w = right - left;
600
601         for(float i=r; i<=12.0f; i+=2*r)
602         {
603                 int x = left + int(w*i/12.0f);
604                 p->drawLine(x, 0, x, h);
605         }
606 }
607 void MicrotonalView::QRatiosLabel::drawContents(QPainter* p)
608 {
609         QLabel::drawContents(p);
610         //      p->eraseRect(rect());
611
612         int left = ui_view->m_roots[0]->x() + ui_view->m_roots[0]->width()/2;
613         int right = ui_view->m_roots[12]->x() + ui_view->m_roots[12]->width()/2;
614         int w = right - left;
615
616         int tick_height = 12;
617         int max_dec = 3;
618
619         // draw ratios
620         p->setPen(QColor(0,0,255));
621         int dec = 0;
622         int dec_h = 2*p->fontMetrics().height()+2;
623         int dec_h2 = (height()-tick_height-p->fontMetrics().height()+2)/3;
624         dec_h = min(dec_h, dec_h2);
625         int ht = ui_view->setting_selectedRoot - ui_view->m_roots[0]->m_ht;
626         if(ui_view->setting_keepRootToLeft->isOn())     ht = 0;
627         for(list<MScale::MValue>::iterator it=ui_view->setting_selectedScale->values.begin(); it!=ui_view->setting_selectedScale->values.end(); ++it)
628         {
629                 float htw = 0.0f;
630
631                 if((*it).is_ratio)
632                         htw = w*f2hf(h2f(ht)*(*it).ratio)/12.0f;
633                 else
634                         htw = w*(ht+(*it).cents/100.0f)/12.0f;
635                 while(htw>w) htw-=w;
636                 while(htw<0) htw+=w;
637
638                 int x = left + int(htw);
639                 int y = tick_height + 2 + dec_h*dec;
640                 p->drawLine(x, 0, x, y+dec_h);
641
642                 if((*it).is_ratio)
643                 {
644                         QRect num_rect = fontMetrics().boundingRect(QString::number((*it).num));
645                         num_rect.moveBy(x+1, y+p->fontMetrics().height());
646                         QRect den_rect = fontMetrics().boundingRect(QString::number((*it).den));
647                         den_rect.moveBy(x+1, y+2*p->fontMetrics().height());
648                         (*it).bounding_rect = num_rect;
649                         (*it).bounding_rect |= den_rect;
650                 }
651                 else
652                 {
653                         QRect cents_rect = fontMetrics().boundingRect(QString::number((*it).cents));
654                         cents_rect.moveBy(x+1, y+2*p->fontMetrics().height());
655
656                         (*it).bounding_rect = cents_rect;
657                 }
658                 (*it).bounding_rect.rLeft() = x;
659                 (*it).bounding_rect.rRight() += 2;
660                 (*it).bounding_rect.rTop() -= 2;
661                 (*it).bounding_rect.rBottom() = y+dec_h;
662
663                 if(ui_view->m_selected_jivalue==&(*it))
664                 {
665                         p->setBrush(QColor(210,210,255));
666                         p->drawRect((*it).bounding_rect);
667                 }
668
669                 if((*it).is_ratio)
670                 {
671                         p->drawText(x+1, y+p->fontMetrics().height(), QString::number((*it).num));
672                         p->drawLine(x, y+p->fontMetrics().height()+2, x+p->fontMetrics().width(QString::number((*it).num)), y+p->fontMetrics().height()+2);
673                         p->drawText(x+1, y+2*p->fontMetrics().height(), QString::number((*it).den));
674                 }
675                 else
676                 {
677                         p->drawText(x+1, y+2*p->fontMetrics().height(), QString::number((*it).cents));
678                 }
679
680                 dec++;
681                 if(dec>max_dec-1)       dec = 0;
682                 if(dec<0)                       dec = max_dec-1;
683         }
684
685         // draw ticks
686         p->setPen(QColor(0,0,0));
687         for(float i=0.0f; i<=12.0f; i+=1.0f)
688         {
689                 int x = left + int(w*i/12.0f);
690                 p->drawLine(x, 0, x, tick_height);
691         }
692         drawTicks(p, 0.5f, tick_height/2);
693         drawTicks(p, 0.25f, tick_height/3);
694         drawTicks(p, 0.125f, tick_height/4);
695 }
696
697 void MicrotonalView::mouseReleaseEvent(QMouseEvent* e)
698 {
699         if(e->button()==LeftButton)
700         {
701                 MScale::MValue* selected_jivalue_old = m_selected_jivalue;
702                 m_selected_jivalue = NULL;
703
704                 QPoint mouse_pos = e->pos() - ui_ratios->pos();
705                 for(list<MScale::MValue>::iterator it=setting_selectedScale->values.begin(); m_selected_jivalue==NULL && it!=setting_selectedScale->values.end(); ++it)
706                         if((*it).bounding_rect.contains(mouse_pos.x(), mouse_pos.y()))
707                                 m_selected_jivalue = &(*it);
708
709                 if(m_selected_jivalue!=selected_jivalue_old)
710                 {
711                         ui_ratios->repaint();
712                         emitTuningFreqChanged();
713                 }
714         }
715
716         View::mouseReleaseEvent(e);
717 }
718
719 void MicrotonalView::refreshScaleList()
720 {
721         ui_scale_menu.clear();
722         for(size_t i=0; i<setting_scales.size(); i++)
723         {
724                 ui_scale_menu.insertItem(setting_scales[i]->getName(), i);
725                 ui_scale_menu.connectItem(i, this, SLOT(selectScale(int)));
726                 if(setting_selectedScale==setting_scales[i])
727                         ui_scale_menu.setItemChecked(i, true);
728         }
729 }
730 void MicrotonalView::loadScale()
731 {
732         QFileDialog dlg_file(setting_lastScalesDirectory, "Scala files (*.scl *.SCL)", this, "open file dialog", true);
733         dlg_file.setCaption(tr("Open scale file"));
734         dlg_file.setMode(QFileDialog::ExistingFile);
735         dlg_file.setInfoPreviewEnabled(true);
736         ScalePreview p(&dlg_file);
737         dlg_file.setInfoPreview(&p,&p);
738         dlg_file.setPreviewMode(QFileDialog::Info);
739
740         dlg_file.exec();
741
742         if(dlg_file.result()==QDialog::Accepted)
743         {
744                 setting_lastScalesDirectory = dlg_file.dirPath();
745                 s_settings->writeEntry("MicrotonalView_lastScalesDirectory", setting_lastScalesDirectory);
746 //              cerr << "setting_lastScalesDirectory=" << setting_lastScalesDirectory << endl;
747                 try
748                 {
749                         MScale* scale = new MScale(dlg_file.selectedFile(), MScale::SCALA);
750                         bool new_one = true;
751                         for(size_t i=0; new_one && i<setting_scales.size(); i++)
752                                 new_one = *scale != *(setting_scales[i]);
753                         if(new_one)
754                         {
755                                 setting_scales.push_back(scale);
756                                 selectScale(setting_scales.size()-1);
757                                 refreshScaleList();
758                         }
759                         else
760                                 QMessageBox::information(this, tr("Open scale file"), tr("Scale name already exist"), QMessageBox::Ok);
761                 }
762                 catch(QString error)
763                 {
764                         QMessageBox::information(this, tr("Open scale file"), tr("Invalid file content !")+"\n("+error+")", QMessageBox::Ok);
765                 }
766         }
767
768         ui_ratios->repaint();
769 }
770
771 void MicrotonalView::load_default_scales()
772 {
773         MScale* scale = new MScale("default");
774         scale->values.push_back(MScale::MValue(25,24));
775         scale->values.push_back(MScale::MValue(16,15));
776         scale->values.push_back(MScale::MValue(9,8));
777         scale->values.push_back(MScale::MValue(7,6));
778         scale->values.push_back(MScale::MValue(75,64));
779         scale->values.push_back(MScale::MValue(6,5));
780         scale->values.push_back(MScale::MValue(5,4));
781         scale->values.push_back(MScale::MValue(32,25));
782         scale->values.push_back(MScale::MValue(125,96));
783         scale->values.push_back(MScale::MValue(4,3));
784         scale->values.push_back(MScale::MValue(25,18));
785         scale->values.push_back(MScale::MValue(45,32));
786         scale->values.push_back(MScale::MValue(36,25));
787         scale->values.push_back(MScale::MValue(3,2));
788         scale->values.push_back(MScale::MValue(25,16));
789         scale->values.push_back(MScale::MValue(8,5));
790         scale->values.push_back(MScale::MValue(5,3));
791         scale->values.push_back(MScale::MValue(125,72));
792         scale->values.push_back(MScale::MValue(16,9));
793         scale->values.push_back(MScale::MValue(9,5));
794         scale->values.push_back(MScale::MValue(15,8));
795         scale->values.push_back(MScale::MValue(48,25));
796         scale->values.push_back(MScale::MValue(125,64));
797         setting_scales.push_back(scale);
798 }
799
800 // ------------------ MicrotonalView::ScalePreview --------------------
801 MicrotonalView::ScalePreview::ScalePreview(QWidget* parent)
802 : QLabel(parent)
803 {
804         setAlignment(QLabel::WordBreak|QLabel::AlignVCenter);
805         setMinimumWidth(100);
806 }
807 void MicrotonalView::ScalePreview::previewUrl(const QUrl& url)
808 {
809         if(url.path().contains(QRegExp("\\.scl$"))==0 && url.path().contains(QRegExp("\\.SCL$"))==0)
810         {
811 //              setText("");
812         }
813         else
814         {
815                 try
816                 {
817                         MScale* scale = new MScale(url.path(), MScale::SCALA);
818                         QString txt;
819                         txt += tr("<b>name:</b> ") + scale->getName() + "<p>";
820                         txt += tr("<b>number of ratio:</b> ") + QString::number(scale->values.size());
821                         setText(txt);
822                         delete scale;
823                 }
824                 catch(QString error)
825                 {
826                         setText(tr("Invalid Scala file: ")+error);
827                 }
828         }
829 }
830