smoosic 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -347,6 +347,10 @@ export class SmoTuplet {
347
347
  static deserialize(jsonObj: SmoTupletParamsSer): SmoTuplet {
348
348
  const tupJson = SmoTuplet.defaults;
349
349
  smoSerialize.serializedMerge(SmoTuplet.parameterArray, jsonObj, tupJson);
350
+ // Make the tuplet count === default if it is 0
351
+ if (tupJson.totalTicks === 0) {
352
+ tupJson.totalTicks = SmoTuplet.defaults.totalTicks;
353
+ }
350
354
  // Legacy schema did not have notesOccupied, we need to calculate it.
351
355
  if ((jsonObj as any).notes !== undefined) {
352
356
  //todo: notesOccupied can probably be removed
@@ -11,7 +11,7 @@ import { SvgHelpers } from '../../render/sui/svgHelpers';
11
11
  import { SmoScore } from '../data/score';
12
12
  import { TickMap } from './tickMap';
13
13
  import { SmoSystemStaff } from '../data/systemStaff';
14
- import { getId } from '../data/common';
14
+ import { getId, Clef, Pitch } from '../data/common';
15
15
  import {SmoUnmakeTupletActor} from "./tickDuration";
16
16
  import { SmoTempoText, TimeSignature } from '../data/measureModifiers';
17
17
 
@@ -42,7 +42,8 @@ export interface ModifierPlacement {
42
42
  * @category SmoTransform
43
43
  */
44
44
  export class PasteBuffer {
45
- notes: PasteNote[];
45
+ notes: Record<number, PasteNote[]>;
46
+ staffIndex: number;
46
47
  noteIndex: number;
47
48
  measures: SmoMeasure[];
48
49
  measureIndex: number;
@@ -54,8 +55,9 @@ export class PasteBuffer {
54
55
  destination: SmoSelector = SmoSelector.default;
55
56
  staffSelectors: SmoSelector[] = [];
56
57
  constructor() {
57
- this.notes = [];
58
+ this.notes = {};
58
59
  this.noteIndex = 0;
60
+ this.staffIndex = 0;
59
61
  this.measures = [];
60
62
  this.measureIndex = -1;
61
63
  this.remainder = 0;
@@ -66,11 +68,18 @@ export class PasteBuffer {
66
68
  this.score = score;
67
69
  }
68
70
  getCopyBufferTickCount() {
69
- let rv = 0;
70
- this.notes.forEach((note) => {
71
- rv += note.note.tickCount;
71
+ let maxRv = 0;
72
+ const staves = Object.keys(this.notes);
73
+ staves.forEach((staffStr: string) => {
74
+ const staff: number = parseInt(staffStr);
75
+ const notes = this.notes[staff];
76
+ let rv = 0;
77
+ notes.forEach((note) => {
78
+ rv += note.note.tickCount;
79
+ });
80
+ maxRv = Math.max(rv, maxRv);
72
81
  });
73
- return rv;
82
+ return maxRv;
74
83
  }
75
84
  setSelections(score: SmoScore, selections: SmoSelection[]) {
76
85
  this.notes = [];
@@ -108,6 +117,7 @@ export class PasteBuffer {
108
117
  let minSelector = selections[0].selector;
109
118
  selections.forEach((selection) => {
110
119
  selector = JSON.parse(JSON.stringify(selection.selector));
120
+ const staff = selector.staff;
111
121
  if (SmoSelector.gt(selector, maxSelector)) {
112
122
  maxSelector = selector;
113
123
  }
@@ -151,17 +161,24 @@ export class PasteBuffer {
151
161
  pasteNote.tupletStart = SmoTupletTree.clone(tupletTree);
152
162
  }
153
163
  }
154
-
155
- this.notes.push(pasteNote);
164
+ if (!this.notes[staff]) {
165
+ this.notes[staff] = [];
166
+ }
167
+ this.notes[staff].push(pasteNote);
156
168
  }
157
169
  });
158
- this.notes.sort((a, b) =>
159
- SmoSelector.gt(a.selector, b.selector) ? 1 : -1
160
- );
170
+ const staves = Object.keys(this.notes);
171
+ staves.forEach((staffStr) => {
172
+ const staff = parseInt(staffStr);
173
+ const notes = this.notes[staff];
174
+ notes.sort((a, b) =>
175
+ SmoSelector.gt(a.selector, b.selector) ? 1 : -1
176
+ );
177
+ });
161
178
  }
162
179
 
163
180
  clearSelections() {
164
- this.notes = [];
181
+ this.notes = {};
165
182
  }
166
183
 
167
184
  _findModifier(selector: SmoSelector) {
@@ -181,7 +198,7 @@ export class PasteBuffer {
181
198
 
182
199
  // Before pasting, populate an array of existing measures from the paste destination
183
200
  // so we know how to place the notes.
184
- _populateMeasureArray(selector: SmoSelector) {
201
+ _populateMeasureArray(selector: SmoSelector, notes: PasteNote[]) {
185
202
  let measureSelection = SmoSelection.measureSelection(this.score!, selector.staff, selector.measure);
186
203
  if (!measureSelection) {
187
204
  return;
@@ -204,16 +221,21 @@ export class PasteBuffer {
204
221
 
205
222
  let currentDuration = tickmapForFirstMeasure.durationMap[selector.tick];
206
223
  const measureTotalDuration = tickmapForFirstMeasure.totalDuration;
207
- for (let i: number = 0; i < this.notes.length; i++) {
208
- const selection: PasteNote = this.notes[i];
224
+
225
+ for (let i: number = 0; i < notes.length; i++) {
226
+ const selection: PasteNote = notes[i];
209
227
  if (selection.tupletStart) {
210
228
  // const tupletTree: SmoTupletTree | null = SmoTupletTree.getTupletTreeForNoteIndex(this.tupletNoteMap, selection.selector.voice, selection.selector.tick);
211
- if (currentDuration + selection.tupletStart.totalTicks > measureTotalDuration && measureSelection !== null) {
229
+ if (currentDuration + selection.tupletStart.totalTicks > measureTotalDuration &&
230
+ currentDuration + selection.note.tickCount < measureTotalDuration &&
231
+ measureSelection !== null) {
212
232
  //if tuplet does not fit in a measure as a whole we cannot paste it, it is ether the whole thing or nothing
213
233
  //reset everything that has been changed so far and return
214
- this.measures = [];
215
- this.staffSelectors = [];
216
- return;
234
+ if (measureSelection === null) {
235
+ this.measures = [];
236
+ this.staffSelectors = [];
237
+ return;
238
+ }
217
239
  }
218
240
  }
219
241
  if (currentDuration + selection.note.tickCount > measureTotalDuration && measureSelection !== null) {
@@ -222,7 +244,8 @@ export class PasteBuffer {
222
244
  const remainder = (currentDuration + selection.note.tickCount) - measureTotalDuration;
223
245
  currentDuration = remainder;
224
246
 
225
- measureSelection = SmoSelection.measureSelection(this.score as SmoScore, measureSelection.selector.staff,measureSelection.selector.measure + 1);
247
+ measureSelection = SmoSelection.measureSelection(this.score as SmoScore,
248
+ measureSelection.selector.staff,measureSelection.selector.measure + 1);
226
249
 
227
250
  // If the paste buffer overlaps the end of the score, we can't paste (TODO: add a measure in this case)
228
251
  if (measureSelection != null) {
@@ -235,7 +258,6 @@ export class PasteBuffer {
235
258
  currentDuration += selection.note.tickCount;
236
259
  }
237
260
  }
238
-
239
261
  const lastMeasure = this.measures[this.measures.length - 1];
240
262
 
241
263
  //adjust the beginning of the paste
@@ -248,10 +270,13 @@ export class PasteBuffer {
248
270
  }
249
271
 
250
272
  if (this.measures.length > 1) {
251
- this._removeOverlappingTuplets(firstMeasure, selector.tick, firstMeasure.voices[selector.voice].notes.length - 1, selector.voice);
252
- this._removeOverlappingTuplets(lastMeasure, 0, lastMeasure.getClosestIndexFromTickCount(selector.voice, currentDuration), selector.voice);
273
+ this._removeOverlappingTuplets(firstMeasure,
274
+ selector.tick, firstMeasure.voices[selector.voice].notes.length - 1, selector.voice);
275
+ this._removeOverlappingTuplets(lastMeasure,
276
+ 0, lastMeasure.getClosestIndexFromTickCount(selector.voice, currentDuration), selector.voice);
253
277
  } else {
254
- this._removeOverlappingTuplets(firstMeasure, selector.tick, lastMeasure.getClosestIndexFromTickCount(selector.voice, currentDuration), selector.voice);
278
+ this._removeOverlappingTuplets(firstMeasure,
279
+ selector.tick, lastMeasure.getClosestIndexFromTickCount(selector.voice, currentDuration), selector.voice);
255
280
  }
256
281
 
257
282
  //if there are more than 2 measures remove tuplets from all but first and last measure.
@@ -284,7 +309,7 @@ export class PasteBuffer {
284
309
  // ### _populateVoice
285
310
  // ### Description:
286
311
  // Create a new voice for a new measure in the paste destination
287
- _populateVoice(): SmoVoice[] {
312
+ _populateVoice(notes: PasteNote[]): SmoVoice[] {
288
313
  // this._populateMeasureArray();
289
314
  const measures = this.measures;
290
315
  let measure = measures[0];
@@ -301,8 +326,8 @@ export class PasteBuffer {
301
326
  measure.voices.push(nvoice);
302
327
  }
303
328
  tickmap = measure.tickmapForVoice(this.destination.voice);
304
- this._populateNew(voice, measure, tickmap, startSelector);
305
- if (this.noteIndex < this.notes.length && this.measureIndex < measures.length) {
329
+ this._populateNew(voice, measure, tickmap, startSelector, notes);
330
+ if (this.noteIndex < notes.length && this.measureIndex < measures.length) {
306
331
  voice = {
307
332
  notes: []
308
333
  };
@@ -399,16 +424,16 @@ export class PasteBuffer {
399
424
  * @param startSelector
400
425
  * @returns
401
426
  */
402
- _populateNew(voice: SmoVoice, measure: SmoMeasure, tickmap: TickMap, startSelector: SmoSelector) {
427
+ _populateNew(voice: SmoVoice, measure: SmoMeasure, tickmap: TickMap, startSelector: SmoSelector, notes: PasteNote[]) {
403
428
  let currentDuration = tickmap.durationMap[startSelector.tick];
404
429
  let i = 0;
405
430
  let j = 0;
406
431
  const totalDuration = tickmap.totalDuration;
407
- while (currentDuration < totalDuration && this.noteIndex < this.notes.length) {
432
+ while (currentDuration < totalDuration && this.noteIndex < notes.length) {
408
433
  if (!this.score) {
409
434
  return;
410
435
  }
411
- const selection: PasteNote = this.notes[this.noteIndex];
436
+ const selection: PasteNote = notes[this.noteIndex];
412
437
  const note: SmoNote = selection.note;
413
438
  if (note.noteType === 'n') {
414
439
  const pitchAr: number[] = [];
@@ -416,6 +441,13 @@ export class PasteBuffer {
416
441
  pitchAr.push(ix);
417
442
  });
418
443
  SmoNote.transpose(note, pitchAr, measure.transposeIndex, selection.originalKey, measure.keySignature);
444
+ } else {
445
+ const npitches: Pitch[] = [];
446
+ note.pitches.forEach((pp) => {
447
+ const npitch = SmoMeasure.defaultPitchForClef[measure.clef as Clef];
448
+ npitches.push(npitch);
449
+ note.pitches = npitches;
450
+ });
419
451
  }
420
452
  this._populateModifier(selection.selector, startSelector, this.score.staves[selection.selector.staff]);
421
453
 
@@ -499,7 +531,8 @@ export class PasteBuffer {
499
531
  diffToAdjustRemainingTuplets += lmap.length;
500
532
  existingIndex++;
501
533
  }
502
- SmoTupletTree.adjustTupletIndexes(measure.tupletTrees, voiceIndex, startIndexToAdjustRemainingTuplets, diffToAdjustRemainingTuplets);
534
+ SmoTupletTree.adjustTupletIndexes(
535
+ measure.tupletTrees, voiceIndex, startIndexToAdjustRemainingTuplets, diffToAdjustRemainingTuplets);
503
536
 
504
537
  for (let i = existingIndex + 1; i < measure.voices[voiceIndex].notes.length; i++) {
505
538
  voice.notes.push(SmoNote.clone(measure.voices[voiceIndex].notes[i]));
@@ -523,19 +556,19 @@ export class PasteBuffer {
523
556
  }
524
557
  serializedMeasure.voices = voices;
525
558
  }
526
- pasteChords(selector: SmoSelector) {
527
- if (this.notes.length < 1) {
528
- return;
559
+ pasteChordNotes(selector: SmoSelector, notes: PasteNote[]): PasteNote[] {
560
+ if (notes.length < 1) {
561
+ return [];
529
562
  }
530
563
  if (!this.score) {
531
- return;
564
+ return [];
532
565
  }
533
566
  let srcTick = 0;
534
567
  let destTick = 0;
535
568
  let srcIndex = 0;
536
569
  let selection = SmoSelection.noteSelection(this.score!, selector.staff, selector.measure, selector.voice, selector.tick);
537
- while (selection && selection.note && srcIndex < this.notes.length) {
538
- const srcNote = this.notes[srcIndex].note;
570
+ while (selection && selection.note && srcIndex < notes.length) {
571
+ const srcNote = notes[srcIndex].note;
539
572
  const chords = srcNote.getChords();
540
573
  if (selection && selection.note) {
541
574
  const destNote = selection.note;
@@ -546,7 +579,7 @@ export class PasteBuffer {
546
579
  chords.forEach((chord) => {
547
580
  if (selection) {
548
581
  const nchord = SmoLyric.transposeChordToKey(
549
- chord, selection.measure.transposeIndex,this.notes[srcIndex].originalKey, selection.measure.keySignature);
582
+ chord, selection.measure.transposeIndex,notes[srcIndex].originalKey, selection.measure.keySignature);
550
583
  destNote.addLyric(nchord);
551
584
  }
552
585
  });
@@ -563,19 +596,80 @@ export class PasteBuffer {
563
596
  srcIndex += 1;
564
597
  }
565
598
  }
599
+ return notes;
566
600
  }
567
- pasteSelections(selector: SmoSelector) {
568
- let i = 0;
569
- if (this.notes.length < 1) {
601
+ pasteIntoScore(score: SmoScore, selector: SmoSelector, func:(selector: SmoSelector, notes: PasteNote[]) => PasteNote[]) {
602
+ let lowest: SmoSelector = SmoSelector.default;
603
+ const enforceRect = true;
604
+ let pasted = 0;
605
+ let startMeasure = 0;
606
+ let startTick = 0;
607
+ let startStaff = 0;
608
+ for (let j = 0; j < score?.staves.length; ++j ) {
609
+ if (!this.notes[j]) {
610
+ continue;
611
+ }
612
+ const notes = this.notes[j];
613
+ if (pasted === 0) {
614
+ lowest.measure = selector.measure;
615
+ lowest.tick = selector.tick;
616
+ lowest.staff = selector.staff;
617
+ startMeasure = notes[0].selector.measure;
618
+ startTick = notes[0].selector.tick;
619
+ startStaff = notes[0].selector.staff;
620
+ } else {
621
+ if (enforceRect) {
622
+ if (notes[0].selector.measure !== startMeasure || notes[0].selector.tick !== startTick) {
623
+ // can only paste rectangles. Maybe warn?
624
+ return;
625
+ }
626
+ if (notes[0].selector.staff !== startStaff + 1) {
627
+ // can only paste rectangles, not clear where next selection goes
628
+ return;
629
+ }
630
+ }
631
+ if (score.staves.length <= lowest.staff + 1) {
632
+ // nowhere to paste this.
633
+ return;
634
+ }
635
+ lowest.staff += 1;
636
+ startStaff += 1;
637
+ }
638
+ pasted += 1;
639
+ // Replace with fresh copy of the pasted notes
640
+ this.notes[j] = func(lowest, notes);
641
+ }
642
+ }
643
+ pasteChords(selector: SmoSelector) {
644
+ if (!this.score) {
570
645
  return;
571
646
  }
647
+ const func = (selector: SmoSelector, notes: PasteNote[]) => {
648
+ return this.pasteChordNotes(selector, notes);
649
+ }
650
+ this.pasteIntoScore(this.score, selector, func);
651
+ }
652
+ pasteSelections(selector: SmoSelector) {
572
653
  if (!this.score) {
573
654
  return;
574
655
  }
575
- const maxCutVoice = this.notes.map((n) => n.selector.voice).reduce((a, b) => a > b ? a : b);
576
- const minCutVoice = this.notes.map((n) => n.selector.voice).reduce((a, b) => a > b ? a : b);
656
+ const func = (selector: SmoSelector, notes: PasteNote[]) => {
657
+ return this.pasteNoteSelections(selector, notes);
658
+ }
659
+ this.pasteIntoScore(this.score, selector, func);
660
+ }
661
+ pasteNoteSelections(selector: SmoSelector, notes: PasteNote[]): PasteNote[] {
662
+ let i = 0;
663
+ if (notes.length < 1) {
664
+ return notes;
665
+ }
666
+ if (!this.score) {
667
+ return notes;
668
+ }
669
+ const maxCutVoice = notes.map((n) => n.selector.voice).reduce((a, b) => a > b ? a : b);
670
+ const minCutVoice = notes.map((n) => n.selector.voice).reduce((a, b) => a > b ? a : b);
577
671
  const backupNotes: PasteNote[] = [];
578
- this.notes.forEach((bb) => {
672
+ notes.forEach((bb) => {
579
673
  const note = (SmoNote.deserialize(bb.note.serialize()));
580
674
  const selector = JSON.parse(JSON.stringify(bb.selector));
581
675
  let tupletStart = bb.tupletStart;
@@ -591,12 +685,12 @@ export class PasteBuffer {
591
685
  this.noteIndex = 0;
592
686
  this.measureIndex = -1;
593
687
  this.remainder = 0;
594
- this._populateMeasureArray(selector);
688
+ this._populateMeasureArray(selector, notes);
595
689
  if (this.measures.length === 0) {
596
- return;
690
+ return notes;
597
691
  }
598
692
 
599
- const voices = this._populateVoice();
693
+ const voices = this._populateVoice(notes);
600
694
  const measureSel = JSON.parse(JSON.stringify(this.destination));
601
695
  const selectors: SmoSelector[] = [];
602
696
  for (i = 0; i < this.measures.length && i < voices.length; ++i) {
@@ -659,6 +753,6 @@ export class PasteBuffer {
659
753
  selection.staff.addStaffModifier(mod.modifier);
660
754
  }
661
755
  });
662
- this.notes = backupNotes;
756
+ return(backupNotes);
663
757
  }
664
758
  }
@@ -307,6 +307,10 @@ export class SmoStretchNoteActor extends TickIteratorBase {
307
307
 
308
308
  static apply(params: SmoStretchNoteParams) {
309
309
  const actor = new SmoStretchNoteActor(params);
310
+ // The duration would extend over the barline.
311
+ if (actor.notesToInsert.length < 1) {
312
+ return;
313
+ }
310
314
  SmoTickIterator.iterateOverTicks(actor.measure, actor, actor.voice);
311
315
  }
312
316
  iterateOverTick(note: SmoNote, tickmap: TickMap, index: number) {
@@ -20,6 +20,7 @@ export class TickMap {
20
20
  keySignature: string;
21
21
  voice: number;
22
22
  notes: SmoNote[] = [];
23
+ priorAccidentals: TickAccidental[] = [];
23
24
  index: number = 0;
24
25
  startIndex: number = 0;
25
26
  endIndex: number = 0;
@@ -107,34 +108,6 @@ export class TickMap {
107
108
  this.durationAccidentalMap[this.durationMap[this.index]] = newObj;
108
109
  }
109
110
 
110
- // ### getActiveAccidental
111
- // return the active accidental for the given note
112
- getActiveAccidental(pitch: Pitch, iteratorIndex: number, keySignature: string) {
113
- let defaultAccidental: string = SmoMusic.getKeySignatureKey(pitch.letter, keySignature);
114
- let i = 0;
115
- let j = 0;
116
- defaultAccidental = defaultAccidental.length > 1 ? defaultAccidental[1] : 'n';
117
- if (iteratorIndex === 0) {
118
- return defaultAccidental;
119
- }
120
- // Back up the accidental map until we have a match, or until we run out
121
- for (i = iteratorIndex; i > 0; --i) {
122
- const map: Record<string, TickAccidental> = this.accidentalMap[i - 1];
123
- const mapKeys = Object.keys(map);
124
- for (j = 0; j < mapKeys.length; ++j) {
125
- const mapKey: string = mapKeys[j];
126
- // The letter name + accidental in the map
127
- const mapPitch: Pitch = map[mapKey].pitch;
128
- const mapAcc = mapPitch.accidental ? mapPitch.accidental : 'n';
129
-
130
- // if the letters match and the accidental...
131
- if (mapPitch.letter.toLowerCase() === pitch.letter) {
132
- return mapAcc;
133
- }
134
- }
135
- }
136
- return defaultAccidental;
137
- }
138
111
  get duration() {
139
112
  return this.totalDuration;
140
113
  }
@@ -426,7 +426,7 @@ export class UndoBuffer {
426
426
  } else {
427
427
  if (typeof(staffMap[buf.selector.staff]) === 'number') {
428
428
  buf.selector.staff = staffMap[buf.selector.staff];
429
- const staff = SmoSystemStaff.deserialize(buf.json);
429
+ const staff = SmoSystemStaff.deserialize(buf.json, !score.preferences.transposingScore);
430
430
  score.replaceStaff(buf.selector.staff, staff);
431
431
  }
432
432
  }
Binary file
Binary file
@@ -12,19 +12,25 @@ import { PromiseHelpers } from '../../common/promiseHelpers';
12
12
  export class SuiDynamicDialogAdapter extends SuiComponentAdapter {
13
13
  modifier: SmoDynamicText;
14
14
  backup: SmoDynamicText;
15
- selection: SmoSelection;
15
+ selections: SmoSelection[] = [];
16
+ changed: boolean = false;
16
17
  constructor(view: SuiScoreViewOperations, modifier: SmoDynamicText) {
17
18
  super(view);
18
19
  this.modifier = modifier;
19
20
  this.backup = new SmoDynamicText(this.modifier);
21
+ this.view.undoTrackerMeasureSelections('Dynamics');
20
22
  if (this.view.tracker.modifierSelections.length) {
21
- this.selection = this.view.tracker.modifierSelections[0].selection!;
23
+ this.view.tracker.modifierSelections.forEach((ms) => {
24
+ if (ms.selection) {
25
+ this.selections.push(ms.selection);
26
+ }
27
+ });
22
28
  } else {
23
- this.selection = this.view.tracker.selections[0];
29
+ this.selections = this.view.tracker.selections;
24
30
  }
25
31
  }
26
32
  async cancel() {
27
- await this.view.addDynamic(this.selection, this.backup);
33
+ this.view.undo();
28
34
  }
29
35
  async commit() {
30
36
  return PromiseHelpers.emptyPromise();
@@ -33,39 +39,48 @@ export class SuiDynamicDialogAdapter extends SuiComponentAdapter {
33
39
  return this.modifier.xOffset;
34
40
  }
35
41
  async remove() {
36
- await this.view.removeDynamic(this.modifier);
42
+ for (let i = 0;i < this.selections.length; ++i) {
43
+ await this.view.removeDynamic(this.modifier);
44
+ }
45
+ }
46
+ syncModifiers() {
47
+ for (let i = 0;i < this.selections.length; ++i) {
48
+ this.view.addDynamic(this.selections[i], this.modifier);
49
+ }
37
50
  }
38
51
  set xOffset(value: number) {
39
52
  this.modifier.xOffset = value;
40
- this.view.addDynamic(this.selection, this.modifier);
53
+ for (let i = 0;i < this.selections.length; ++i) {
54
+ this.view.addDynamic(this.selections[i], this.modifier);
55
+ }
41
56
  }
42
57
  get fontSize() {
43
58
  return this.modifier.fontSize;
44
59
  }
45
60
  set fontSize(value: number) {
46
61
  this.modifier.fontSize = value;
47
- this.view.addDynamic(this.selection, this.modifier);
62
+ this.syncModifiers();
48
63
  }
49
64
  get yOffsetLine() {
50
65
  return this.modifier.yOffsetLine;
51
66
  }
52
67
  set yOffsetLine(value: number) {
53
68
  this.modifier.yOffsetLine = value;
54
- this.view.addDynamic(this.selection, this.modifier);
69
+ this.syncModifiers();
55
70
  }
56
71
  get yOffsetPixels() {
57
72
  return this.modifier.yOffsetPixels;
58
73
  }
59
74
  set yOffsetPixels(value: number) {
60
75
  this.modifier.yOffsetPixels = value;
61
- this.view.addDynamic(this.selection, this.modifier);
76
+ this.syncModifiers();
62
77
  }
63
78
  get text() {
64
79
  return this.modifier.text;
65
80
  }
66
81
  set text(value: string) {
67
82
  this.modifier.text = value;
68
- this.view.addDynamic(this.selection, this.modifier);
83
+ this.syncModifiers();
69
84
  }
70
85
  }
71
86
  /**
@@ -79,6 +79,13 @@ export class SuiScorePreferencesAdapter extends SuiComponentAdapter {
79
79
  this.preferences.transposingScore = value;
80
80
  this.view.updateScorePreferences(this.preferences);
81
81
  }
82
+ get showPartNames() {
83
+ return this.preferences.showPartNames;
84
+ }
85
+ set showPartNames(value: boolean) {
86
+ this.preferences.showPartNames = value;
87
+ this.view.updateScorePreferences(this.preferences);
88
+ }
82
89
  async cancel() {
83
90
  const p1 = JSON.stringify(this.preferences);
84
91
  const p2 = JSON.stringify(this.backup);
@@ -125,6 +132,10 @@ export class SuiScorePreferencesDialog extends SuiDialogAdapterBase<SuiScorePref
125
132
  smoName: 'hideEmptyLines',
126
133
  control: 'SuiToggleComponent',
127
134
  label: 'Hide Empty Lines'
135
+ }, {
136
+ smoName: 'showPartNames',
137
+ control: 'SuiToggleComponent',
138
+ label: 'Show Part Names in Score'
128
139
  }, {
129
140
  smoName: 'defaultDupleDuration',
130
141
  control: 'SuiDropdownComponent',
@@ -3,7 +3,6 @@
3
3
  import { buildDom, createTopDomContainer } from '../../common/htmlHelpers';
4
4
  import { SvgBox } from '../../smo/data/common';
5
5
  import { UndoBuffer } from '../../smo/xform/undo';
6
- import { SuiDynamicsMenu } from './dynamics';
7
6
  import { SuiBeamMenu, SuiBeamMenuOptions } from './beams';
8
7
  import { layoutDebug } from '../../render/sui/layoutDebug';
9
8
  import { SuiScoreViewOperations } from '../../render/sui/scoreViewOperations';
@@ -366,8 +365,7 @@ export class SuiMenuManager {
366
365
  });
367
366
  }
368
367
  }
369
- export const menuTranslationsInit = () => {
370
- MenuTranslations.push(suiMenuTranslation(SuiDynamicsMenu.defaults, 'SuiDynamicsMenu'));
368
+ export const menuTranslationsInit = () => {
371
369
  MenuTranslations.push(suiConfiguredMenuTranslate(SuiBeamMenuOptions, 'Beam', 'SuiBeamMenu'));
372
370
  }
373
371
 
@@ -119,16 +119,18 @@ const lyricsDialogMenuOption: SuiConfiguredMenuOption = {
119
119
  */
120
120
  const dynamicsDialogMenuOption: SuiConfiguredMenuOption = {
121
121
  handler: async (menu: SuiMenuBase) => {
122
- const sel = menu.view.tracker.selections[0];
122
+ const sel = menu.view.tracker.selections;
123
123
  let modifier = null;
124
- if (sel.note) {
125
- const dynamics = sel.note.getModifiers('SmoDynamicText');
124
+ if (sel[0].note) {
125
+ const dynamics = sel[0].note.getModifiers('SmoDynamicText');
126
126
  if (dynamics.length) {
127
127
  modifier = dynamics[0];
128
128
  } else {
129
129
  const params = SmoDynamicText.defaults;
130
130
  modifier = new SmoDynamicText(params);
131
- menu.view.addDynamic(sel, modifier);
131
+ for (let i = 0; i < sel.length; ++i) {
132
+ await menu.view.addDynamic(sel[i], modifier);
133
+ }
132
134
  }
133
135
  }
134
136
  createAndDisplayDialog(SuiDynamicModifierDialog, {
package/typedoc.ts CHANGED
@@ -141,7 +141,6 @@ export * from './src/ui/i18n/translationEditor';
141
141
  export * from './src/ui/keyBindings/default/editorKeys';
142
142
  export * from './src/ui/keyBindings/default/trackerKeys';
143
143
  export * from './src/ui/menus/beams';
144
- export * from './src/ui/menus/dynamics';
145
144
  export * from './src/ui/menus/file';
146
145
  export * from './src/ui/menus/keySignature';
147
146
  export * from './src/ui/menus/language';
Binary file
@@ -1,61 +0,0 @@
1
- import { SmoDynamicText } from '../../smo/data/noteModifiers';
2
- import { SuiMenuBase, SuiMenuParams, MenuDefinition } from './menu';
3
- declare var $: any;
4
- /**
5
- * @category SuiMenu
6
- */
7
- export class SuiDynamicsMenu extends SuiMenuBase {
8
- constructor(params: SuiMenuParams) {
9
- super(params);
10
- }
11
- static defaults: MenuDefinition = {
12
- label: 'Dynamics',
13
- menuItems: [{
14
- icon: 'pianissimo',
15
- text: 'Pianissimo',
16
- value: 'pp'
17
- }, {
18
- icon: 'piano',
19
- text: 'Piano',
20
- value: 'p'
21
- }, {
22
- icon: 'mezzopiano',
23
- text: 'Mezzo-piano',
24
- value: 'mp'
25
- }, {
26
- icon: 'mezzoforte',
27
- text: 'Mezzo-forte',
28
- value: 'mf'
29
- }, {
30
- icon: 'forte',
31
- text: 'Forte',
32
- value: 'f'
33
- }, {
34
- icon: 'fortissimo',
35
- text: 'Fortissimo',
36
- value: 'ff'
37
- }, {
38
- icon: 'sfz',
39
- text: 'sfortzando',
40
- value: 'sfz'
41
- }, {
42
- icon: '',
43
- text: 'Cancel',
44
- value: 'cancel'
45
- }]
46
- };
47
-
48
- getDefinition() {
49
- return SuiDynamicsMenu.defaults;
50
- }
51
-
52
- async selection(ev: any) {
53
- const text: string = $(ev.currentTarget).attr('data-value');
54
- const props = SmoDynamicText.defaults;
55
- props.text = text;
56
- const dynamic = new SmoDynamicText(props);
57
- await this.view.addDynamic(this.tracker.selections[0], dynamic);
58
- this.complete();
59
- }
60
- keydown() { }
61
- }