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