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