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 
19 #include "RespellCommand.h"
20 
21 #include "base/NotationTypes.h"
22 #include "base/Selection.h"
23 #include "document/BasicSelectionCommand.h"
24 #include "document/CommandRegistry.h"
25 #include "base/BaseProperties.h"
26 #include <QString>
27 
28 
29 namespace Rosegarden
30 {
31 using namespace BaseProperties;
32 using namespace Accidentals;
33 
34 QString
35 RespellCommand::getGlobalName(RespellType type)
36 {
37     switch (type.type) {
38 
39     case RespellType::Set: {
40             QString s(tr("Respell with %1"));
41             //!!! should be in notationstrings:
42             if (type.accidental == DoubleSharp) {
43                 s = s.arg(tr("Do&uble Sharp"));
44             } else if (type.accidental == Sharp) {
45                 s = s.arg(tr("&Sharp"));
46             } else if (type.accidental == Flat) {
47                 s = s.arg(tr("&Flat"));
48             } else if (type.accidental == DoubleFlat) {
49                 s = s.arg(tr("Dou&ble Flat"));
50             } else if (type.accidental == Natural) {
51                 s = s.arg(tr("&Natural"));
52             } else {
53                 s = s.arg(tr("N&one"));
54             }
55             return s;
56         }
57 
58     case RespellType::Up:
59         return tr("Respell Accidentals &Upward");
60 
61     case RespellType::Down:
62         return tr("Respell Accidentals &Downward");
63 
64     case RespellType::Restore:
65         return tr("&Restore Accidentals");
66     }
67 
68     return tr("Respell Accidentals");
69 }
70 
71 RespellCommand::RespellType
72 RespellCommand::getArgument(QString actionName, CommandArgumentQuerier &)
73 {
74     RespellType type;
75     type.type = RespellType::Set;
76     type.accidental = Natural;
77 
78     if (actionName == "respell_doubleflat") {
79         type.accidental = DoubleFlat;
80     } else if (actionName == "respell_flat") {
81         type.accidental = Flat;
82     } else if (actionName == "respell_natural") {
83         type.accidental = Natural;
84     } else if (actionName == "respell_sharp") {
85         type.accidental = Sharp;
86     } else if (actionName == "respell_doublesharp") {
87         type.accidental = DoubleSharp;
88     } else if (actionName == "respell_restore") {
89         type.type = RespellType::Restore;
90     } else if (actionName == "respell_up") {
91         type.type = RespellType::Up;
92     } else if (actionName == "respell_down") {
93         type.type = RespellType::Down;
94     }
95 
96     return type;
97 }
98 
99 void
100 RespellCommand::registerCommand(CommandRegistry *r)
101 {
102     RespellType type;
103     type.type = RespellType::Set;
104 
105     type.accidental = DoubleFlat;
106     r->registerCommand
107         ("respell_doubleflat",
108          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
109 
110     type.accidental = Flat;
111     r->registerCommand
112         ("respell_flat",
113          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
114 
115     type.accidental = Natural;
116     r->registerCommand
117         ("respell_natural",
118          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
119 
120     type.accidental = Sharp;
121     r->registerCommand
122         ("respell_sharp",
123          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
124 
125     type.accidental = DoubleSharp;
126     r->registerCommand
127         ("respell_doublesharp",
128          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
129 
130     type.accidental = Natural;
131 
132     type.type = RespellType::Up;
133     r->registerCommand
134         ("respell_up",
135          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
136 
137     type.type = RespellType::Down;
138     r->registerCommand
139         ("respell_down",
140          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
141 
142     type.type = RespellType::Restore;
143     r->registerCommand
144         ("respell_restore",
145          new ArgumentAndSelectionCommandBuilder<RespellCommand>());
146 }
147 
148 void
149 RespellCommand::modifySegment()
150 {
151     EventSelection::eventcontainer::iterator i;
152 
153     for (i = m_selection->getSegmentEvents().begin();
154          i != m_selection->getSegmentEvents().end(); ++i) {
155 
156         if ((*i)->isa(Note::EventType)) {
157 
158             if (m_type.type == RespellType::Up ||
159                 m_type.type == RespellType::Down) {
160 
161                 Accidental acc = NoAccidental;
162                 (*i)->get<String>(ACCIDENTAL, acc);
163 
164                 if (m_type.type == RespellType::Down) {
165                     if (acc == DoubleFlat) {
166                         acc = Flat;
167                     } else if (acc == Flat || acc == NoAccidental) {
168                         acc = Sharp;
169                     } else if (acc == Sharp) {
170                         acc = DoubleSharp;
171                     }
172                 } else {
173                     if (acc == Flat) {
174                         acc = DoubleFlat;
175                     } else if (acc == Sharp || acc == NoAccidental) {
176                         acc = Flat;
177                     } else if (acc == DoubleSharp) {
178                         acc = Sharp;
179                     }
180                 }
181 
182                 (*i)->set<String>(ACCIDENTAL, acc);
183 
184             } else if (m_type.type == RespellType::Set) {
185 
186                 // trap respelling black key notes as natural; which is
187                 // impossible, and makes rawPitchToDisplayPitch() do crazy
188                 // things as a consequence (fixes #1349782)
189                 // 1 = C#, 3 = D#, 6 = F#, 8 = G#, 10 = A#
190                 long pitch;
191                 pitch = 0;  // Avoid a "may be used uninitialized" compilation warning
192                 (*i)->get<Int>(PITCH, pitch);
193                 pitch %= 12;
194                 if ((pitch == 1 || pitch == 3 || pitch == 6 || pitch == 8 || pitch == 10 )
195                     && m_type.accidental == Natural) {
196                     // fail silently; is there anything to do here?
197                 } else {
198                     (*i)->set<String>(ACCIDENTAL, m_type.accidental);
199                 }
200 
201             } else {
202 
203                 (*i)->unset(ACCIDENTAL);
204             }
205         }
206     }
207 }
208 
209 }
210