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 #include "NoteStyleFileReader.h"
19 
20 #include <string>
21 #include "misc/Strings.h"
22 #include "NotationStrings.h"
23 #include "misc/Debug.h"
24 #include "gui/general/ResourceFinder.h"
25 #include "NoteStyle.h"
26 #include "document/io/XMLReader.h"
27 
28 #include <QFileInfo>
29 #include <QDir>
30 
31 
32 
33 namespace Rosegarden {
34 
35 
36 NoteStyleFileReader::NoteStyleFileReader(QString name) :
37     m_style(new NoteStyle(name)),
38     m_haveNote(false)
39 {
40 /*!!!
41     //QString styleDirectory =
42     //    KGlobal::dirs()->findResource("appdata", "styles/");
43     QString styleDirectory = IconLoader::getResourcePath( "styles" );
44 
45     QString styleFileName =
46         QString("%1/%2.xml").arg(styleDirectory).arg(name);
47 */
48     QString styleFileName = ResourceFinder().getResourcePath
49         ("styles", QString("%1.xml").arg(name));
50 
51     QFileInfo fileInfo(styleFileName);
52 
53     if (styleFileName == "" || !fileInfo.isReadable()) {
54         throw StyleFileReadFailed(tr("Can't open style file \"%1\" for style \"%2\"").arg(styleFileName).arg(name));
55     }
56 
57     QFile styleFile(styleFileName);
58 
59     XMLReader reader;
60     reader.setHandler(this);
61     bool ok = reader.parse(styleFile);
62     styleFile.close();
63 
64     if (!ok) {
65         throw StyleFileReadFailed(m_errorString);
66     }
67 }
68 
69 bool
70 NoteStyleFileReader::startElement(const QString &, const QString &,
71                                   const QString &qName,
72                                   const QXmlStreamAttributes &attributes)
73 {
74     QString lcName = qName.toLower();
75 
76     if (lcName == "rosegarden-note-style") {
77 
78         QString s = attributes.value("base-style").toString();
79         if ( !s.isEmpty() ) m_style->setBaseStyle(s);
80 
81     } else if (lcName == "note") {
82 
83         m_haveNote = true;
84 
85         QString s = attributes.value("type").toString();
86         if (s.isEmpty() ) {
87             m_errorString = tr("type is a required attribute of note");
88             return false;
89         }
90 
91         try {
92             Note::Type type = NotationStrings::getNoteForName(s).getNoteType();
93             if (!setFromAttributes(type, attributes)) return false;
94 
95         } catch (const NotationStrings::MalformedNoteName &n) {
96             m_errorString = tr("Unrecognised note name %1").arg(s);
97             return false;
98         }
99 
100     } else if (lcName == "global") {
101 
102         if (m_haveNote) {
103             m_errorString = tr("global element must precede note elements");
104             return false;
105         }
106 
107         for (Note::Type type = Note::Shortest; type <= Note::Longest; ++type) {
108             if (!setFromAttributes(type, attributes)) return false;
109         }
110     }
111 
112     return true;
113 }
114 
115 
116 bool
117 NoteStyleFileReader::setFromAttributes(Note::Type type,
118                                        const QXmlStreamAttributes &attributes)
119 {
120     QString s;
121     bool haveShape = false;
122 
123     s = attributes.value("shape").toString();
124     if (!s.isEmpty() ) {
125         m_style->setShape(type, s.toLower());
126         haveShape = true;
127     }
128 
129     s = attributes.value("charname").toString();
130     if (!s.isEmpty() ) {
131         if (haveShape) {
132             m_errorString = tr("global and note elements may have shape "
133                                "or charname attribute, but not both");
134             return false;
135         }
136         m_style->setShape(type, NoteStyle::CustomCharName);
137         m_style->setCharName(type, s);
138     }
139 
140     s = attributes.value("filled").toString();
141     if (!s.isEmpty() ) m_style->setFilled(type, s.toLower() == "true");
142 
143     s = attributes.value("stem").toString();
144     if (!s.isEmpty() ) m_style->setStem(type, s.toLower() == "true");
145 
146     s = attributes.value("flags").toString();
147     if (!s.isEmpty() ) m_style->setFlagCount(type, s.toInt());
148 
149     s = attributes.value("slashes").toString();
150     if (!s.isEmpty() ) m_style->setSlashCount(type, s.toInt());
151 
152     NoteStyle::HFixPoint hfix;
153     NoteStyle::VFixPoint vfix;
154     m_style->getStemFixPoints(type, hfix, vfix);
155     bool haveHFix = false;
156     bool haveVFix = false;
157 
158     s = attributes.value("hfixpoint").toString();
159     if (!s.isEmpty() ) {
160         s = s.toLower();
161         haveHFix = true;
162         if (s == "normal") hfix = NoteStyle::Normal;
163         else if (s == "central") hfix = NoteStyle::Central;
164         else if (s == "reversed") hfix = NoteStyle::Reversed;
165         else haveHFix = false;
166     }
167 
168     s = attributes.value("vfixpoint").toString();
169     if (!s.isEmpty() ) {
170         s = s.toLower();
171         haveVFix = true;
172         if (s == "near") vfix = NoteStyle::Near;
173         else if (s == "middle") vfix = NoteStyle::Middle;
174         else if (s == "far") vfix = NoteStyle::Far;
175         else haveVFix = false;
176     }
177 
178     if (haveHFix || haveVFix) {
179         m_style->setStemFixPoints(type, hfix, vfix);
180         // otherwise they inherit from base style, so avoid setting here
181     }
182 
183     return true;
184 }
185 
186 
187 }
188 
189