1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 Other copyrights also apply to some parts of this work. Please
9 see the AUTHORS file and individual file headers for details.
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information.
16 */
17
18 #define RG_MODULE_STRING "[ChordMap]"
19
20 #include "misc/Debug.h"
21 #include "misc/Strings.h"
22 #include "ChordMap.h"
23
24 #include "rosegarden-version.h"
25
26 #include <QFile>
27 #include <QTextStream>
28
29 namespace Rosegarden
30 {
31
32 namespace Guitar
33 {
34
35 int ChordMap::FILE_FORMAT_VERSION_MAJOR = 1;
36 int ChordMap::FILE_FORMAT_VERSION_MINOR = 0;
Chord()37 int ChordMap::FILE_FORMAT_VERSION_POINT = 0;
38
39 ChordMap::ChordMap()
40 : m_needSave(false)
41 {
Chord(const QString & root,const QString & ext)42 }
43
44 void ChordMap::insert(const Chord& c)
45 {
46 m_map.insert(c);
47 m_needSave = true;
48 }
49
50
Chord(const Event & e)51 ChordMap::chordarray
52 ChordMap::getChords(const QString& root, const QString& ext) const
53 {
54 chordarray res;
55
56 Chord tmp(root, ext);
57
58 // RG_DEBUG << "ChordMap::getChords() : chord = " /*<< tmp*/ <<
59 // " - ext is empty : " << ext.isEmpty();
60
61 for (chordset::const_iterator i = m_map.lower_bound(tmp); i != m_map.end(); ++i) {
62
63 // RG_DEBUG << "ChordMap::getChords() : checking chord "/* << *i*/;
64
65 if (i->getRoot() != root)
66 break;
67
68 if (/* ext.isNull() || */ i->getExt() == ext) {
69
70 // RG_DEBUG << "ChordMap::getChords() : adding chord " << *i;
71
72 res.push_back(*i);
73 } else {
74 break;
75 }
76 }
77
78 return res;
79 }
80
81 QStringList
82 ChordMap::getRootList() const
83 {
84 static QStringList rootNotes;
85
86 if (rootNotes.count() == 0) {
87 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
hasAltBass() const88 rootNotes = QString("A,A#/Bb,B,C,C#/Db,D,D#/Eb,E,F,F#/Gb,G,G#/Ab")
89 .split(",", Qt::SkipEmptyParts);
90 #else
91 rootNotes = QString("A,A#/Bb,B,C,C#/Db,D,D#/Eb,E,F,F#/Gb,G,G#/Ab")
92 .split(",", QString::SkipEmptyParts);
93 #endif
operator <(const Chord & a,const Chord & b)94 }
95
96 // extract roots from map itself - not a very good idea
97 //
98 // QString currentRoot;
99 //
100 // for(chordset::const_iterator i = m_map.begin(); i != m_map.end(); ++i) {
101 // const Chord& chord = *i;
102 // if (chord.getRoot() != currentRoot) {
103 // rootNotes.push_back(chord.getRoot());
104 // currentRoot = chord.getRoot();
105 // }
106 // }
107
108 return rootNotes;
109 }
110
111 QStringList
operator <<(QDebug & dbg,const Rosegarden::Guitar::Chord & c)112 ChordMap::getExtList(const QString& root) const
113 {
114 QStringList extList;
115 QString currentExt = "ZZ";
116
117 Chord tmp(root);
118
119 for (chordset::const_iterator i = m_map.lower_bound(tmp);
120 i != m_map.end(); ++i) {
121 const Chord& chord = *i;
122
123 // RG_DEBUG << "ChordMap::getExtList() : chord = " << chord;
124
125 if (chord.getRoot() != root)
126 break;
127
128 if (chord.getExt() != currentExt) {
129
130 // RG_DEBUG << "ChordMap::getExtList() : adding ext " << chord.getExt() <<
131 // " for root " << root;
132
133 extList.push_back(chord.getExt());
134 currentExt = chord.getExt();
135 }
136 }
137
138 return extList;
139 }
140
141 void
142 ChordMap::substitute(const Chord& oldChord, const Chord& newChord)
143 {
144 remove(oldChord);
145 insert(newChord);
146 }
147
148 void
149 ChordMap::remove(const Chord& c)
150 {
151 m_map.erase(c);
152 m_needSave = true;
153 }
154
155 bool ChordMap::saveDocument(
156 const QString& filename, bool userChordsOnly, QString& /*errMsg*/)
157 {
158 QFile file(filename);
159 file.open(QIODevice::WriteOnly);
160
161 QTextStream outStream(&file);
162
163 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
164 // qt6 default codec is UTF-8
165 #else
166 outStream.setCodec("UTF-8");
167 #endif
168
169 outStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
170 << "<!DOCTYPE rosegarden-chord-data>\n"
171 << "<rosegarden-chord-data version=\"" << VERSION
172 << "\" format-version-major=\"" << FILE_FORMAT_VERSION_MAJOR
173 << "\" format-version-minor=\"" << FILE_FORMAT_VERSION_MINOR
174 << "\" format-version-point=\"" << FILE_FORMAT_VERSION_POINT
175 << "\">\n";
176
177 outStream << "<chords>\n";
178
179 QString currentExt, currentRoot;
180 bool inChord = false;
181 bool inChordset = false;
182
183 for(iterator i = begin(); i != end(); ++i) {
184 const Chord& chord = *i;
185
186 if (userChordsOnly && !chord.isUserChord())
187 continue; // skip non-user chords
188
189 if (chord.getRoot() != currentRoot) {
190
191 currentRoot = chord.getRoot();
192
193 // If we are in a <chord>, close it.
194 if (inChord) {
195 outStream << " </chord>\n";
196 inChord = false;
197 }
198
199 // If we are in a <chordset>, close it.
200 if (inChordset) {
201 outStream << " </chordset>\n";
202 inChordset = false;
203 }
204
205 // open new chordset
206 outStream << " <chordset root=\"" << chord.getRoot() << "\">\n";
207 inChordset = true;
208 currentExt = "NEWEXT"; // make sure we open a new chord
209 }
210
211 if (chord.getExt() != currentExt) {
212
213 currentExt = chord.getExt();
214
215 // If we are in a <chord>, close it.
216 if (inChord) {
217 outStream << " </chord>\n";
218 inChord = false;
219 }
220
221 // open new chord
222 outStream << " <chord";
223 if (!chord.getExt().isEmpty())
224 outStream << " ext=\"" << chord.getExt() << "\"";
225 if (chord.isUserChord())
226 outStream << " user=\"true\"";
227
228 outStream << ">\n";
229 inChord = true;
230 }
231
232 outStream << " <fingering>" << chord.getFingering().toString().c_str() <<
233 "</fingering>\n";
234 }
235
236 // If we are in a <chord>, close it.
237 if (inChord) {
238 outStream << " </chord>\n";
239 inChord = false;
240 }
241
242 // If we are in a <chordset>, close it.
243 if (inChordset) {
244 outStream << " </chordset>\n";
245 inChordset = false;
246 }
247
248 outStream << "</chords>\n";
249 outStream << "</rosegarden-chord-data>\n";
250
251 return outStream.status() == QTextStream::Ok;
252 }
253
254 void
255 ChordMap::debugDump() const
256 {
257 RG_DEBUG << "ChordMap::debugDump()";
258
259 for (chordset::const_iterator chord = m_map.begin();
260 chord != m_map.end(); ++chord) {
261 RG_DEBUG << " " << *chord;
262 }
263 }
264
265 }
266
267 }
268