1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2011 Werner Schweer and others
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENSE.GPL
11 //=============================================================================
12
13 #include "scoreview.h"
14 #include "musescore.h"
15 #include "libmscore/undo.h"
16
17 #include "libmscore/score.h"
18 #include "libmscore/element.h"
19 #include "seq.h"
20 #include "libmscore/mscore.h"
21
22 #include "articulationprop.h"
23 #include "timesigproperties.h"
24 #include "stafftextproperties.h"
25 #include "selinstrument.h"
26 #include "pianoroll/pianoroll.h"
27 #include "editstyle.h"
28 #include "editstaff.h"
29 #include "measureproperties.h"
30
31 #include "libmscore/staff.h"
32 #include "libmscore/segment.h"
33 #include "libmscore/bend.h"
34 #include "libmscore/box.h"
35 #include "libmscore/text.h"
36 #include "libmscore/articulation.h"
37 #include "libmscore/volta.h"
38 #include "libmscore/timesig.h"
39 #include "libmscore/accidental.h"
40 #include "libmscore/clef.h"
41 #include "libmscore/dynamic.h"
42 #include "libmscore/tempotext.h"
43 #include "libmscore/keysig.h"
44 #include "libmscore/stafftext.h"
45 #include "libmscore/staffstate.h"
46 #include "libmscore/note.h"
47 #include "libmscore/layoutbreak.h"
48 #include "libmscore/image.h"
49 #include "libmscore/hairpin.h"
50 #include "libmscore/chord.h"
51 #include "libmscore/rest.h"
52 #include "libmscore/harmony.h"
53 #include "libmscore/glissando.h"
54 #include "libmscore/fret.h"
55 #include "libmscore/instrchange.h"
56 #include "libmscore/instrtemplate.h"
57 #include "libmscore/slur.h"
58 #include "libmscore/jump.h"
59 #include "libmscore/marker.h"
60 #include "libmscore/measure.h"
61 #include "libmscore/iname.h"
62 #include "libmscore/system.h"
63
64 namespace Ms {
65
66 //---------------------------------------------------------
67 // genPropertyMenu1
68 //---------------------------------------------------------
69
genPropertyMenu1(Element * e,QMenu * popup)70 void ScoreView::genPropertyMenu1(Element* e, QMenu* popup)
71 {
72 if ((!e->generated() || e->type() == ElementType::BAR_LINE) && enableExperimental){
73 if (e->flag(ElementFlag::HAS_TAG)) {
74 popup->addSeparator();
75
76 QMenu* menuLayer = new QMenu(tr("Layer"));
77 for (int i = 0; i < MAX_TAGS; ++i) {
78 QString tagName = score()->layerTags()[i];
79 if (!tagName.isEmpty()) {
80 QAction* a = menuLayer->addAction(tagName);
81 a->setData(QString("layer-%1").arg(i));
82 a->setCheckable(true);
83 a->setChecked(e->tag() & (1 << i));
84 }
85 }
86 popup->addMenu(menuLayer);
87 }
88 }
89 }
90
91 //---------------------------------------------------------
92 // genPropertyMenuText
93 //---------------------------------------------------------
94
genPropertyMenuText(Element * e,QMenu * popup)95 void ScoreView::genPropertyMenuText(Element* e, QMenu* popup)
96 {
97 if (e->flag(ElementFlag::HAS_TAG) && enableExperimental) {
98 popup->addSeparator();
99
100 QMenu* menuLayer = new QMenu(tr("Layer"));
101 for (int i = 0; i < MAX_TAGS; ++i) {
102 QString tagName = score()->layerTags()[i];
103 if (!tagName.isEmpty()) {
104 QAction* a = menuLayer->addAction(tagName);
105 a->setData(QString("layer-%1").arg(i));
106 a->setCheckable(true);
107 a->setChecked(e->tag() & (1 << i));
108 }
109 }
110 popup->addMenu(menuLayer);
111 }
112 // popup->addAction(tr("Text Style…"))->setData("text-style");
113 // popup->addAction(tr("Text Properties…"))->setData("text-props");
114 }
115
116 //---------------------------------------------------------
117 // createElementPropertyMenu
118 //---------------------------------------------------------
119
createElementPropertyMenu(Element * e,QMenu * popup)120 void ScoreView::createElementPropertyMenu(Element* e, QMenu* popup)
121 {
122 if (e->isBarLine())
123 genPropertyMenu1(e, popup);
124 else if (e->isArticulation()) {
125 genPropertyMenu1(e, popup);
126 popup->addAction(tr("Articulation Properties…"))->setData("a-props");
127 }
128 else if (e->isBeam())
129 popup->addAction(getAction("flip"));
130 else if (e->isStem())
131 popup->addAction(getAction("flip"));
132 else if (e->isHook())
133 popup->addAction(getAction("flip"));
134 else if (e->isHBox()) {
135 QMenu* textMenu = popup->addMenu(tr("Add"));
136 // borrow translation info from global actions
137 // but create new actions with local handler
138 textMenu->addAction(getAction("frame-text")->text())->setData("frame-text");
139 textMenu->addAction(getAction("picture")->text())->setData("picture");
140 }
141 else if (e->isVBox()) {
142 QMenu* textMenu = popup->addMenu(tr("Add"));
143 // borrow translation info from global actions
144 // but create new actions with local handler
145 textMenu->addAction(getAction("frame-text")->text())->setData("frame-text");
146 textMenu->addAction(getAction("title-text")->text())->setData("title-text");
147 textMenu->addAction(getAction("subtitle-text")->text())->setData("subtitle-text");
148 textMenu->addAction(getAction("composer-text")->text())->setData("composer-text");
149 textMenu->addAction(getAction("poet-text")->text())->setData("poet-text");
150 textMenu->addAction(getAction("part-text")->text())->setData("part-text");
151 textMenu->addAction(getAction("insert-hbox")->text())->setData("insert-hbox");
152 textMenu->addAction(getAction("picture")->text())->setData("picture");
153 }
154 else if (e->isVoltaSegment())
155 genPropertyMenu1(e, popup);
156 else if (e->isTimeSig()) {
157 genPropertyMenu1(e, popup);
158 TimeSig* ts = toTimeSig(e);
159 // if the time sig. is not generated (= not courtesy) add the specific menu item
160 QAction* a;
161 if (!ts->generated() && ts->measure() != score()->firstMeasure()) {
162 a = popup->addAction(ts->showCourtesySig()
163 ? tr("Hide Courtesy Time Signature")
164 : tr("Show Courtesy Time Signature") );
165 a->setData("ts-courtesy");
166 }
167 if (!ts->generated()) {
168 popup->addSeparator();
169 popup->addAction(tr("Time Signature Properties…"))->setData("ts-props");
170 }
171 }
172 else if (e->isClef()) {
173 genPropertyMenu1(e, popup);
174 Clef* clef = toClef(e);
175 if (clef->measure() != score()->firstMeasure()) {
176 QAction* a = popup->addAction(toClef(e)->showCourtesy()
177 ? tr("Hide Courtesy Clef")
178 : tr("Show Courtesy Clef") );
179 a->setData("clef-courtesy");
180 }
181 }
182 else if (e->isStaffText()) {
183 genPropertyMenuText(e, popup);
184 popup->addAction(tr("Staff Text Properties…"))->setData("st-props");
185 }
186 else if (e->isSystemText()) {
187 genPropertyMenuText(e, popup);
188 popup->addAction(tr("System Text Properties…"))->setData("st-props");
189 }
190 else if (e->isText()
191 || e->isSystemText()
192 || e->isRehearsalMark()
193 || e->isMarker()
194 || e->isJump()
195 || e->isLyrics()
196 || e->isFiguredBass()) {
197 genPropertyMenuText(e, popup);
198 }
199 else if (e->isHarmony()) {
200 genPropertyMenu1(e, popup);
201 QAction* a = getAction("realize-chord-symbols");
202 if (a)
203 popup->addAction(a->text())->setData("realize-chord-symbols-dialog");
204 }
205 else if (e->isTempoText())
206 genPropertyMenu1(e, popup);
207 else if (e->isKeySig()) {
208 genPropertyMenu1(e, popup);
209 KeySig* ks = toKeySig(e);
210 if (!e->generated() && ks->measure() != score()->firstMeasure()) {
211 QAction* a = popup->addAction(ks->showCourtesy()
212 ? tr("Hide Courtesy Key Signature")
213 : tr("Show Courtesy Key Signature") );
214 a->setData("key-courtesy");
215 }
216 }
217 else if (e->isStaffState() && toStaffState(e)->staffStateType() == StaffStateType::INSTRUMENT) {
218 popup->addAction(tr("Change Instrument Properties…"))->setData("ss-props");
219 }
220 else if (e->isSlurSegment()) {
221 genPropertyMenu1(e, popup);
222 }
223 else if (e->isRest()) {
224 QAction* b = popup->actions()[0];
225 QAction* a = popup->insertSeparator(b);
226 a->setText(tr("Staff"));
227 a = new QAction(tr("Staff/Part Properties…"), 0);
228 a->setData("staff-props");
229 popup->insertAction(b, a);
230
231 a = popup->insertSeparator(b);
232 a->setText(tr("Measure"));
233 a = new QAction(tr("Measure Properties…"), 0);
234 a->setData("measure-props");
235 // disable property changes for multi measure rests
236 a->setEnabled(!toRest(e)->segment()->measure()->isMMRest());
237
238 popup->insertAction(b, a);
239 genPropertyMenu1(e, popup);
240 }
241 else if (e->isNote()) {
242 QAction* b = popup->actions()[0];
243 QAction* a = popup->insertSeparator(b);
244 a->setText(tr("Staff"));
245 a = new QAction(tr("Staff/Part Properties…"), 0);
246 a->setData("staff-props");
247 popup->insertAction(b, a);
248
249 a = popup->insertSeparator(b);
250 a->setText(tr("Measure"));
251 a = new QAction(tr("Measure Properties…"), 0);
252 a->setData("measure-props");
253 // disable property changes for multi measure rests
254 a->setEnabled(!toNote(e)->chord()->segment()->measure()->isMMRest());
255
256 popup->insertAction(b, a);
257
258 genPropertyMenu1(e, popup);
259
260 if (enableExperimental) {
261 popup->addSeparator();
262 popup->addAction(tr("Chord Articulation…"))->setData("articulation");
263 }
264 }
265 else if (e->isInstrumentChange()) {
266 genPropertyMenu1(e, popup);
267 popup->addAction(tr("Select Instrument…"))->setData("ch-instr");
268 }
269 else if (e->isInstrumentName())
270 popup->addAction(tr("Staff/Part Properties…"))->setData("staff-props");
271 else
272 genPropertyMenu1(e, popup);
273
274 if (EditStyle::elementHasPage(e)) {
275 popup->addSeparator();
276 popup->addAction(tr("Style…"))->setData("style");
277 }
278 }
279
280 //---------------------------------------------------------
281 // elementPropertyAction
282 //---------------------------------------------------------
283
elementPropertyAction(const QString & cmd,Element * e)284 void ScoreView::elementPropertyAction(const QString& cmd, Element* e)
285 {
286 if (cmd == "a-props") {
287 editArticulationProperties(toArticulation(e));
288 }
289 else if (cmd == "measure-props") {
290 Measure* m = 0;
291 if (e->type() == ElementType::NOTE)
292 m = toNote(e)->chord()->segment()->measure();
293 else if (e->type() == ElementType::REST)
294 m = toRest(e)->segment()->measure();
295 if (m) {
296 MeasureProperties vp(m);
297 vp.exec();
298 }
299 }
300 else if (cmd == "picture") {
301 mscore->addImage(score(), e);
302 }
303 else if (cmd == "frame-text") {
304 Text* t = new Text(score(), Tid::FRAME);
305 t->setParent(e);
306 score()->undoAddElement(t);
307 score()->select(t, SelectType::SINGLE, 0);
308 startEditMode(t);
309 }
310 else if (cmd == "title-text") {
311 Text* t = new Text(score(), Tid::TITLE);
312 t->setParent(e);
313 score()->undoAddElement(t);
314 score()->select(t, SelectType::SINGLE, 0);
315 startEditMode(t);
316 }
317 else if (cmd == "subtitle-text") {
318 Text* t = new Text(score(), Tid::SUBTITLE);
319 t->setParent(e);
320 score()->undoAddElement(t);
321 score()->select(t, SelectType::SINGLE, 0);
322 startEditMode(t);
323 }
324 else if (cmd == "composer-text") {
325 Text* t = new Text(score(), Tid::COMPOSER);
326 t->setParent(e);
327 score()->undoAddElement(t);
328 score()->select(t, SelectType::SINGLE, 0);
329 startEditMode(t);
330 }
331 else if (cmd == "poet-text") {
332 Text* t = new Text(score(), Tid::POET);
333 t->setParent(e);
334 score()->undoAddElement(t);
335 score()->select(t, SelectType::SINGLE, 0);
336 startEditMode(t);
337 }
338 else if (cmd == "part-text") {
339 Text* t = new Text(score(), Tid::INSTRUMENT_EXCERPT);
340 t->setParent(e);
341 score()->undoAddElement(t);
342 score()->select(t, SelectType::SINGLE, 0);
343 startEditMode(t);
344 }
345 else if (cmd == "insert-hbox") {
346 HBox* s = new HBox(score());
347 double w = e->width() - s->leftMargin() * DPMM - s->rightMargin() * DPMM;
348 s->setBoxWidth(Spatium(w / s->spatium()));
349 s->setParent(e);
350 score()->undoAddElement(s);
351 score()->select(s, SelectType::SINGLE, 0);
352 startEditMode(s);
353 }
354 if (cmd == "ts-courtesy") {
355 for (int stave = 0; stave < score()->nstaves(); stave++) {
356 TimeSig* ts = toTimeSig(toSegment(e->parent())->element(stave*VOICES));
357 if (ts)
358 ts->undoChangeProperty(Pid::SHOW_COURTESY, !ts->showCourtesySig());
359 }
360 }
361 else if (cmd == "ts-props") {
362 editTimeSigProperties(toTimeSig(e));
363 }
364 else if (cmd == "smallNote")
365 e->undoChangeProperty(Pid::SMALL, !toNote(e)->small());
366 else if (cmd == "clef-courtesy") {
367 Clef* clef = toClef(e);
368 bool show = !clef->showCourtesy();
369 clef->undoChangeProperty(Pid::SHOW_COURTESY, show);
370 Clef* otherClef = clef->otherClef();
371 if (otherClef)
372 otherClef->undoChangeProperty(Pid::SHOW_COURTESY, show);
373 }
374 else if (cmd == "st-props") {
375 editStaffTextProperties(toStaffTextBase(e));
376 }
377 #if 0
378 else if (cmd == "text-style") {
379 Text* t = toText(e);
380 QString name = t->textStyle().name();
381 TextStyleDialog ts(0, score());
382 ts.setPage(name);
383 ts.exec();
384 }
385 else if (cmd == "text-props") {
386 Text* ot = toText(e);
387 Text* nText = toText(ot->clone());
388 TextProperties tp(nText);
389 int rv = tp.exec();
390 if (rv) {
391 qDebug("text-props %d %d", int(ot->textStyleType()), int(nText->textStyleType()));
392 // if (ot->textStyleType() != nText->textStyleType()) {
393 // nText->restyle(ot->textStyleType());
394 // ot->undoChangeProperty(Pid::TEXT_STYLE_TYPE, int(nText->textStyleType()));
395 // }
396 // if (ot->textStyle() != nText->textStyle())
397 // ot->undoChangeProperty(Pid::TEXT_STYLE, QVariant::fromValue<TextStyle>(nText->textStyle()));
398 if (ot->xmlText() != nText->xmlText())
399 ot->undoChangeProperty(Pid::TEXT, nText->xmlText());
400 }
401 delete nText;
402 }
403 #endif
404 else if (cmd == "key-courtesy") {
405 for (int stave = 0; stave < score()->nstaves(); stave++) {
406 KeySig* ks = toKeySig(toSegment(e->parent())->element(stave*VOICES));
407 score()->undo(new ChangeKeySig(ks, ks->keySigEvent(), !ks->showCourtesy() /*, ks->showNaturals()*/));
408 }
409 }
410 else if (cmd == "ss-props") {
411 StaffState* ss = toStaffState(e);
412 SelectInstrument si(ss->instrument(), 0);
413 if (si.exec()) {
414 const InstrumentTemplate* it = si.instrTemplate();
415 if (it) {
416 // TODO: undo/redo
417 ss->setInstrument(Instrument::fromTemplate(it));
418 ss->staff()->part()->setInstrument(ss->instrument(), ss->segment()->tick());
419 score()->masterScore()->rebuildMidiMapping();
420 seq->initInstruments();
421 score()->setLayoutAll();
422 }
423 else
424 qDebug("no template selected?");
425 }
426 }
427 else if (cmd == "articulation") {
428 Note* note = toNote(e);
429 mscore->editInPianoroll(note->staff());
430 }
431 else if (cmd == "style")
432 mscore->showStyleDialog(e);
433 else if (cmd == "ch-instr")
434 selectInstrument(toInstrumentChange(e));
435 else if (cmd == "staff-props") {
436 Fraction tick = {-1,1};
437 if (e->isChordRest()) {
438 tick = toChordRest(e)->tick();
439 }
440 else if (e->isNote()) {
441 tick = toNote(e)->chord()->tick();
442 }
443 else if (e->isMeasure()) {
444 tick = toMeasure(e)->tick();
445 }
446 else if (e->isInstrumentName()) {
447 System* system = toSystem(toInstrumentName(e)->parent());
448 Measure* m = system ? system->firstMeasure() : nullptr;
449 if (m)
450 tick = m->tick();
451 }
452 score()->startCmd();
453 EditStaff editStaff(e->staff(), tick, 0);
454 connect(&editStaff, SIGNAL(instrumentChanged()), mscore, SLOT(instrumentChanged()));
455 editStaff.exec();
456 if (score()->undoStack()->active())
457 score()->endCmd();
458 }
459 else if (cmd.startsWith("layer-")) {
460 int n = cmd.mid(6).toInt();
461 uint mask = 1 << n;
462 e->setTag(mask);
463 }
464 }
465
466 //---------------------------------------------------------
467 // editArticulationProperties
468 //---------------------------------------------------------
469
editArticulationProperties(Articulation * ar)470 void ScoreView::editArticulationProperties(Articulation* ar)
471 {
472 ArticulationProperties rp(ar);
473 rp.exec();
474 }
475
476 //---------------------------------------------------------
477 // editTimeSigProperties
478 //---------------------------------------------------------
479
editTimeSigProperties(TimeSig * ts)480 void ScoreView::editTimeSigProperties(TimeSig* ts)
481 {
482 TimeSig* r = new TimeSig(*ts);
483 TimeSigProperties tsp(r);
484
485 if (tsp.exec()) {
486 ts->undoChangeProperty(Pid::TIMESIG_TYPE, int(r->timeSigType()));
487 ts->undoChangeProperty(Pid::SHOW_COURTESY, r->showCourtesySig());
488 ts->undoChangeProperty(Pid::NUMERATOR_STRING, r->numeratorString());
489 ts->undoChangeProperty(Pid::DENOMINATOR_STRING, r->denominatorString());
490 ts->undoChangeProperty(Pid::GROUPS, QVariant::fromValue<Groups>(r->groups()));
491
492 if (r->sig() != ts->sig()) {
493 score()->cmdAddTimeSig(ts->measure(), ts->staffIdx(), r, true);
494 r = 0;
495 }
496 }
497 delete r;
498 }
499
500 //---------------------------------------------------------
501 // selectInstrument
502 //---------------------------------------------------------
503
selectInstrument(InstrumentChange * ic)504 void Ms::ScoreView::selectInstrument(InstrumentChange* ic)
505 {
506 SelectInstrument si(ic->instrument(), 0);
507 if (si.exec()) {
508 const InstrumentTemplate* it = si.instrTemplate();
509 if (it) {
510 Instrument instr = Instrument::fromTemplate(it);
511 ic->setInit(true);
512 ic->setupInstrument(&instr);
513 }
514 else
515 qDebug("no template selected?");
516 }
517 }
518
519 //---------------------------------------------------------
520 // editStaffTextProperties
521 //---------------------------------------------------------
522
editStaffTextProperties(StaffTextBase * st)523 void ScoreView::editStaffTextProperties(StaffTextBase* st)
524 {
525 StaffTextProperties rp(st);
526 if (rp.exec()) {
527 Score* score = st->score();
528 StaffTextBase* nt = toStaffTextBase(rp.staffTextBase()->clone());
529 nt->setScore(score);
530 score->undoChangeElement(st, nt);
531 score->masterScore()->updateChannel();
532 score->updateCapo();
533 score->updateSwing();
534 score->setPlaylistDirty();
535 }
536 }
537
538 }
539