Compare commits

...

245 Commits

Author SHA1 Message Date
RomanPudashkin 2e3a93abe6
Merge pull request #18531 from RomanPudashkin/fix_updating_offset_property_410
fix_updating_offset_property_410
2023-07-11 16:58:04 +03:00
Roman Pudashkin 0423c039fd Init submodel of RestBeamSettingsModel 2023-07-11 16:08:15 +03:00
Casper Jeukendrup 7809bd6827 Init submodels of NoteSettingsProxyModel 2023-07-11 16:06:32 +03:00
Casper Jeukendrup d4b0566e18 Init submodels of RestSettingsProxyModel 2023-07-11 16:06:22 +03:00
Casper Jeukendrup 05b05c53a4 Init submodels of BarlineSettingsModel 2023-07-11 16:06:12 +03:00
Casper Jeukendrup 451698f2f2 Init submodels of PlaybackProxyModel 2023-07-11 16:05:58 +03:00
Casper Jeukendrup 4bcbfe67a7 Fix updating the Offset property in the Inspector when dragging item in the notation 2023-07-11 16:05:47 +03:00
RomanPudashkin 9b8469880e
Merge pull request #18527 from RomanPudashkin/vst_compat_fix_410
vst_compat_fix_410
2023-07-11 14:48:30 +03:00
RomanPudashkin 18898935f0
Merge pull request #18511 from mike-spa/port#18510
Port #18510
2023-07-11 14:45:14 +03:00
Elnur Ismailzada af43a827cd
Merge pull request #18528 from Eism/navigation_score_ornaments_fix_4.1
Navigation with Alt+Left/Right doesn't work properly with some new Ornaments. 4.1
2023-07-11 14:15:14 +03:00
Eism 8cee8c434a fixed #17814: Fixed ornaments navigation in score 2023-07-11 13:24:57 +03:00
Roman Pudashkin eb9ede0954 fix #18525: the Instrument category has the highest priority for compatibility reasons 2023-07-11 12:59:38 +03:00
RomanPudashkin eaa5398ddd
Merge pull request #18521 from cbjeukendrup/410_fix_superfluous_thin_bracket
[4.1.0] Don't add superfluous thin brackets for each instrument
2023-07-11 11:12:41 +03:00
Michele Spagnolo 4648c6fdf0 Correct scaling errors of chordlines 2023-07-11 10:00:56 +02:00
Casper Jeukendrup 6ffd47906c
Don't add superfluous thin brackets
Finalising a thin bracket should happen only when encountering a different instrument (i.e. the previous bracket will not grow any longer). Not for the first part of every staff.

This logic error already existed, but has surfaced only after ce01ab2243.
2023-07-11 02:53:56 +02:00
RomanPudashkin 3fcb045694
Merge pull request #18505 from mike-spa/port#18501
Port #18501
2023-07-10 18:16:57 +03:00
Michele Spagnolo bfb85e8d19 added vtest 2023-07-10 15:57:12 +02:00
Michele Spagnolo 029f900a74 fix utests 2023-07-10 14:54:28 +02:00
Michele Spagnolo bcae85ee83 Correct default offset for expression above and below staff
correction
2023-07-10 14:54:23 +02:00
Michele Spagnolo dae97c236a Correct for different dynamic and expression offset when snapping 2023-07-10 14:54:12 +02:00
RomanPudashkin d983c71886
Merge pull request #18502 from RomanPudashkin/correct-playback-transposing-instruments_410
correct-playback-transposing-instruments_410
2023-07-10 14:19:41 +03:00
RomanPudashkin 7fcb906c67
Merge pull request #18486 from cbjeukendrup/410_win_portable
[4.1.0] Create Windows Portable Nightly Builds and publish Portable builds to OSUOSL
2023-07-10 13:43:42 +03:00
sammik 3e43ade74f use tpc1 for playback, if possible 2023-07-10 13:32:27 +03:00
sammik b7d6b587d8 update tpcs if "flipped keysig" is added, or removed 2023-07-10 13:32:19 +03:00
Casper Jeukendrup 0c23c22c35
Really enable nightly builds for Windows Portable 2023-07-08 21:05:50 +02:00
Casper Jeukendrup a9348f4ac2
Create Portable nightly builds for Windows 2023-07-08 21:05:50 +02:00
Casper Jeukendrup e03c17b134
[ci] When publishing Windows build, publish Portable build too 2023-07-08 21:05:49 +02:00
RomanPudashkin eb4cd16e51
Merge pull request #18479 from cbjeukendrup/410_crash_linked_tab
[4.1.0] Fix crash when creating linked staff for TAB-only staff
2023-07-08 14:53:29 +03:00
Casper Jeukendrup a46041f569
Fix crash when creating linked staff for TAB-only staff 2023-07-08 11:06:59 +02:00
RomanPudashkin 5f333a2466
Merge pull request #18470 from RomanPudashkin/tremolo_crash_410
tremolo_crash_410
2023-07-07 20:21:29 +03:00
Roman Pudashkin 2c59d5db24 fix #18467 2023-07-07 19:27:51 +03:00
RomanPudashkin 0759bc9037
Merge pull request #18464 from mike-spa/port#18460
Port #18463: Fix cross beam force horizontal
2023-07-07 12:51:14 +03:00
Michele Spagnolo bc1b2d1f8e Fix cross beam force horizontal 2023-07-07 11:09:03 +02:00
RomanPudashkin aa456a35b2
Merge pull request #18459 from RomanPudashkin/prefersharpflat-import-lot_accidentals_410
prefersharpflat-import-lot_accidentals_410
2023-07-07 11:48:56 +03:00
sammik 294cf58d67 fix #18425 - part containing key with more than 6 accidentals has to be PreferSharpFlat::NONE 2023-07-07 09:24:04 +03:00
RomanPudashkin 193bce042a
Merge pull request #18444 from RomanPudashkin/crash_when_open_score_fix_410
crash_when_open_score_fix_410
2023-07-06 19:18:48 +03:00
RomanPudashkin e782848712
Merge pull request #18443 from asattely/aug-dot-bug-410
[4.1.0] Bugfix for aug dot deletion
2023-07-06 18:54:17 +03:00
Roman Pudashkin 6184db8142 fix #18442 2023-07-06 17:55:41 +03:00
RomanPudashkin 151e4cf294
Merge pull request #18435 from Eism/revert_to_factory_fix_4.1
fixed #18433: App doesn't auto restart properly after Revert to factory settings (Windows only). 4.1
2023-07-06 17:22:57 +03:00
asattely 8c11cb7421 small bugfix for aug dot deletion 2023-07-06 10:16:45 -04:00
Eism 9bd8886025 fixed #18433: Added default splash screen 2023-07-06 13:36:56 +03:00
RomanPudashkin a4f87747a9
Merge pull request #18432 from Eism/wasapi_device_null_check_4.1
update 4.1
2023-07-06 13:31:53 +03:00
Eism 321b629ed5 fixed #18427: Select first group when initializing model 2023-07-06 11:58:25 +03:00
Eism 0db45dd77a Checking devices for null 2023-07-06 11:55:44 +03:00
Casper Jeukendrup 690c09639d
Merge pull request #18420 from musescore/ci_run_lupdate
Update in-repo translation source files
2023-07-05 19:40:29 +02:00
cbjeukendrup a23cba5926 Run lupdate 2023-07-05 17:25:40 +00:00
RomanPudashkin f4cbb874a9
Merge pull request #18418 from RomanPudashkin/update_410
update_410
2023-07-05 19:54:50 +03:00
RomanPudashkin 22763af6ec
Merge pull request #18419 from asattely/dots-cleanup-410
[4.1.0] Cleanup of dot positioning code and manual direction bugfix
2023-07-05 19:18:36 +03:00
Eism 9e1219033b fixed #18412: Added nullptr check 2023-07-05 19:08:02 +03:00
asattely dffa31a96a Cleanup of dot positioning code and manual direction bugfix 2023-07-05 11:31:41 -04:00
Casper Jeukendrup ca2af7f5fe Don't show splash screen at all when launching a second instance manually 2023-07-05 18:15:23 +03:00
Casper Jeukendrup d18515dbb1 Only show "Loading new file" splash screen when really doing that 2023-07-05 18:15:15 +03:00
Casper Jeukendrup 95d0965617 Show the correct dialog when an error occurs while opening a file 2023-07-05 18:14:52 +03:00
I Woithe 40f190e3bc Fix musescore#12028: Fix messaging when opening non-existing scores 2023-07-05 18:14:43 +03:00
Eism e420fc8cdb fixed #18254: subscribe to a change the current notation at the level above 2023-07-05 18:14:43 +03:00
Roman Pudashkin 4b0d5dbb1b register vst3 files without any audio effect inside as failed plugins 2023-07-05 18:14:43 +03:00
Roman Pudashkin 67c6a5e5ac use io::completeBasename for generating resourceId as it was before PR #16990
(to maintain backward compatibility with old projects)
2023-07-05 18:14:43 +03:00
Roman Pudashkin 8eb364b127 make sure we can register two plugins with the same resourceId but with different location
(the same plugin is installed in two different places)
2023-07-05 18:14:43 +03:00
Roman Pudashkin 1314082159 use known_audio_plugins.json to store information about installed plugins
(instead of having a separate file for each plugin to avoid possible name conflicts, the user may have the same plugin installed in several different locations)
2023-07-05 18:14:43 +03:00
Roman Pudashkin 207c1dc79c optimization 2023-07-05 18:14:43 +03:00
Roman Pudashkin a60c7f05c6 write logs related to audio plugins registration to a separate file (to avoid creating a lot of log files) 2023-07-05 18:14:43 +03:00
RomanPudashkin 8b2ee61ef7
Merge pull request #18258 from HemantAntony/17949-aux_sends_too_close_4.1
[4.1] Fix #17949: Aux sends 1 and 2 are too close together
2023-07-05 18:01:31 +03:00
RomanPudashkin 54cb194808
Merge pull request #18414 from cbjeukendrup/410_block_too_new_files
[4.1.0] Completely block opening files from newer versions
2023-07-05 16:55:27 +03:00
RomanPudashkin c8031c28e7
Merge pull request #18402 from cbjeukendrup/410_crash_ctrlshiftdrag_harpdiagram
[4.1.0] Fix crash when Ctrl+Shift+Dragging Harp Pedal Diagram
2023-07-05 16:10:03 +03:00
Casper Jeukendrup 6f400f8050
Show filepath using native separators (backslash on Windows) 2023-07-05 14:38:29 +02:00
Casper Jeukendrup ee9fefd074
Completely block opening files from newer versions
Basically remove the "Open anyway" button
2023-07-05 14:38:28 +02:00
RomanPudashkin 0d75514334
Merge pull request #18408 from bakajikara/textline-properties-410
[4.1.0] Textline properties: reintroduce missing text edit field
2023-07-05 15:16:22 +03:00
RomanPudashkin 7b8f7824e2
Merge pull request #18396 from RomanPudashkin/capo_compat_410
capo_compat_410
2023-07-05 15:01:06 +03:00
Roman Pudashkin afbfe78eb7 don't show BrailleViewStub stub 2023-07-05 13:52:48 +03:00
Roman Pudashkin dd0a019e93 replace the old Capo (based on StaffText) with the new Capo item 2023-07-05 13:52:46 +03:00
RomanPudashkin 1a4eb551e6
Merge pull request #18411 from mike-spa/port#18407
Correct autoplace for expression when not snapping to dynamic
2023-07-05 13:40:30 +03:00
bakajikara 38457a2e3d Fixed utest errors 2023-07-05 18:46:09 +09:00
Michele Spagnolo 87568fd528 Correct autoplace for expression when not snapping to dynamic 2023-07-05 11:45:34 +02:00
RomanPudashkin 59d706e50c
Merge pull request #18406 from mike-spa/portMoreStuff
Port more stuff
2023-07-05 12:21:37 +03:00
bakajikara 6c454debcc Add setPropertyFlags 2023-07-05 18:03:54 +09:00
bakajikara 7931f5068d Introduced styles for pedal line texts 2023-07-05 18:03:54 +09:00
bakajikara 2f4f9c2101 Fixed missing offset property default 2023-07-05 18:03:54 +09:00
bakajikara 9ee7db3874 Fixed hairpin text property default 2023-07-05 18:03:54 +09:00
bakajikara 5a0bb72598 Added text tab to pedal line properties (#14928) 2023-07-05 18:03:54 +09:00
bakajikara 54055bf289 fixed endText was invisible (#16368) 2023-07-05 18:03:54 +09:00
RomanPudashkin 4cffb3191d Merge pull request #18273 from mike-spa/fixCrashOnReadingSpecificFiles
Fix crash on reading specific files
2023-07-05 09:56:38 +02:00
RomanPudashkin 351e7bd7a7 Merge pull request #18184 from mike-spa/fixCustomDynamicsXmlExport
Fix xml export of dynamics with custom text
2023-07-05 09:54:00 +02:00
RomanPudashkin a672ddd58d
Merge pull request #18403 from HemantAntony/18315-harp_notation_palette_4.1
[4.1] Fix #18315: Change Harp palette
2023-07-05 10:33:32 +03:00
Hemant Antony 483803afa7 Fix #18315: Change Harp palette 2023-07-05 10:28:30 +05:30
Casper Jeukendrup e0840cdfd8
Fix crash when Ctrl+Shift+Dragging Harp Pedal Diagram 2023-07-05 01:59:37 +02:00
Roman Pudashkin d79cbfaf98 fix #18065: add Capo if it is not in the Guitar palette 2023-07-04 19:45:26 +03:00
RomanPudashkin 1654979c9b
Merge pull request #18394 from cbjeukendrup/410_crash_select_all_drag_certain_score
[4.1.0] Fix crash when selecting all and trying to drag in score with ottava line without segments
2023-07-04 19:30:28 +03:00
RomanPudashkin b485208874
Merge pull request #18393 from cbjeukendrup/410_export_pngsvg_fix_filename_pagenumber
[4.1.0] Fix exporting PNG or SVG files with multiple pages
2023-07-04 19:26:03 +03:00
Casper Jeukendrup 97b069523d
Fix crash when selecting all and trying to drag in score with ottava line without segments
Check if segments list is not empty before taking front. Segments list of ottava line can apparently be empty on TAB staves.
2023-07-04 17:43:34 +02:00
RomanPudashkin 9fb7ace5e7
Merge pull request #18391 from cbjeukendrup/410_insert_note_in_tuplet_error
[4.1.0] Add missing error message when trying to insert note during tuplet
2023-07-04 18:08:01 +03:00
Casper Jeukendrup 646d635010
Fix exporting PNG or SVG files with multiple pages if they are currently viewed in Continuous view 2023-07-04 17:07:31 +02:00
Casper Jeukendrup 523586d4a4
Fix exporting PNG or SVG files with multiple pages 2023-07-04 17:07:30 +02:00
RomanPudashkin 88bd571371
Merge pull request #18390 from cbjeukendrup/410_multisolo
[4.1.0] Fix solo for multiple tracks
2023-07-04 18:02:59 +03:00
RomanPudashkin d68bf6b7ea
Merge pull request #18388 from RomanPudashkin/disabled_aux_optimization_410
disabled_aux_optimization_410
2023-07-04 17:17:11 +03:00
Casper Jeukendrup 6c47335ee0
Add missing error message when trying to insert note during tuplet 2023-07-04 16:11:26 +02:00
RomanPudashkin ba98347dec
Merge pull request #18386 from mike-spa/portPRsFromMaster-2
Port PRs from master
2023-07-04 17:05:39 +03:00
Casper Jeukendrup 548a9f988c
Fix solo for multiple tracks
Regression introduced in 649f01534d
2023-07-04 15:56:57 +02:00
Roman Pudashkin b3133635ff fix #18332: don't start aux processing if the aux channels are not receiving an audio signal 2023-07-04 16:24:44 +03:00
RomanPudashkin eba898734c Merge pull request #18086 from mike-spa/fixRestVerticalOffsetWithInvisibleChords
Fix rest vertical offset with invisible chords
2023-07-04 14:56:46 +02:00
RomanPudashkin 70d0a7c98b Merge pull request #18282 from mike-spa/fixStyleMigration
Fix style migration
2023-07-04 14:55:56 +02:00
RomanPudashkin 73a4c8fda1 Merge pull request #18291 from mike-spa/fixCrashOnEnterTieCancelAndReEnter
Fix crash on enter tie, cancel, and re-enter
2023-07-04 14:55:24 +02:00
RomanPudashkin 2dafc60512 Merge pull request #18320 from mike-spa/fixEnterUnicodeSymbolsAfterSmuflSymbols
Fix enter Unicode symbols after Smufl symbols
2023-07-04 14:55:03 +02:00
Michele Spagnolo 2b36756e18 Merge pull request #18323 from mike-spa/fixStemSlashScaling 2023-07-04 14:53:55 +02:00
RomanPudashkin d52a31cf04 Merge pull request #18362 from mike-spa/disableSmallAccidentalForOrnaments
Disable "Small accidental" option for ornament accidental
2023-07-04 14:43:32 +02:00
Casper Jeukendrup 0e63b40017
Merge pull request #18189 from Jojo-Schmitz/4.1.0-issue#98
Crescendo lines overlap + Chord text frame
2023-07-04 14:17:13 +02:00
RomanPudashkin 053f4991e3
Merge pull request #18379 from Eism/new_score_wizard_close_fix_4.1
fixed #18001: New Score > "Cancel or "Done" or exiting from score wizard puts MuseScore window behind another window. 4.1
2023-07-04 10:45:33 +03:00
Eism 750b492373 Removed unnecessary log 2023-07-04 09:50:58 +03:00
Eism 6b141b7aae Revert "Select first instrument in New Score Dialog"
This reverts commit fd54090d06.
2023-07-04 09:50:42 +03:00
RomanPudashkin 7bfcbece58
Merge pull request #18375 from asattely/stem-distance-in-staff-4.1.0
[4.2 backport] Stem distance in staff 4.1.0
2023-07-04 09:37:55 +03:00
RomanPudashkin c6abf259d9
Merge pull request #18378 from cbjeukendrup/410_mscwriter_error_handling
[4.1.0] Add more careful error handling for MscWriter and ZipContainer
2023-07-04 09:35:51 +03:00
Casper Jeukendrup 9fb1d7e138
Improve error message when saving score fails for unknown reason
Say that there was an unknown error, instead of saying nothing.
2023-07-04 03:09:20 +02:00
Casper Jeukendrup ead6c2141b
Add more careful error handling for MscWriter and ZipContainer 2023-07-04 03:09:19 +02:00
asattely dcd1d3371c vtest for backport of 18205 2023-07-03 17:02:53 -04:00
asattely 43539bf8a2 backport of 18205 2023-07-03 17:02:53 -04:00
RomanPudashkin e1df0ae712
Merge pull request #18363 from HemantAntony/17511-changes_on_selecting_field_page_settings_4.1
[4.1] Fix #17511: Changed score updating on changing spinbox in Page Settings and Style dialogs
2023-07-03 20:18:20 +03:00
RomanPudashkin 2e42e6aac3
Merge pull request #18366 from alexpavlov96/percussion_crash
fixed crash while layouting percussion
2023-07-03 19:48:37 +03:00
Casper Jeukendrup f2af14a914
Merge pull request #18372 from musescore/ci_run_lupdate
Update in-repo translation source files
2023-07-03 18:32:04 +02:00
cbjeukendrup d88e882c86 Run lupdate 2023-07-03 16:25:05 +00:00
Alexander Pavlov 03c8f7d476 fixed crash while layouting percussion 2023-07-03 16:48:38 +03:00
Hemant Antony 9891f275b6 Changed score updating on changing Style Dialog spinboxes 2023-07-03 18:23:10 +05:30
Hemant Antony c820bd447b Fix #17511: Changed score updating on changing Page Settings spinboxes 2023-07-03 18:23:03 +05:30
RomanPudashkin c2ca4674a9
Merge pull request #18360 from Eism/update_dialog_multiinstances_fix_4.1
fixed #15870: Crash when declining update then using search bar in New Score dialog. 4.1
2023-07-03 13:30:52 +03:00
RomanPudashkin 134c7f3af1
Merge pull request #18361 from Eism/project_save_fail_msg_4.1
fixed #16104: No indication when error occurs while saving a file. 4.1
2023-07-03 13:29:53 +03:00
Eism c285343daa fixed #16104: Show a warning with information about the save error 2023-07-03 12:21:13 +03:00
Eism aede10d67b Check and show update only for first instance 2023-07-03 12:17:43 +03:00
RomanPudashkin 6ff8c4ff1b
Merge pull request #18322 from asattely/beam-distance-410
[4.1.0] Beam distance fix backport
2023-07-03 12:04:21 +03:00
RomanPudashkin 89b5a47d7e
Merge pull request #18338 from RomanPudashkin/aux_solo_mute_410
aux_solo_mute_410
2023-07-01 13:02:08 +03:00
Roman Pudashkin 3044e5ded4 implemented the ability to mute aux channels 2023-07-01 10:17:26 +03:00
Roman Pudashkin 5c63cd785f implemented the ability to save the aux solo/mute state to the project 2023-07-01 10:17:17 +03:00
Roman Pudashkin b628a80e83 renaming: soloMuteState -> trackSoloMuteState 2023-07-01 10:17:09 +03:00
Roman Pudashkin 27ae291997 hide/disable the solo button for the master/aux channels 2023-07-01 10:17:00 +03:00
RomanPudashkin 7185eb8260
Merge pull request #18324 from asattely/tremolo-fixes-410
PR #18259 for 4.1.0 (Tremolo fix megaissue)
2023-06-30 17:50:11 +03:00
asattely 0158c5b176 trem fix vtests 2023-06-30 09:31:32 -04:00
asattely 64335fa7e6 remove trems when inserting time in the middle of them 2023-06-30 09:31:20 -04:00
asattely f476a00a21 fix relayout issue 2023-06-30 09:31:10 -04:00
asattely 9073b5ae40 fix tremolo styles (trad vs trad alternate) 2023-06-30 09:30:48 -04:00
asattely 6b5db2bcbd fix incorrectly flat trem beams 2023-06-30 09:30:25 -04:00
asattely 5e02ebcf02 backport of PR #18303 2023-06-30 09:24:48 -04:00
Casper Jeukendrup baa047dd4a
Merge pull request #18307 from cbjeukendrup/410_crash_ctrlshiftdrag_hairpin
[4.1.0] Fix crash when Ctrl+Shift+Dragging Hairpin
2023-06-30 11:59:18 +02:00
RomanPudashkin f32d5858bf
Merge pull request #18311 from mike-spa/portingSeveralPRs
Porting several PRs
2023-06-30 12:16:26 +03:00
Peter Jonas f7fab29515 Properly refresh braille when notation changes
Close #18283 - Crash after adding notes, undo, and adding notes again
2023-06-30 10:07:33 +01:00
Michele Spagnolo 8e9b37a2dc Fix file corruption on copy-paste of nested tuplets with parts 2023-06-30 10:08:21 +02:00
Michele Spagnolo 5f91a4cb73 Switch to canvas coordinates when passing the drop to Measure 2023-06-30 10:07:57 +02:00
Michele Spagnolo f02c043b06 Don't allow deleting trill cue note 2023-06-30 10:07:31 +02:00
Michele Spagnolo df4a0439bd Display info message when trying to delete a key signature that can't be deleted
correction
2023-06-30 10:07:02 +02:00
RomanPudashkin 2d1c3b47d1
Merge pull request #18305 from cbjeukendrup/410_expr
[4.1.0] Add > Text > Expression: really add an expression, not just staff text
2023-06-30 10:17:34 +03:00
RomanPudashkin 5928dfd1e6
Merge pull request #18306 from cbjeukendrup/410_expr_special_chrs
[4.1.0] Fix entering special characters in Expression text
2023-06-30 10:15:10 +03:00
Casper Jeukendrup 2604906b5d
Fix crash when Ctrl+Shift+Dragging Hairpin 2023-06-29 23:37:41 +02:00
Casper Jeukendrup 57553606e1
Fix entering special characters in Expression text 2023-06-29 23:31:39 +02:00
Casper Jeukendrup 63b3499932
Add > Text > Expression: really add an expression, not just staff text 2023-06-29 23:29:07 +02:00
Casper Jeukendrup ea50c6d538
Merge pull request #18296 from HemantAntony/18274-toggle_buttons_are_wrong_colour_4.1 2023-06-29 21:03:45 +02:00
Hemant Antony 8294b3b1e1 Fix #18274: Toggle buttons are wrong colour 2023-06-29 23:46:24 +05:30
RomanPudashkin fa42edc4e4
Merge pull request #18284 from cbjeukendrup/410_crash_svg
[4.1.0] Fix crash when trying to export "potential" (not-yet-initialized) part scores
2023-06-29 19:31:12 +03:00
Casper Jeukendrup 0e27fb3e82
Don't remember last used filename in Export dialog
We can re-implement this later, but it should only happen if exactly the same notations are selected and the same unit type. These settings may namely cause extra things to be added to the name, but these extra things are not applicable to other values of these settings, which causes confusion.
2023-06-29 16:10:26 +02:00
Casper Jeukendrup 002eef3709
Fix crash when trying to export potential (not-yet-initialized) part scores
The problem is that we tried to know whether only one file will be created. If that is the case, we would show the full filename in the file dialog; otherwise, we show a base filename and for each exported file something (like page number) is added to the filename.
To compute the number of files being created, in the case of SVG and PNG, we needed to know the number of pages. But this can't be known yet for potential not-yet-initalized part scores. That caused a crash.
Solution: if we can't know whether only one file will be created, we just assume `false`.
We do need to make sure that the decision about this at the moment that we _ask_ the export path is based on the same state as the decision at the moment that we are _actually exporting_ scores.
2023-06-29 16:10:24 +02:00
RomanPudashkin 11c4c02d07
Merge pull request #18251 from MarcSabatella/revoice-fix-410
Port PR #18227 for 4.1.0 branch (fixes accessibility-related crashes)
2023-06-29 17:02:18 +03:00
RomanPudashkin ce4afc9a7d
Merge pull request #18278 from RomanPudashkin/trill_playback_410
trill_playback_410
2023-06-29 16:22:53 +03:00
Elnur Ismailzada 161c74da28
Merge pull request #18276 from Eism/update_versioning_fix_4.1
Ignore unstable(alfa/beta/rc) versions for stable releases. 4.1
2023-06-29 15:31:23 +03:00
Roman Pudashkin 96e0ab3b81 fix #17867: use the default interval if it is not specified explicitly 2023-06-29 15:19:19 +03:00
Eism 25dcb7b3f0 Ignore unstable(alfa/beta/rc) versions for stable releases 2023-06-29 14:15:52 +03:00
Elnur Ismailzada 28512d0761
Merge pull request #18271 from Eism/update_release_dialog_fix_4.1
fixed #16774: Update announcement popup (for new releases) has poor UX / UI. 4.1
2023-06-29 14:15:04 +03:00
Eism 4a77c51e6b Small fixes in UI 2023-06-29 11:58:19 +03:00
Eism 0143883d28 Testing mode by default
not to accidentally notify all users about the update
2023-06-29 11:58:12 +03:00
Eism 976a6aaa53 fixed: The scrollbar overlaps the text 2023-06-29 11:58:02 +03:00
Eism 66d0bd55f5 fixed: The title is weak 2023-06-29 11:57:54 +03:00
Eism f2b6b2d6f2 Enabled Markdown formatting for the release info dialog 2023-06-29 11:57:43 +03:00
Hemant Antony 4fe1b0db70 Fix #17949: Aux sends 1 and 2 are too close together 2023-06-29 00:14:01 +05:30
Elnur Ismailzada 37b87934a4
Merge pull request #18250 from Eism/backend_score_parts_show_customs_4.1
fixed #17162: Some issues with parts in PDF on musescore.com. 4.1
2023-06-28 18:35:23 +03:00
Marc Sabatella 43f124a526 PR #18227 for 4.1.0 branch 2023-06-28 08:48:05 -06:00
Eism 6fe1a1f376 fixed #17162: Provide custom parts for --score-parts command
like for --score-parts-pdf command
2023-06-28 17:42:28 +03:00
RomanPudashkin dfc286f3b0
Merge pull request #18246 from RomanPudashkin/silent_buffers_optimization_410
silent_buffers_optimization_410
2023-06-28 16:56:55 +03:00
RomanPudashkin b3b8efe5b2
Merge pull request #18247 from RomanPudashkin/treble_clef_playback_410
treble_clef_playback_410
2023-06-28 16:45:19 +03:00
Roman Pudashkin c5c089c8bc fix #18163: use the old implementation for staffIdxSetFromRange
Although the new implementation is more efficient and returns a more correct result (for trackFrom = 4 and trackTo = 8 it returns {1} instead of {0, 1}), PlaybackModel relies heavily on the old result of this function (it expects {0, 1}). So, when the user enters a note on the staff with idx = 1, PlaybackModel clears events for both 0 and 1 staves
2023-06-28 15:56:41 +03:00
Roman Pudashkin a16439b2c7 fix #18037: don't send "silent" buffers to the aux channels 2023-06-28 15:54:51 +03:00
RomanPudashkin 61bb83eadb
Merge pull request #18233 from HemantAntony/18223-preferences_window_ui_change_4.1
[4.1] Fix #18223: New Braille icon and changed Preferences window height
2023-06-28 10:33:14 +03:00
Hemant Antony 67e4e732f1 Fix #18223: New Braille icon and changed Preferences window height 2023-06-28 09:32:16 +05:30
RomanPudashkin 41a5ebe53c
Merge pull request #18181 from Jojo-Schmitz/4.1.0-musicxml-expression
Fix GH#18166: All expression texts not exported to MusicXML
2023-06-27 19:09:01 +03:00
Elnur Ismailzada e01f1e8407
Merge pull request #18219 from Eism/crashpad_fix_2_4.1
Crashpad fix. 4.1
2023-06-27 17:18:28 +03:00
Eism 7b48df5599 Updated for ubuntu 20 2023-06-27 16:31:29 +03:00
Eism fb3c06b162 Reverted AppImage: Remove unnecessary OpenSSL dependency
commit 043d13c1df
2023-06-27 16:31:22 +03:00
Eism 340107b10c Updated crashpad for macos
built on macos x86_64
2023-06-27 16:31:15 +03:00
Elnur Ismailzada b15601b9ea Revert "Updated crash handler for linux"
This reverts commit 5d78711038.
2023-06-27 16:31:05 +03:00
RomanPudashkin e493c1fff4
Merge pull request #18211 from mike-spa/port-18063
Port PR #18063 to 4.1 branch
2023-06-27 11:49:13 +03:00
Michele Spagnolo e3c8f2b2b6 Improve cloning of ornaments
code review correction
2023-06-27 10:02:42 +02:00
RomanPudashkin 1eebaf164d
Merge pull request #18207 from cbjeukendrup/410_keynav_insp
[4.1.0] Fix keyboard navigation in More Text Settings popup and Dynamics inspector
2023-06-27 10:01:51 +03:00
Casper Jeukendrup 482efeffce
Fix keyboard navigation in Dynamics inspector
partially by using the new CheckBoxPropertyView component
2023-06-26 22:25:58 +02:00
Casper Jeukendrup 1061b3b3f1
Introduce new CheckBoxPropertyView component
that is a real InspectorPropertyView with a checkbox.
2023-06-26 22:25:58 +02:00
Casper Jeukendrup 3e8d2ae430
Rename CheckBoxPropertyView to PropertyCheckBox 2023-06-26 22:25:57 +02:00
Casper Jeukendrup 40c5607cd7
Fix keyboard navigation in More Text Settings popup 2023-06-26 22:25:57 +02:00
Casper Jeukendrup 083424b92e
Merge pull request #18200 from musescore/ci_run_lupdate
Update in-repo translation source files
2023-06-26 17:41:41 +02:00
cbjeukendrup 8f9aeb545c Run lupdate 2023-06-26 15:40:45 +00:00
Peter Jonas bc2b409e5d Instruments: Rename Lauds family to Laúds
Close #17758
2023-06-26 16:39:27 +01:00
Casper Jeukendrup d2acf7aa10
Merge pull request #18192 from Jojo-Schmitz/4.1.0-ogg-export-sample-rate
Fix GH#17659: OGG export always at 48000 Hz
2023-06-26 17:32:09 +02:00
RomanPudashkin 053e1e40b3
Merge pull request #18179 from mike-spa/port-PR18024-to-410
Port PR #18024 to 4.1 branch
2023-06-26 16:35:34 +03:00
Joachim Schmitz c3a6e7698a Fix GH#17659: OGG export always at 48000 Hz 2023-06-26 15:13:03 +02:00
worldwideweary 819d62deb2 Draw hairpins as polygons
except when crossing system breaks or non-solid lines.
Also using `BevelJoin` rather than `MiterJoin`
2023-06-26 15:05:32 +02:00
worldwideweary c3c043c4c2 Adjust bounding box of chord symbols if they have frames 2023-06-26 15:05:32 +02:00
RomanPudashkin 0e6ef99cf3
Merge pull request #18183 from cbjeukendrup/410_mmrepeat_pl
Multi-measure repeats playback for 4.1.0
2023-06-26 14:55:12 +03:00
Casper Jeukendrup c317225488
Code review fixes 2023-06-26 12:42:42 +02:00
Casper Jeukendrup 99ed8fde46
Add multi measure repeat unit test 2023-06-26 12:42:42 +02:00
Casper Jeukendrup 84abc7faa0
Make single measure repeat unit test more useful 2023-06-26 12:42:42 +02:00
Casper Jeukendrup 508327e3d6
Implement playback for multi-measure repeats 2023-06-26 12:42:41 +02:00
Joachim Schmitz 6474828563 Fix GH#18166: All expression texts not exported to MusicXML 2023-06-26 10:01:56 +02:00
Michele Spagnolo f153091bc7 version tag correction 2023-06-26 09:36:12 +02:00
asattely a75ae35e3a update tests with combined artics in them 2023-06-26 09:27:24 +02:00
asattely 423fbc30bd reset user offsets for objects that have changed during development 2023-06-26 09:27:20 +02:00
asattely 7580df2b17 Convert all combined articulations from 4.0 scores to their components 2023-06-26 09:27:03 +02:00
Casper Jeukendrup fcaa649665
Merge pull request #18150 from musescore/ci_run_lupdate 2023-06-23 17:36:59 +02:00
cbjeukendrup ff474eb382 Run lupdate 2023-06-23 15:36:11 +00:00
Casper Jeukendrup a3827ef162
Merge pull request #18148 from cbjeukendrup/410_backticks 2023-06-23 17:33:52 +02:00
Casper Jeukendrup a63ed089fa
Don't use backticks in translatable strings
lupdate is too stupid to understand it, apparently
2023-06-23 16:34:36 +02:00
RomanPudashkin e1197fe673
Merge pull request #18144 from mike-spa/portPRsFromMaster
Port PRs from master into 4.1
2023-06-23 17:08:34 +03:00
Elnur Ismailzada 683aab3c5a
Merge pull request #18141 from Eism/braille_navigation_fix_4.1
fixed #18068: [Accessibility] Tab navigation gets stuck in Braille panel. 4.1
2023-06-23 15:06:41 +03:00
Elnur Ismailzada c1d4900229
Merge pull request #18140 from Eism/score_switch_zoom_fix_4.1
fixed #17906: Opening parts, then returning to score, score is zoomed in very far. 4.1
2023-06-23 15:06:31 +03:00
Michele Spagnolo 0428219356 Updated some incorrect vtests 2023-06-23 13:40:48 +02:00
Michele Spagnolo b988d36445 Use std::set instead of std::vector for startingSpanners and endSpanners 2023-06-23 13:40:41 +02:00
Michele Spagnolo 311378527c Move startingSpanners / endingSpanners from EngravingItem to Chord 2023-06-23 13:40:36 +02:00
Michele Spagnolo a468a17cde Fix trill crash on part 2023-06-23 13:40:29 +02:00
Eism 4f70f46167 fixed #18068: Handle Tab symbol as shortcut for TextArea 2023-06-23 14:18:32 +03:00
Eism fac7ce310c fixed #17906: Don't clear zoom settings when switching parts 2023-06-23 14:14:36 +03:00
RomanPudashkin 36d428a282
Merge pull request #18137 from mike-spa/portFixTremoloBugInTAB
Fix tremolo bug in TAB staves
2023-06-23 13:28:20 +03:00
RomanPudashkin 3695542cce
Merge pull request #18136 from Jojo-Schmitz/4.1.0-ornaments-translation
Fix GH#18019 and fix GH#18121
2023-06-23 10:44:36 +03:00
Michele Spagnolo ec735242d6 Fix tremolo bug in TAB staves 2023-06-23 09:38:18 +02:00
Joachim Schmitz b6484de64c Fix GH#18121: Turn should come before inverted turn in Ornaments palette 2023-06-23 09:00:16 +02:00
Joachim Schmitz 91492890e8 Fix GH#18019: Translations for most ornaments don't work 2023-06-23 09:00:16 +02:00
Casper Jeukendrup 90a28b5eae
Merge pull request #18125 from cbjeukendrup/410_win_portable_signing
[4.1.0] Enable signing for Windows Portable builds
2023-06-22 20:58:51 +02:00
Casper Jeukendrup b0f1da5d55
Enable signing for Windows Portable builds 2023-06-22 20:07:36 +02:00
Casper Jeukendrup d38d9cd364
Merge pull request #18117 from musescore/ci_run_lupdate
Update in-repo translation source files
2023-06-22 15:45:14 +02:00
cbjeukendrup ec96a22dac Run lupdate 2023-06-22 13:38:42 +00:00
RomanPudashkin 7d15ce0a43
Merge pull request #18110 from Jojo-Schmitz/musicxml-chord-symbols-bass
Fix #320818: Chord symbols with a bass extension don't export to MusicXML correctly (nor import back)
2023-06-22 15:36:40 +03:00
RomanPudashkin a8d4af44c1
Merge pull request #18112 from Eism/score_popups_nav_fix_2_4.1
score_popups_nav_fix and crash_reports_update for 4.1
2023-06-22 15:36:20 +03:00
Joachim Schmitz 39183b5fb5 Fix #320818: Chord symbols with a bass extension don't export to MusicXML correctly (nor import back) 2023-06-22 13:38:26 +02:00
Joachim Schmitz 814feb3878 Add MusicXML export unit test for chord symbols (with bass extension) 2023-06-22 13:38:03 +02:00
RomanPudashkin e20fedb5c2
Merge pull request #18111 from cbjeukendrup/410_ci
Enable CI checks for pull requests for 4.1.0 branch
2023-06-22 14:36:24 +03:00
Eism 12ee4c3099 Disabled crashpad for vtests 2023-06-22 13:52:54 +03:00
Eism e91138b6cb Updated crash handler for macOS 2023-06-22 13:52:48 +03:00
Eism 28182c1729 Updated crash handler for linux 2023-06-22 13:52:42 +03:00
Eism 6a2065dfb9 No need to manually start navigation in open popups, that's the job of the NavigationController 2023-06-22 13:52:23 +03:00
Eism 792317a4fd Hide element popup and context menu if nothing is selected 2023-06-22 13:52:16 +03:00
Eism b0f1074be6 Explicitly disabled navigation section for popups 2023-06-22 13:52:07 +03:00
Eism 723049d43d Fixed navigation order for the notation view 2023-06-22 13:51:57 +03:00
RomanPudashkin 462dcdb3aa
Merge pull request #18107 from cbjeukendrup/cl_open_login_410
[4.1.0] Ensure the user is logged in before trying to open cloud score
2023-06-22 13:48:13 +03:00
Casper Jeukendrup bb023d2d95
Enable CI checks for pull requests for 4.1.0 branch 2023-06-22 12:31:43 +02:00
Casper Jeukendrup 1175e334f2
Ensure the user is logged in before trying to open cloud score 2023-06-22 11:46:22 +02:00
Casper Jeukendrup da38073eaa
Package translations with Portable Windows build 2023-06-22 02:45:33 +02:00
348 changed files with 8870 additions and 4093 deletions

View File

@ -4,6 +4,7 @@ on:
pull_request:
branches:
- master
- 4.1.0
jobs:
codestyle:

View File

@ -17,7 +17,7 @@ on:
default: ''
env:
CURRENT_RELEASE_BRANCH: 4.0.2
CURRENT_RELEASE_BRANCH: 4.1.0
jobs:
build_armhf:

View File

@ -4,6 +4,7 @@ on:
pull_request:
branches:
- master
- 4.1.0
schedule:
- cron: '0 3 */1 */1 *' # At 03:00 on every day-of-month for master
@ -24,7 +25,7 @@ on:
default: ''
env:
CURRENT_RELEASE_BRANCH: 4.0.2
CURRENT_RELEASE_BRANCH: 4.1.0
jobs:
build_mu4:

View File

@ -4,6 +4,7 @@ on:
pull_request:
branches:
- master
- 4.1.0
schedule:
- cron: '0 3 */1 */1 *' # At 03:00 on every day-of-month for master
@ -24,7 +25,7 @@ on:
default: ''
env:
CURRENT_RELEASE_BRANCH: 4.0.2
CURRENT_RELEASE_BRANCH: 4.1.0
DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer
jobs:

View File

@ -4,9 +4,9 @@ on:
workflow_dispatch:
inputs:
mode:
description: 'Mode: stable, testing'
description: 'Mode: stable, testing(alpha, beta, rc)'
required: true
default: 'stable'
default: 'testing'
tag:
description: 'Release tag (latest stable by default)'
required: false

View File

@ -4,9 +4,9 @@ on:
workflow_dispatch:
inputs:
mode:
description: 'Mode: stable, testing'
description: 'Mode: stable, testing(alpha, beta, rc)'
required: true
default: 'stable'
default: 'testing'
defaults:
run:

View File

@ -4,6 +4,7 @@ on:
pull_request:
branches:
- master
- 4.1.0
workflow_dispatch:
jobs:

View File

@ -4,7 +4,7 @@ on:
pull_request:
branches:
- master
- 4.0.2
- 4.1.0
jobs:
setup:

View File

@ -4,6 +4,7 @@ on:
pull_request:
branches:
- master
- 4.1.0
schedule:
- cron: '0 3 */1 */1 *' # At 03:00 on every day-of-month for master
@ -24,7 +25,7 @@ on:
default: ''
env:
CURRENT_RELEASE_BRANCH: 4.0.2
CURRENT_RELEASE_BRANCH: 4.1.0
jobs:
build_mu4_x64:
@ -112,8 +113,10 @@ jobs:
fi
if [ $DO_BUILD == 'false' ]; then
DO_UPLOAD_SYMBOLS='false'
DO_PUBLISH='false'
DO_UPDATE_TS='false'
DO_PLACEHOLDER_TRANSLATIONS='false'
DO_UPLOAD_SYMBOLS='false'
fi
ADD_INFO="_${GITHUB_REF#refs/heads/}"
@ -249,11 +252,37 @@ jobs:
if [[ "$BUILD_MODE" == "testing_build" || "$BUILD_MODE" == "stable_build" ]]; then
DO_BUILD='true'
fi
if [[ "$BUILD_MODE" == "nightly_build" && "${{ github.repository }}" == "musescore/MuseScore" ]]; then
DO_BUILD='true'
fi
DO_PUBLISH='false'
if [[ "${{ github.event.inputs.publish }}" == "on" || "$BUILD_MODE" == "nightly_build" ]]; then
DO_PUBLISH='true'
if [ -z "${{ secrets.OSUOSL_SSH_ENCRYPT_SECRET }}" ]; then
echo "warning: not set OSUOSL_SSH_ENCRYPT_SECRET, publish disabled"
DO_PUBLISH='false'
fi
fi
if [ "${{ github.event_name }}" == "pull_request" ]; then PR_INFO="_${{ github.event.pull_request.number }}_${pull_request_title}"; fi
UPLOAD_ARTIFACT_NAME="$(tr '":<>|*?/\\' '_' <<<"MU4_${BUILD_NUMBER}_Win${PR_INFO}_portable")"
DO_UPDATE_TS='false'
if [[ "$BUILD_MODE" == "testing_build" || "$BUILD_MODE" == "stable_build" ]]; then
DO_UPDATE_TS='true'
if [ -z "${{ secrets.TRANSIFEX_API_TOKEN }}" ]; then
echo "warning: not set TRANSIFEX_API_TOKEN, update .ts disabled"
DO_UPDATE_TS='false'
fi
fi
if [ $DO_BUILD == 'false' ]; then
DO_PUBLISH='false'
DO_UPDATE_TS='false'
fi
ADD_INFO="_${GITHUB_REF#refs/heads/}"
if [ "${{ github.event_name }}" == "schedule" ] && [ "${{ github.event.schedule }}" == "0 5 */1 */1 *" ]; then ADD_INFO="_${CURRENT_RELEASE_BRANCH}"; fi
if [ "${{ github.event_name }}" == "pull_request" ]; then ADD_INFO="_${{ github.event.pull_request.number }}_${pull_request_title}"; fi
UPLOAD_ARTIFACT_NAME="$(tr '":<>|*?/\\' '_' <<<"MU4_${BUILD_NUMBER}_Win_portable${ADD_INFO}")"
echo "github.repository: ${{ github.repository }}"
echo "BUILD_MODE=$BUILD_MODE" >> $GITHUB_ENV
@ -262,8 +291,10 @@ jobs:
echo "BUILD_NUMBER: $BUILD_NUMBER"
echo "DO_BUILD=$DO_BUILD" >> $GITHUB_ENV
echo "DO_BUILD: $DO_BUILD"
echo "DO_UPDATE_TS=$DO_UPDATE_TS" >> $GITHUB_ENV
echo "DO_UPDATE_TS: $DO_UPDATE_TS"
echo "DO_PUBLISH=$DO_PUBLISH" >> $GITHUB_ENV
echo "DO_PUBLISH: $DO_PUBLISH"
echo "DO_PUBLISH: $DO_PUBLISH"
echo "UPLOAD_ARTIFACT_NAME=$UPLOAD_ARTIFACT_NAME" >> $GITHUB_ENV
echo "UPLOAD_ARTIFACT_NAME: $UPLOAD_ARTIFACT_NAME"
@ -276,6 +307,17 @@ jobs:
shell: bash
run: |
bash ./build/ci/windows/make_environment.sh
- name: Update .ts files
if: env.DO_UPDATE_TS == 'true'
shell: bash
run: |
bash ./build/ci/translation/tx_install.sh -t ${{ secrets.TRANSIFEX_API_TOKEN }} -s windows
bash ./build/ci/translation/tx_pull.sh
- name: Generate .qm files
if: env.DO_BUILD == 'true'
shell: bash
run: |
bash ./build/ci/translation/run_lrelease.sh
- name: Build
if: env.DO_BUILD == 'true'
shell: cmd
@ -287,16 +329,18 @@ jobs:
if: env.DO_BUILD == 'true'
shell: cmd
run: |
build\ci\windows\package.bat --portable ON
IF ${{ secrets.WIN_SIGN_CERTIFICATE_ENCRYPT_SECRET != 0 }} == true ( SET S_S=${{ secrets.WIN_SIGN_CERTIFICATE_ENCRYPT_SECRET }} ) ELSE ( SET S_S="''" )
IF ${{ secrets.WIN_SIGN_CERTIFICATE_PASSWORD != 0 }} == true ( SET S_P=${{ secrets.WIN_SIGN_CERTIFICATE_PASSWORD }} ) ELSE ( SET S_P="''" )
build\ci\windows\package.bat --portable ON --signsecret %S_S% --signpass %S_P%
- name: Checksum
if: env.DO_BUILD == 'true'
run: |
bash ./build/ci/tools/checksum.sh
- name: Publish package
if: env.DO_PUBLISH == 'true'
shell: bash
run: |
build\ci\windows\publish.bat --secret ${{ secrets.OSUOSL_SSH_ENCRYPT_SECRET }}
shell: cmd
bash ./build/ci/tools/osuosl/publish.sh -s ${{ secrets.OSUOSL_SSH_ENCRYPT_SECRET }} --os windows -v 4 --arch x86_64-portable
- name: Upload artifacts on GitHub
if: env.DO_BUILD == 'true'
uses: actions/upload-artifact@v3

View File

@ -4,6 +4,7 @@ on:
pull_request:
branches:
- master
- 4.1.0
jobs:
run_tests:

View File

@ -47,17 +47,20 @@ if(BUILD_MODE MATCHES "DEV")
set(MUSESCORE_UNSTABLE ON)
set(MUSESCORE_VERSION_LABEL "dev")
set(MUSESCORE_NAME_VERSION "${MUSESCORE_NAME} ${MUSESCORE_VERSION_MAJOR}")
set(MUSESCORE_ALLOW_UPDATE_ON_PRERELEASE OFF)
endif()
if(BUILD_MODE MATCHES "TESTING")
set(MUSESCORE_UNSTABLE OFF)
set(MUSESCORE_VERSION_LABEL "Testing")
set(MUSESCORE_NAME_VERSION "${MUSESCORE_NAME} ${MUSESCORE_VERSION_MAJOR}.${MUSESCORE_VERSION_MINOR} ${MUSESCORE_VERSION_LABEL}")
set(MUSESCORE_ALLOW_UPDATE_ON_PRERELEASE ON)
endif()
if(BUILD_MODE MATCHES "RELEASE")
set(MUSESCORE_UNSTABLE OFF)
set(MUSESCORE_NAME_VERSION "${MUSESCORE_NAME} ${MUSESCORE_VERSION_MAJOR}")
set(MUSESCORE_ALLOW_UPDATE_ON_PRERELEASE OFF)
endif()
if (MUSESCORE_UNSTABLE)
@ -148,6 +151,8 @@ if(BUILD_CONFIGURE MATCHES "VTEST")
set(MUE_INSTALL_SOUNDFONT OFF)
set(MUE_BUILD_CRASHPAD_CLIENT OFF)
endif()
###########################################
@ -228,6 +233,9 @@ if (MUSESCORE_UNSTABLE)
add_definitions(-DMUSESCORE_UNSTABLE)
endif()
if (MUSESCORE_ALLOW_UPDATE_ON_PRERELEASE)
add_definitions(-DMUSESCORE_ALLOW_UPDATE_ON_PRERELEASE)
endif()
function(def_opt name val)
if (${val})

View File

@ -53,6 +53,7 @@ apt_packages_runtime=(
libegl1-mesa-dev
libodbc1
libpq-dev
libssl-dev
libxcomposite-dev
libxcursor-dev
libxi-dev
@ -275,7 +276,8 @@ additional_qt_components=(
# linuxdeploy may have missed some libraries that we need
# Report new additions at https://github.com/linuxdeploy/linuxdeploy/issues
additional_libraries=(
# none
libssl.so.1.1 # OpenSSL (for Save Online)
libcrypto.so.1.1 # OpenSSL (for Save Online)
)
# FALLBACK LIBRARIES

View File

@ -1,4 +1,23 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-only
# MuseScore-CLA-applies
#
# MuseScore
# Music Composition & Notation
#
# Copyright (C) 2021 MuseScore BVBA and others
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import io
import sys

View File

@ -80,6 +80,7 @@ apt_packages_runtime=(
libegl1-mesa-dev
libodbc1
libpq-dev
libssl-dev
libxcomposite-dev
libxcursor-dev
libxi-dev

View File

@ -193,7 +193,8 @@ additional_qt_components=(
# linuxdeploy may have missed some libraries that we need
# Report new additions at https://github.com/linuxdeploy/linuxdeploy/issues
additional_libraries=(
# none
libssl.so.1.1 # OpenSSL (for Save Online)
libcrypto.so.1.1 # OpenSSL (for Save Online)
)
# FALLBACK LIBRARIES

View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-only
# MuseScore-CLA-applies
#
# MuseScore
# Music Composition & Notation
#
# Copyright (C) 2021 MuseScore BVBA and others
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sys
import json
import markdown
RELEASE_INFO_FILE = sys.argv[1]
print("=== Load json ===")
json_file = open(RELEASE_INFO_FILE, "r+")
release_info_json = json.load(json_file)
json_file.close()
print("=== Make html version of body ===")
release_body_markdown = release_info_json["body"]
release_body_html = markdown.markdown(release_body_markdown)
# Correct result of Markdown parser
# Escape single quotes
release_body_html = release_body_html.replace("'", "`")
# Correct new lines next to <ul> and </ul>
release_body_html = release_body_html.replace("\n<ul>\n", "<ul>")
release_body_html = release_body_html.replace("\n</ul>\n", "</ul>")
release_info_json["body"] = "'" + release_body_html + "'"
release_info_json["bodyMarkdown"] = release_body_markdown
release_info_json_updated = json.dumps(release_info_json)
print("=== Write json ===")
json_file = open(RELEASE_INFO_FILE, "w")
json_file.write(release_info_json_updated)
json_file.close()

View File

@ -49,3 +49,8 @@ RELEASE_INFO=$(curl \
mkdir -p $ARTIFACTS_DIR
echo $RELEASE_INFO > $ARTIFACTS_DIR/release_info.json
cat $ARTIFACTS_DIR/release_info.json
pip install markdown
HERE="$(cd "$(dirname "$0")" && pwd)"
python3 $HERE/correct_release_info.py ${ARTIFACTS_DIR}/release_info.json

View File

@ -118,7 +118,9 @@ if [ "$BUILD_MODE" == "nightly_build" ]; then
echo "Delete old MuseScoreNightly files"
number_to_keep=42 # includes the one we just uploaded and the symlink to it
if [ "$OS" == "linux" ]; then
((++number_to_keep)) # one extra for the zsync file
((++number_to_keep)) # one extra for the zsync file
elif [ "$OS" == "windows" ]; then
((number_to_keep *= 2)) # two nightlies each night, namely portable and normal
fi
ssh -i $SSH_KEY musescore-nightlies@ftp-osl.osuosl.org "cd ~/ftp/$FTP_PATH; ls MuseScoreNightly* -t | tail -n +${number_to_keep} | xargs rm -f"
fi

View File

@ -54,6 +54,16 @@ IF %BUILD_MODE% == stable_build ( SET PACKAGE_TYPE="msi") ELSE (
SET DO_SIGN=OFF
IF %PACKAGE_TYPE% == "msi" (
SET DO_SIGN=ON
)
IF %PACKAGE_TYPE% == "portable" (
IF %BUILD_MODE% == testing_build (
SET DO_SIGN=ON
)
IF %BUILD_MODE% == stable_build (
SET DO_SIGN=ON
)
)
IF %DO_SIGN% == ON (
IF %SIGN_CERTIFICATE_ENCRYPT_SECRET% == "" (
SET DO_SIGN=OFF
ECHO "warning: not set SIGN_CERTIFICATE_ENCRYPT_SECRET"

Binary file not shown.

View File

@ -344,7 +344,7 @@
<name>Bandurrias</name>
</Family>
<Family id="lauds">
<name>Lauds</name>
<name>Laúds</name>
</Family>
<Family id="strings">
<name>Strings</name>

View File

@ -188,7 +188,7 @@ QT_TRANSLATE_NOOP("engraving/instruments/family", "Shamisens"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Sitars"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Tamburicas"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Bandurrias"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Lauds"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Laúds"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Strings"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Orchestral Strings"),
QT_TRANSLATE_NOOP("engraving/instruments/family", "Viols"),

View File

@ -19465,7 +19465,7 @@
</message>
<message>
<location filename="../instruments/instrumentsxml.h" line="191"/>
<source>Lauds</source>
<source>Laúds</source>
<translation type="unfinished"></translation>
</message>
<message>

File diff suppressed because it is too large Load Diff

View File

@ -169,10 +169,18 @@ int App::run(int argc, char** argv)
if (multiInstancesProvider()->isMainInstance()) {
splashScreen = new SplashScreen(SplashScreen::Default);
} else {
QString fileName = startupScenario()->startupScoreFile().displayName(true /* includingExtension */);
splashScreen = new SplashScreen(SplashScreen::ForNewInstance, fileName);
project::ProjectFile file = startupScenario()->startupScoreFile();
if (file.isValid()) {
splashScreen = new SplashScreen(SplashScreen::ForNewInstance, file.displayName(true /* includingExtension */));
} else if (startupScenario()->isStartWithNewFileAsSecondaryInstance()) {
splashScreen = new SplashScreen(SplashScreen::ForNewInstance);
} else {
splashScreen = new SplashScreen(SplashScreen::Default);
}
}
}
if (splashScreen) {
splashScreen->show();
}
#endif

View File

@ -36,6 +36,8 @@ public:
virtual void setStartupType(const std::optional<std::string>& type) = 0;
virtual bool isStartWithNewFileAsSecondaryInstance() const = 0;
virtual const project::ProjectFile& startupScoreFile() const = 0;
virtual void setStartupScoreFile(const std::optional<project::ProjectFile>& file) = 0;

View File

@ -60,6 +60,19 @@ void StartupScenario::setStartupType(const std::optional<std::string>& type)
m_startupTypeStr = type ? type.value() : "";
}
bool StartupScenario::isStartWithNewFileAsSecondaryInstance() const
{
if (m_startupScoreFile.isValid()) {
return false;
}
if (!m_startupTypeStr.empty()) {
return modeTypeTromString(m_startupTypeStr) == StartupModeType::StartWithNewScore;
}
return false;
}
const mu::project::ProjectFile& StartupScenario::startupScoreFile() const
{
return m_startupScoreFile;

View File

@ -48,6 +48,8 @@ public:
void setStartupType(const std::optional<std::string>& type) override;
bool isStartWithNewFileAsSecondaryInstance() const override;
const project::ProjectFile& startupScoreFile() const override;
void setStartupScoreFile(const std::optional<project::ProjectFile>& file) override;

View File

@ -34,7 +34,7 @@ StyledDialogView {
title: qsTrc("appshell/preferences", "Preferences")
contentWidth: 880
contentHeight: 600
contentHeight: 640
resizable: true
property string currentPageId: ""

View File

@ -191,7 +191,7 @@ void PreferencesModel::load(const QString& currentPageId)
makeItem("advanced", QT_TRANSLATE_NOOP("appshell/preferences", "Advanced"), IconCode::Code::CONFIGURE,
"Preferences/AdvancedPreferencesPage.qml"),
makeItem("braille", QT_TRANSLATE_NOOP("appshell/preferences", "Braille"), IconCode::Code::VISIBILITY_OFF,
makeItem("braille", QT_TRANSLATE_NOOP("appshell/preferences", "Braille"), IconCode::Code::BRAILLE,
"Preferences/BraillePreferencesPage.qml")
};

View File

@ -70,7 +70,9 @@ void NotationBraille::init()
globalContext()->currentNotationChanged().onNotify(this, [this]() {
if (notation()) {
doBraille(true);
notation()->notationChanged().onNotify(this, [this]() {
doBraille(true);
});
notation()->interaction()->selectionChanged().onNotify(this, [this]() {
doBraille();
@ -278,9 +280,7 @@ void NotationBraille::setShortcut(const QString& sequence)
interaction()->selectLastElement();
} else if (seq == "Ctrl+Home") {
interaction()->selectFirstElement();
}// else if(shortcutsController()->isRegistered(seq)) {
// shortcutsController()->activate(seq);
//}
}
}
path_t NotationBraille::tablesDefaultDirPath() const

View File

@ -101,8 +101,26 @@ StyledFlickable {
brailleModel.cursorPosition = brailleTextArea.cursorPosition;
}
Component.onCompleted: {
textInputFieldModel.init()
}
TextInputFieldModel {
id: textInputFieldModel
}
Keys.onPressed: {
if(event.key !== Qt.Key_Shift && event.key !== Qt.Key_Alt &&
if (event.key === Qt.Key_Tab) {
//! NOTE: We need to handle Tab key here because https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html#tab-focus
//! and we don't use qt navigation system
if (textInputFieldModel.handleShortcut(Qt.Key_Tab, Qt.NoModifier)) {
brailleTextArea.focus = false
event.accepted = true
return
}
}
if (event.key !== Qt.Key_Shift && event.key !== Qt.Key_Alt &&
event.key !== Qt.Key_Control) {
var shortcut = "";
@ -145,6 +163,4 @@ StyledFlickable {
}
}
}
ScrollBar.vertical: StyledScrollBar {}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<museScore version="3.01">
<programVersion>4.0.0</programVersion>
<programRevision>3543170</programRevision>
<museScore version="4.10">
<programVersion>4.1.0</programVersion>
<programRevision></programRevision>
<Score>
<LayerTag id="0" tag="default"></LayerTag>
<currentLayer>0</currentLayer>

View File

@ -131,12 +131,10 @@ Ret BackendApi::exportScoreParts(const io::path_t& in, const io::path_t& out, co
return prj.ret;
}
INotationPtr notation = prj.val->masterNotation()->notation();
QFile outputFile;
openOutputFile(outputFile, out);
Ret ret = doExportScoreParts(notation, outputFile);
Ret ret = doExportScoreParts(prj.val->masterNotation(), outputFile);
outputFile.close();
@ -529,16 +527,16 @@ mu::RetVal<QByteArray> BackendApi::processWriter(const std::string& writerName,
return result;
}
Ret BackendApi::doExportScoreParts(const notation::INotationPtr notation, QIODevice& destinationDevice)
Ret BackendApi::doExportScoreParts(const IMasterNotationPtr masterNotation, QIODevice& destinationDevice)
{
mu::engraving::MasterScore* score = notation->elements()->msScore()->masterScore();
QJsonArray partsObjList;
QJsonArray partsMetaList;
QJsonArray partsTitles;
for (const mu::engraving::Excerpt* excerpt : score->excerpts()) {
mu::engraving::Score* part = excerpt->excerptScore();
ExcerptNotationList excerpts = allExcerpts(masterNotation);
for (IExcerptNotationPtr excerpt : excerpts) {
mu::engraving::Score* part = excerpt->notation()->elements()->msScore();
std::map<String, String> partMetaTags = part->metaTags();
QJsonValue partTitle(part->name());
@ -650,7 +648,12 @@ RetVal<QByteArray> BackendApi::scorePartJson(mu::engraving::Score* score, const
if (!ok) {
LOGW() << "Error save mscz file";
}
mscWriter.close();
if (mscWriter.hasError()) {
ok = false;
LOGW() << "Error write mscz file";
}
QByteArray ba = QByteArray::fromRawData(reinterpret_cast<const char*>(scoreData.constData()), static_cast<int>(scoreData.size()));

View File

@ -81,7 +81,7 @@ private:
static mu::RetVal<QByteArray> processWriter(const std::string& writerName, const notation::INotationPtrList notations,
const project::INotationWriter::Options& options);
static Ret doExportScoreParts(const notation::INotationPtr notation, QIODevice& destinationDevice);
static Ret doExportScoreParts(const notation::IMasterNotationPtr notation, QIODevice& destinationDevice);
static Ret doExportScorePartsPdfs(const notation::IMasterNotationPtr notation, QIODevice& destinationDevice,
const std::string& scoreFileName);
static Ret doExportScoreTranspose(const notation::INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator = false);

View File

@ -34,9 +34,8 @@ mu::Ret DiagnosticFilesWriter::writeDiagnosticFiles(const path_t& destinationPat
{
TRACEFUNC;
static const std::vector<std::string> DIRS_TO_WRITE {
const std::vector<std::string> DIRS_TO_WRITE {
"logs",
"audio_plugins",
"plugins",
"workspaces",
};
@ -61,6 +60,7 @@ mu::Ret DiagnosticFilesWriter::writeDiagnosticFiles(const path_t& destinationPat
const std::vector<std::string> FILES_TO_WRITE {
"shortcuts.xml",
"midi_mappings.xml",
"known_audio_plugins.json",
};
for (const std::string& fileName : FILES_TO_WRITE) {

View File

@ -81,8 +81,7 @@ inline Ret make_ret(Err err, const io::path_t& filePath = "")
break;
case Err::FileTooNew:
text = mtrc("engraving", "This file was saved using a newer version of MuseScore. "
"Visit the <a href=\"%1\">MuseScore website</a> to obtain the latest version.")
.arg(u"https://musescore.org");
"Please visit <a href=\"https://musescore.org\">musescore.org</a> to obtain the latest version.");
break;
case Err::FileOld300Format:
text = mtrc("engraving", "This file was last saved in a development version of 3.0.");

View File

@ -26,6 +26,7 @@
#include "io/dir.h"
#include "serialization/zipreader.h"
#include "serialization/xmlstreamreader.h"
#include "engraving/engravingerrors.h"
#include "log.h"
@ -65,7 +66,7 @@ const MscReader::Params& MscReader::params() const
return m_params;
}
bool MscReader::open()
Ret MscReader::open()
{
return reader()->open(m_params.device, m_params.filePath);
}
@ -253,18 +254,23 @@ MscReader::ZipFileReader::~ZipFileReader()
}
}
bool MscReader::ZipFileReader::open(IODevice* device, const path_t& filePath)
Ret MscReader::ZipFileReader::open(IODevice* device, const path_t& filePath)
{
m_device = device;
if (!m_device) {
if (!FileInfo::exists(filePath)) {
LOGE() << "path does not exist: " << filePath;
return make_ret(Err::FileNotFound, filePath);
}
m_device = new File(filePath);
m_selfDeviceOwner = true;
}
if (!m_device->isOpen()) {
if (!m_device->open(IODevice::ReadOnly)) {
LOGD() << "failed open file: " << filePath;
return false;
LOGE() << "failed open file: " << filePath;
return make_ret(Err::FileOpenError, filePath);
}
}
@ -303,7 +309,7 @@ StringList MscReader::ZipFileReader::fileList() const
StringList files;
std::vector<ZipReader::FileInfo> fileInfoList = m_zip->fileInfoList();
if (m_zip->hasError()) {
LOGD() << "failed read meta";
LOGE() << "failed read meta";
}
for (const ZipReader::FileInfo& fi : fileInfoList) {
@ -332,13 +338,13 @@ ByteArray MscReader::ZipFileReader::fileData(const String& fileName) const
ByteArray data = m_zip->fileData(fileName.toStdString());
if (m_zip->hasError()) {
LOGD() << "failed read data";
LOGE() << "failed read data for filename " << fileName;
return ByteArray();
}
return data;
}
bool MscReader::DirReader::open(IODevice* device, const path_t& filePath)
Ret MscReader::DirReader::open(IODevice* device, const path_t& filePath)
{
if (device) {
NOT_SUPPORTED;
@ -346,13 +352,13 @@ bool MscReader::DirReader::open(IODevice* device, const path_t& filePath)
}
if (!FileInfo::exists(filePath)) {
LOGD() << "not exists path: " << filePath;
return false;
LOGE() << "path does not exist: " << filePath;
return make_ret(Err::FileNotFound, filePath);
}
m_rootPath = containerPath(filePath);
return true;
return make_ok();
}
void MscReader::DirReader::close()
@ -400,29 +406,34 @@ ByteArray MscReader::DirReader::fileData(const String& fileName) const
io::path_t filePath = m_rootPath + "/" + fileName;
File file(filePath);
if (!file.open(IODevice::ReadOnly)) {
LOGD() << "failed open file: " << filePath;
LOGE() << "failed open file: " << filePath;
return ByteArray();
}
return file.readAll();
}
bool MscReader::XmlFileReader::open(IODevice* device, const path_t& filePath)
Ret MscReader::XmlFileReader::open(IODevice* device, const path_t& filePath)
{
m_device = device;
if (!m_device) {
if (!FileInfo::exists(filePath)) {
LOGE() << "path does not exist: " << filePath;
return make_ret(Err::FileNotFound, filePath);
}
m_device = new File(filePath);
m_selfDeviceOwner = true;
}
if (!m_device->isOpen()) {
if (!m_device->open(IODevice::ReadOnly)) {
LOGD() << "failed open file: " << filePath;
return false;
LOGE() << "failed open file: " << filePath;
return make_ret(Err::FileOpenError, filePath);
}
}
return true;
return make_ok();
}
void MscReader::XmlFileReader::close()
@ -453,13 +464,13 @@ StringList MscReader::XmlFileReader::fileList() const
m_device->seek(0);
XmlStreamReader xml(m_device);
while (xml.readNextStartElement()) {
if ("files" != xml.name()) {
if (xml.name() != "files") {
xml.skipCurrentElement();
continue;
}
while (xml.readNextStartElement()) {
if ("file" != xml.name()) {
if (xml.name() != "file") {
xml.skipCurrentElement();
continue;
}
@ -511,13 +522,13 @@ ByteArray MscReader::XmlFileReader::fileData(const String& fileName) const
m_device->seek(0);
XmlStreamReader xml(m_device);
while (xml.readNextStartElement()) {
if ("files" != xml.name()) {
if (xml.name() != "files") {
xml.skipCurrentElement();
continue;
}
while (xml.readNextStartElement()) {
if ("file" != xml.name()) {
if (xml.name() != "file") {
xml.skipCurrentElement();
continue;
}

View File

@ -22,6 +22,7 @@
#ifndef MU_ENGRAVING_MSCREADER_H
#define MU_ENGRAVING_MSCREADER_H
#include "types/ret.h"
#include "types/string.h"
#include "io/path.h"
#include "io/iodevice.h"
@ -51,7 +52,7 @@ public:
void setParams(const Params& params);
const Params& params() const;
bool open();
Ret open();
void close();
bool isOpened() const;
@ -77,7 +78,7 @@ private:
struct IReader {
virtual ~IReader() = default;
virtual bool open(io::IODevice* device, const io::path_t& filePath) = 0;
virtual Ret open(io::IODevice* device, const io::path_t& filePath) = 0;
virtual void close() = 0;
virtual bool isOpened() const = 0;
//! NOTE In the case of reading from a directory,
@ -92,7 +93,7 @@ private:
struct ZipFileReader : public IReader
{
~ZipFileReader() override;
bool open(io::IODevice* device, const io::path_t& filePath) override;
Ret open(io::IODevice* device, const io::path_t& filePath) override;
void close() override;
bool isOpened() const override;
bool isContainer() const override;
@ -107,7 +108,7 @@ private:
struct DirReader : public IReader
{
bool open(io::IODevice* device, const io::path_t& filePath) override;
Ret open(io::IODevice* device, const io::path_t& filePath) override;
void close() override;
bool isOpened() const override;
bool isContainer() const override;
@ -120,7 +121,7 @@ private:
struct XmlFileReader : public IReader
{
bool open(io::IODevice* device, const io::path_t& filePath) override;
Ret open(io::IODevice* device, const io::path_t& filePath) override;
void close() override;
bool isOpened() const override;
bool isContainer() const override;

View File

@ -55,6 +55,7 @@ void MscWriter::setParams(const Params& params)
}
if (m_writer) {
m_hadError = m_writer->hasError();
delete m_writer;
m_writer = nullptr;
}
@ -67,7 +68,7 @@ const MscWriter::Params& MscWriter::params() const
return m_params;
}
bool MscWriter::open()
Ret MscWriter::open()
{
return writer()->open(m_params.device, m_params.filePath);
}
@ -75,10 +76,12 @@ bool MscWriter::open()
void MscWriter::close()
{
if (m_writer) {
writeMeta();
m_writer->close();
if (m_writer->isOpened()) {
writeMeta();
m_writer->close();
}
m_hadError = m_writer->hasError();
delete m_writer;
m_writer = nullptr;
}
@ -89,6 +92,11 @@ bool MscWriter::isOpened() const
return m_writer ? m_writer->isOpened() : false;
}
bool MscWriter::hasError() const
{
return m_writer ? m_writer->hasError() : m_hadError;
}
MscWriter::IWriter* MscWriter::writer() const
{
if (!m_writer) {
@ -253,7 +261,7 @@ MscWriter::ZipFileWriter::~ZipFileWriter()
}
}
bool MscWriter::ZipFileWriter::open(io::IODevice* device, const path_t& filePath)
Ret MscWriter::ZipFileWriter::open(io::IODevice* device, const path_t& filePath)
{
m_device = device;
if (!m_device) {
@ -264,7 +272,7 @@ bool MscWriter::ZipFileWriter::open(io::IODevice* device, const path_t& filePath
if (!m_device->isOpen()) {
if (!m_device->open(IODevice::WriteOnly)) {
LOGE() << "failed open file: " << filePath;
return false;
return make_ret(m_device->error(), m_device->errorString());
}
}
@ -289,6 +297,11 @@ bool MscWriter::ZipFileWriter::isOpened() const
return m_device ? m_device->isOpen() : false;
}
bool MscWriter::ZipFileWriter::hasError() const
{
return (m_device ? m_device->hasError() : false) || (m_zip ? m_zip->hasError() : false);
}
bool MscWriter::ZipFileWriter::addFileData(const String& fileName, const ByteArray& data)
{
IF_ASSERT_FAILED(m_zip) {
@ -300,32 +313,39 @@ bool MscWriter::ZipFileWriter::addFileData(const String& fileName, const ByteArr
LOGE() << "failed write files to zip";
return false;
}
return true;
}
bool MscWriter::DirWriter::open(io::IODevice* device, const io::path_t& filePath)
Ret MscWriter::DirWriter::open(io::IODevice* device, const io::path_t& filePath)
{
if (device) {
NOT_SUPPORTED;
m_hasError = true;
return false;
}
if (filePath.empty()) {
LOGE() << "file path is empty";
m_hasError = true;
return false;
}
m_rootPath = containerPath(filePath);
Dir dir(m_rootPath);
if (!dir.removeRecursively()) {
Ret ret = dir.removeRecursively();
if (!ret) {
LOGE() << "failed clear dir: " << dir.absolutePath();
return false;
m_hasError = true;
return ret;
}
if (!dir.mkpath(dir.absolutePath())) {
ret = dir.mkpath(dir.absolutePath());
if (!ret) {
LOGE() << "failed make path: " << dir.absolutePath();
return false;
m_hasError = true;
return ret;
}
return true;
@ -341,6 +361,11 @@ bool MscWriter::DirWriter::isOpened() const
return FileInfo::exists(m_rootPath);
}
bool MscWriter::DirWriter::hasError() const
{
return m_hasError;
}
bool MscWriter::DirWriter::addFileData(const String& fileName, const ByteArray& data)
{
io::path_t filePath = m_rootPath + "/" + fileName;
@ -349,6 +374,7 @@ bool MscWriter::DirWriter::addFileData(const String& fileName, const ByteArray&
if (!fileDir.exists()) {
if (!fileDir.mkpath(fileDir.absolutePath())) {
LOGE() << "failed make path: " << fileDir.absolutePath();
m_hasError = true;
return false;
}
}
@ -356,11 +382,13 @@ bool MscWriter::DirWriter::addFileData(const String& fileName, const ByteArray&
File file(filePath);
if (!file.open(IODevice::WriteOnly)) {
LOGE() << "failed open file: " << filePath;
m_hasError = true;
return false;
}
if (file.write(data) != data.size()) {
LOGE() << "failed write file: " << filePath;
m_hasError = true;
return false;
}
@ -375,7 +403,7 @@ MscWriter::XmlFileWriter::~XmlFileWriter()
}
}
bool MscWriter::XmlFileWriter::open(io::IODevice* device, const path_t& filePath)
Ret MscWriter::XmlFileWriter::open(io::IODevice* device, const path_t& filePath)
{
m_device = device;
if (!m_device) {
@ -386,7 +414,7 @@ bool MscWriter::XmlFileWriter::open(io::IODevice* device, const path_t& filePath
if (!m_device->isOpen()) {
if (!m_device->open(IODevice::WriteOnly)) {
LOGE() << "failed open file: " << filePath;
return false;
return make_ret(m_device->error(), m_device->errorString());
}
}
@ -413,6 +441,11 @@ bool MscWriter::XmlFileWriter::isOpened() const
return m_device ? m_device->isOpen() : false;
}
bool MscWriter::XmlFileWriter::hasError() const
{
return m_device ? m_device->hasError() : false;
}
bool MscWriter::XmlFileWriter::addFileData(const String& fileName, const ByteArray& data)
{
if (!m_stream) {

View File

@ -23,6 +23,7 @@
#define MU_ENGRAVING_MSCWRITER_H
#include "types/string.h"
#include "types/ret.h"
#include "io/path.h"
#include "io/iodevice.h"
#include "mscio.h"
@ -52,9 +53,10 @@ public:
void setParams(const Params& params);
const Params& params() const;
bool open();
Ret open();
void close();
bool isOpened() const;
bool hasError() const;
void writeStyleFile(const ByteArray& data);
void writeScoreFile(const ByteArray& data);
@ -72,18 +74,20 @@ private:
struct IWriter {
virtual ~IWriter() = default;
virtual bool open(io::IODevice* device, const io::path_t& filePath) = 0;
virtual Ret open(io::IODevice* device, const io::path_t& filePath) = 0;
virtual void close() = 0;
virtual bool isOpened() const = 0;
virtual bool hasError() const = 0;
virtual bool addFileData(const String& fileName, const ByteArray& data) = 0;
};
struct ZipFileWriter : public IWriter
{
~ZipFileWriter() override;
bool open(io::IODevice* device, const io::path_t& filePath) override;
Ret open(io::IODevice* device, const io::path_t& filePath) override;
void close() override;
bool isOpened() const override;
bool hasError() const override;
bool addFileData(const String& fileName, const ByteArray& data) override;
private:
@ -94,20 +98,23 @@ private:
struct DirWriter : public IWriter
{
bool open(io::IODevice* device, const io::path_t& filePath) override;
Ret open(io::IODevice* device, const io::path_t& filePath) override;
void close() override;
bool isOpened() const override;
bool hasError() const override;
bool addFileData(const String& fileName, const ByteArray& data) override;
private:
io::path_t m_rootPath;
bool m_hasError = false;
};
struct XmlFileWriter : public IWriter
{
~XmlFileWriter() override;
bool open(io::IODevice* device, const io::path_t& filePath) override;
Ret open(io::IODevice* device, const io::path_t& filePath) override;
void close() override;
bool isOpened() const override;
bool hasError() const override;
bool addFileData(const String& fileName, const ByteArray& data) override;
private:
io::IODevice* m_device = nullptr;
@ -135,6 +142,7 @@ private:
Params m_params;
mutable IWriter* m_writer = nullptr;
Meta m_meta;
bool m_hadError = false;
};
}

View File

@ -68,7 +68,7 @@ BeamTremoloLayout::BeamTremoloLayout(EngravingItem* e)
isGrace = m_trem->chord1()->isGrace();
}
m_element = e;
m_spatium = e->score()->spatium();
m_spatium = e->spatium();
m_tick = m_element->tick();
m_beamSpacing = e->score()->styleB(Sid::useWideBeams) ? 4 : 3;
m_beamDist = (m_beamSpacing / 4.0) * m_spatium * e->mag()
@ -546,9 +546,9 @@ bool BeamTremoloLayout::calculateAnchors(const std::vector<ChordRest*>& chordRes
int slant = computeDesiredSlant(startNote, endNote, middleLine, dictator, pointer);
bool isFlat = slant == 0;
int specialSlant = isFlat ? isSlopeConstrained(startNote, endNote) : -1;
bool forceFlat = specialSlant == 0;
bool smallSlant = specialSlant == 1;
SlopeConstraint specialSlant = isFlat ? getSlopeConstraint(startNote, endNote) : SlopeConstraint::NO_CONSTRAINT;
bool forceFlat = specialSlant == SlopeConstraint::FLAT;
bool smallSlant = specialSlant == SlopeConstraint::SMALL_SLOPE;
if (isFlat) {
dictator = m_up ? std::min(pointer, dictator) : std::max(pointer, dictator);
pointer = dictator;
@ -720,8 +720,12 @@ bool BeamTremoloLayout::calculateAnchorsCross()
minY = std::max(minY, chordBeamAnchorY(toChord(c)));
}
}
m_startAnchor.ry() = (maxY + minY) / 2;
m_endAnchor.ry() = (maxY + minY) / 2;
m_startAnchor.setX(chordBeamAnchorX(startCr, ChordBeamAnchorType::Start));
m_endAnchor.setX(chordBeamAnchorX(endCr, ChordBeamAnchorType::End));
m_slope = 0;
if (!noSlope()) {
@ -825,8 +829,6 @@ bool BeamTremoloLayout::calculateAnchorsCross()
// nothing needs to be done, the beam is already horizontal and placed nicely
}
}
m_startAnchor.setX(chordBeamAnchorX(startCr, ChordBeamAnchorType::Start));
m_endAnchor.setX(chordBeamAnchorX(endCr, ChordBeamAnchorType::End));
m_slope = (m_endAnchor.y() - m_startAnchor.y()) / (m_endAnchor.x() - m_startAnchor.x());
}
return true;
@ -874,10 +876,10 @@ int BeamTremoloLayout::computeDesiredSlant(int startNote, int endNote, int middl
if (startNote == endNote) {
return 0;
}
int slopeConstrained = isSlopeConstrained(startNote, endNote);
if (slopeConstrained == 0) {
SlopeConstraint slopeConstrained = getSlopeConstraint(startNote, endNote);
if (slopeConstrained == SlopeConstraint::FLAT) {
return 0;
} else if (slopeConstrained == 1) {
} else if (slopeConstrained == SlopeConstraint::SMALL_SLOPE) {
return dictator > pointer ? -1 : 1;
}
@ -889,77 +891,84 @@ int BeamTremoloLayout::computeDesiredSlant(int startNote, int endNote, int middl
return std::min(maxSlope, _maxSlopes[interval]) * (m_up ? 1 : -1);
}
int BeamTremoloLayout::isSlopeConstrained(int startNote, int endNote) const
SlopeConstraint BeamTremoloLayout::getSlopeConstraint(int startNote, int endNote) const
{
if (m_notes.empty()) {
return SlopeConstraint::NO_CONSTRAINT;
}
// 0 to constrain to flat, 1 to constrain to 0.25, <0 for no constraint
if (startNote == endNote) {
return 0;
return SlopeConstraint::FLAT;
} else if (m_beamType == BeamType::TREMOLO) {
// tremolos don't need the small slope constraint since they only have two notes
return SlopeConstraint::NO_CONSTRAINT;
}
// if a note is more extreme than the endpoints, slope is 0
// p.s. _notes is a sorted vector
if (m_notes.size() > 2) {
if (m_elements.size() > 2) {
if (m_up) {
int higherEnd = std::min(startNote, endNote);
if (higherEnd > m_notes[0]) {
return 0; // a note is higher in the staff than the highest end
return SlopeConstraint::FLAT; // a note is higher in the staff than the highest end
}
if (higherEnd == m_notes[0] && higherEnd >= m_notes[1]) {
if (higherEnd > m_notes[1]) {
return 0; // a note is higher in the staff than the highest end
return SlopeConstraint::FLAT; // a note is higher in the staff than the highest end
}
size_t chordCount = m_elements.size();
if (chordCount >= 3 && m_notes.size() >= 3) {
bool middleNoteHigherThanHigherEnd = higherEnd >= m_notes[2];
if (middleNoteHigherThanHigherEnd) {
return 0; // two notes are the same as the highest end (notes [0] [1] and [2] higher than or same as higherEnd)
return SlopeConstraint::FLAT; // two notes are the same as the highest end (notes [0] [1] and [2] higher than or same as higherEnd)
}
bool secondNoteSameHeightAsHigherEnd = startNote < endNote && m_elements[1]->isChord()
&& toChord(m_elements[1])->upLine() == higherEnd;
bool secondToLastNoteSameHeightAsHigherEnd = endNote < startNote && m_elements[chordCount - 2]->isChord() && toChord(
m_elements[chordCount - 2])->upLine() == higherEnd;
if (!(secondNoteSameHeightAsHigherEnd || secondToLastNoteSameHeightAsHigherEnd)) {
return 0; // only one note same as higher end, but it is not a neighbor
return SlopeConstraint::FLAT; // only one note same as higher end, but it is not a neighbor
} else {
// there is a single note next to the highest one with equivalent height
// and they are neighbors. this is our exception, so
// the slope may be a max of 0.25.
return 1;
return SlopeConstraint::SMALL_SLOPE;
}
} else {
return 0; // only two notes in entire beam, in this case startNote == endNote
return SlopeConstraint::FLAT; // only two notes in entire beam, in this case startNote == endNote
}
}
} else {
int lowerEnd = std::max(startNote, endNote);
if (lowerEnd < m_notes[m_notes.size() - 1]) {
return 0;
return SlopeConstraint::FLAT;
}
if (lowerEnd == m_notes[m_notes.size() - 1] && lowerEnd <= m_notes[m_notes.size() - 2]) {
if (lowerEnd < m_notes[m_notes.size() - 2]) {
return 0;
return SlopeConstraint::FLAT;
}
size_t chordCount = m_elements.size();
if (chordCount >= 3 && m_notes.size() >= 3) {
bool middleNoteLowerThanLowerEnd = lowerEnd <= m_notes[m_notes.size() - 3];
if (middleNoteLowerThanLowerEnd) {
return 0;
return SlopeConstraint::FLAT;
}
bool secondNoteSameHeightAsLowerEnd = startNote > endNote && m_elements[1]->isChord()
&& toChord(m_elements[1])->downLine() == lowerEnd;
bool secondToLastNoteSameHeightAsLowerEnd = endNote > startNote && m_elements[chordCount - 2]->isChord() && toChord(
m_elements[chordCount - 2])->downLine() == lowerEnd;
if (!(secondNoteSameHeightAsLowerEnd || secondToLastNoteSameHeightAsLowerEnd)) {
return 0;
return SlopeConstraint::FLAT;
} else {
return 1;
return SlopeConstraint::SMALL_SLOPE;
}
} else {
return 0;
return SlopeConstraint::FLAT;
}
}
}
}
return -1;
return SlopeConstraint::NO_CONSTRAINT;
}
int BeamTremoloLayout::getMaxSlope() const

View File

@ -36,6 +36,13 @@ enum class ActionIconType;
enum class SpannerSegmentType;
}
enum class SlopeConstraint
{
NO_CONSTRAINT,
FLAT,
SMALL_SLOPE,
};
namespace mu::engraving::layout::v0 {
class BeamTremoloLayout
{
@ -88,7 +95,7 @@ private:
int getMiddleStaffLine(ChordRest* startChord, ChordRest* endChord, int staffLines) const;
int computeDesiredSlant(int startNote, int endNote, int middleLine, int dictator, int pointer) const;
int isSlopeConstrained(int startNote, int endNote) const;
SlopeConstraint getSlopeConstraint(int startNote, int endNote) const;
void offsetBeamWithAnchorShortening(std::vector<ChordRest*> chordRests, int& dictator, int& pointer, int staffLines,
bool isStartDictator, int stemLengthDictator) const;
bool isValidBeamPosition(int yPos, bool isStart, bool isAscending, bool isFlat, int staffLines, bool isOuter) const;

View File

@ -703,6 +703,8 @@ void ChordLayout::layoutArticulations(Chord* item, LayoutContext& ctx)
const double minDist = ctx.style().styleMM(Sid::articulationMinDistance);
const ArticulationStemSideAlign articulationHAlign = ctx.style().styleV(Sid::articulationStemHAlign).value<ArticulationStemSideAlign>();
const bool keepArticsTogether = ctx.style().styleB(Sid::articulationKeepTogether);
const double stemSideDistance = ctx.style().styleMM(Sid::propertyDistanceStem);
int numCloseArtics = 0;
bool hasStaffArticsUp = false;
bool hasStaffArticsDown = false;
@ -777,8 +779,10 @@ void ChordLayout::layoutArticulations(Chord* item, LayoutContext& ctx)
} else {
x = item->centerX();
}
if (bottom) {
if (!headSide && item->stem()) {
double stemBottom = item->stem()->bbox().translated(item->stem()->pos()).bottom();
// Check if there's a hook, because the tip of the hook always extends slightly past the end of the stem
if (item->hook()) {
y = item->hook()->bbox().translated(item->hook()->pos()).bottom();
@ -790,13 +794,22 @@ void ChordLayout::layoutArticulations(Chord* item, LayoutContext& ctx)
if (line < lines && !(line % 2)) {
line += 1;
}
double dist = (line * _lineDist) - stemBottom;
bool hasBeam = item->beam() || (item->tremolo() && item->tremolo()->twoNotes());
if (line < lines && hasBeam && dist < stemSideDistance) {
// beams can give stems weird unpredictable lengths, so we should enforce min
// distance even inside the staff
// adjust downwards a space but stop at the bottom of the staff
line = std::min(line + 2, lines);
}
if (line < lines) { // align between staff lines
y = line * _lineDist;
y -= a->height() * .5;
} else if (line == lines) {
y = ctx.style().styleMM(Sid::propertyDistanceStem) + lines * _lineDist;
y = stemSideDistance + (lines * _lineDist);
} else {
y += ctx.style().styleMM(Sid::propertyDistanceStem);
y += stemSideDistance;
}
} else {
x = item->centerX();
@ -822,25 +835,36 @@ void ChordLayout::layoutArticulations(Chord* item, LayoutContext& ctx)
}
}
// center symbol
} else {
} else { // topside
if (!headSide && item->stem()) {
double stemTop = item->stem()->bbox().translated(item->stem()->pos()).top();
// Check if there's a hook, because the tip of the hook always extends slightly past the end of the stem
if (item->hook()) {
y = item->hook()->bbox().translated(item->hook()->pos()).top();
} else {
y = item->stem()->bbox().translated(item->stem()->pos()).top();
}
int line = round((y - _lineDist) / _lineDist);
int line = round((y - _lineDist) / _lineDist);
if (line > 0 && !(line % 2)) {
line -= 1;
}
double dist = stemTop - (line * _lineDist);
bool hasBeam = item->beam() || (item->tremolo() && item->tremolo()->twoNotes());
if (line > 0 && hasBeam && dist < stemSideDistance) {
// beams can give stems weird unpredictable lengths, so we should enforce min
// distance even inside the staff
// adjust upwards a space but stop at the top of the staff
line = std::max(line - 2, 0);
}
if (line > 0) { // align between staff lines
y = line * _lineDist;
y += a->height() * .5;
} else if (line == 0) {
y = -ctx.style().styleMM(Sid::propertyDistanceStem);
y = -stemSideDistance;
} else {
y -= ctx.style().styleMM(Sid::propertyDistanceStem);
y -= stemSideDistance;
}
} else {
x = item->centerX();
@ -1278,7 +1302,8 @@ void ChordLayout::computeUp(Chord* item, LayoutContext& ctx)
Chord* c1 = item->_tremolo->chord1();
Chord* c2 = item->_tremolo->chord2();
bool cross = c1->staffMove() != c2->staffMove();
if (cross && item == c1) {
if (item == c1) {
// we have to lay out the tremolo because it hasn't been laid out at all yet, and we need its direction
TLayout::layout(item->_tremolo, ctx);
}
Measure* measure = item->findMeasure();
@ -2136,6 +2161,102 @@ static std::pair<double, double> layoutAccidental(const MStyle& style, AcEl* me,
return std::pair<double, double>(me->x, me->x + me->width);
}
//---------------------------------------------------------
// placeDots
//---------------------------------------------------------
void ChordLayout::placeDots(const std::vector<Chord*>& chords, const std::vector<Note*>& notes)
{
Chord* chord = nullptr;
for (Chord* c : chords) {
if (c->dots() > 0) {
chord = c;
break;
} else {
for (Note* note : c->notes()) {
note->setDotRelativeLine(0); // this manages the deletion of dots
}
}
}
if (!chord || chord->staff()->isTabStaff(chord->tick())) {
return;
}
std::vector<Note*> topDownNotes;
std::vector<Note*> bottomUpNotes;
std::vector<int> anchoredDots;
// construct combined chords using the notes from overlapping chords
getNoteListForDots(chord, topDownNotes, bottomUpNotes, anchoredDots);
for (Note* note : notes) {
bool onLine = !(note->line() & 1);
if (onLine) {
std::unordered_map<int, Note*> alreadyAdded;
bool finished = false;
for (Note* otherNote : bottomUpNotes) {
int dotMove = otherNote->dotPosition() == DirectionV::UP ? -1 : 1;
int otherDotLoc = otherNote->line() + dotMove;
bool added = alreadyAdded.count(otherDotLoc);
if (!added && mu::contains(anchoredDots, otherDotLoc)) {
dotMove = -dotMove; // if the desired space is taken, adjust opposite
} else if (added && alreadyAdded[otherDotLoc] != otherNote) {
dotMove = -dotMove;
}
// set y for this note
if (note == otherNote) {
note->setDotRelativeLine(dotMove);
finished = true;
anchoredDots.push_back(note->line() + dotMove);
alreadyAdded[otherNote->line() + dotMove] = otherNote;
break;
}
}
if (!finished) {
alreadyAdded.clear();
for (Note* otherNote : topDownNotes) {
int dotMove = otherNote->dotPosition() == DirectionV::DOWN ? 1 : -1;
int otherDotLoc = otherNote->line() + dotMove;
bool added = alreadyAdded.count(otherDotLoc);
if (!added && mu::contains(anchoredDots, otherDotLoc)) {
dotMove = -dotMove;
} else if (added && alreadyAdded[otherDotLoc] != otherNote) {
dotMove = -dotMove;
}
// set y for this note
if (note == otherNote) {
note->setDotRelativeLine(dotMove);
finished = true;
anchoredDots.push_back(note->line() + dotMove);
break;
}
if (!added) {
alreadyAdded[otherNote->line() + dotMove] = otherNote;
}
}
}
IF_ASSERT_FAILED(finished)
{
// this should never happen
// the note is on a line and topDownNotes and bottomUpNotes are all of the lined notes
note->setDotRelativeLine(0);
}
} else {
// on a space; usually this means the dot is on this same line, but there is an exception
// for a unison within the same chord.
for (Note* otherNote : note->chord()->notes()) {
if (note == otherNote) {
note->setDotRelativeLine(0); // same space as notehead
break;
}
if (note->line() == otherNote->line()) {
bool adjustDown = (note->chord()->voice() & 1) && !note->chord()->up();
note->setDotRelativeLine(adjustDown ? 2 : -2);
break;
}
}
}
}
}
//---------------------------------------------------------
// layoutChords3
// - calculate positions of notes, accidentals, dots
@ -2275,6 +2396,11 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
double xx = x + note->headBodyWidth() + chord->pos().x();
//---------------------------------------------------
// layout dots simply
// we will check for conflicts after all the notes have been processed
//---------------------------------------------------
DirectionV dotPosition = note->userDotPosition();
if (chord->dots()) {
if (chord->up()) {
@ -2287,12 +2413,12 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
// resolve dot conflicts
int line = note->line();
Note* above = (i < nNotes - 1) ? notes[i + 1] : 0;
if (above && (!above->visible() || above->dotsHidden())) {
if (above && (!above->visible() || above->dotsHidden() || above->chord()->dots() == 0)) {
above = 0;
}
int intervalAbove = above ? line - above->line() : 1000;
Note* below = (i > 0) ? notes[i - 1] : 0;
if (below && (!below->visible() || below->dotsHidden())) {
if (below && (!below->visible() || below->dotsHidden() || below->chord()->dots() == 0)) {
below = 0;
}
int intervalBelow = below ? below->line() - line : 1000;
@ -2302,22 +2428,19 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
dotPosition = DirectionV::DOWN;
} else if (intervalBelow == 1 && intervalAbove != 1) {
dotPosition = DirectionV::UP;
} else if (intervalAbove == 0 && above->chord()->dots()) {
} else if (intervalAbove == 0 || intervalBelow == 0) {
// unison
if (((above->voice() & 1) == (note->voice() & 1))) {
above->setDotY(DirectionV::UP);
dotPosition = DirectionV::DOWN;
}
dotPosition = DirectionV::AUTO; // unison conflicts taken care of later
}
} else {
// space
if (intervalAbove == 0 && above->chord()->dots()) {
// unison
if (!(note->voice() & 1)) {
dotPosition = DirectionV::UP;
dotPosition = DirectionV::UP; // space, doesn't matter
} else {
if (!(above->voice() & 1)) {
above->setDotY(DirectionV::UP);
above->setDotPosition(DirectionV::UP);
} else {
dotPosition = DirectionV::DOWN;
}
@ -2326,8 +2449,13 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
}
}
}
note->setDotY(dotPosition); // also removes invalid dots
if (dotPosition == DirectionV::AUTO) {
dotPosition = note->voice() & 1 ? DirectionV::DOWN : DirectionV::UP;
}
note->setDotPosition(dotPosition);
}
// Now, we can resolve note conflicts as a superchord
placeDots(chords, notes);
// if there are no non-mirrored notes in a downstem chord,
// then use the stem X position as X origin for accidental layout
@ -2563,6 +2691,70 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
}
}
//---------------------------------------------------------
// getNoteListForDots
// This method populates three lists: one for chord notes that need to be checked from the top down,
// one for chords from the bottom up, and one for spaces (where the dot will be in that space)
//---------------------------------------------------------
void ChordLayout::getNoteListForDots(Chord* c, std::vector<Note*>& topDownNotes, std::vector<Note*>& bottomUpNotes,
std::vector<int>& anchoredDots)
{
bool hasVoices = c->measure()->hasVoices(c->staffIdx(), c->tick(), c->ticks());
if (!hasVoices) {
// only this voice, so topDownNotes is just the notes in the chord
for (Note* note : c->notes()) {
if (note->line() & 1) {
int newOffset = 0;
bool adjustDown = (c->voice() & 1) && !c->up();
if (!anchoredDots.empty() && anchoredDots.back() == note->line()) {
if (anchoredDots.size() >= 2 && anchoredDots[anchoredDots.size() - 2] == note->line() + (adjustDown ? 2 : -2)) {
newOffset = adjustDown ? -2 : 2;
} else {
newOffset = adjustDown ? 2 : -2;
}
}
anchoredDots.push_back(note->line() + newOffset);
} else {
topDownNotes.push_back(note);
}
}
} else {
// Get a list of notes in this staff that adjust dots from top down,
// bottom up, and also start our locked-in dot list by adding all lines where dots are
// guaranteed
Measure* m = c->measure();
size_t firstVoice = c->track() - c->voice();
for (size_t i = firstVoice; i < firstVoice + VOICES; ++i) {
if (Chord* voiceChord = m->findChord(c->tick(), i)) {
bool startFromTop = !((voiceChord->voice() & 1) && !voiceChord->up());
if (startFromTop) {
for (Note* note : voiceChord->notes()) {
if (note->line() & 1) {
anchoredDots.push_back(note->line());
} else {
topDownNotes.push_back(note);
}
}
} else {
for (Note* note : voiceChord->notes()) {
if (note->line() & 1) {
anchoredDots.push_back(note->line());
} else {
bottomUpNotes.push_back(note);
}
}
}
}
}
}
// our two lists now contain only notes that are on lines
std::sort(topDownNotes.begin(), topDownNotes.end(),
[](Note* n1, Note* n2) { return n1->line() < n2->line(); });
std::sort(bottomUpNotes.begin(), bottomUpNotes.end(),
[](Note* n1, Note* n2) { return n1->line() > n2->line(); });
}
/* updateGraceNotes()
* Processes a full measure, making sure that all grace notes are
* attacched to the correct segment. Has to be performed after
@ -2779,10 +2971,15 @@ void ChordLayout::resolveRestVSChord(std::vector<Rest*>& rests, std::vector<Chor
bool ignoreYOffset = (restAbove && restYOffset > 0) || (!restAbove && restYOffset < 0);
PointF offset = ignoreYOffset ? PointF(0, restYOffset) : PointF(0, 0);
Shape chordShape = chord->shape().translated(chord->pos());
chordShape.removeInvisibles();
if (chordShape.empty()) {
continue;
}
double clearance = 0.0;
Shape restShape = rest->shape().translated(rest->pos() - offset);
if (chord->segment() == rest->segment()) {
Shape chordShape = chord->shape().translated(chord->pos());
clearance = restAbove ? restShape.verticalClearance(chordShape) : chordShape.verticalClearance(restShape);
} else {
Note* limitNote = restAbove ? chord->upNote() : chord->downNote();
@ -3078,8 +3275,8 @@ void ChordLayout::layoutNote2(Note* item, LayoutContext& ctx)
if (isTabStaff && staffType->stemThrough()) {
// with TAB's, dot Y is not calculated during layoutChords3(),
// as layoutChords3() is not even called for TAB's;
// setDotY() actually also manages creation/deletion of NoteDot's
item->setDotY(DirectionV::AUTO);
// setDotRelativeLine() actually also manages creation/deletion of NoteDot's
item->setDotRelativeLine(0);
// use TAB default note-to-dot spacing
dd = STAFFTYPE_TAB_DEFAULTDOTDIST_X * item->spatium();
@ -3162,11 +3359,7 @@ void ChordLayout::checkStartEndSlurs(Chord* chord, LayoutContext& ctx)
if (!slur->endChord()) {
continue;
}
std::vector<Spanner*>& endingSp = slur->endChord()->endingSpanners();
if (std::find(endingSp.begin(), endingSp.end(), slur) == endingSp.end()) {
// Slur not added. Add it now.
endingSp.push_back(slur);
}
slur->endChord()->addEndingSpanner(slur);
}
for (Spanner* spanner : chord->_endingSpanners) {
if (!spanner->isSlur()) {

View File

@ -63,6 +63,7 @@ public:
static void layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_t staffIdx);
static double layoutChords2(std::vector<Note*>& notes, bool up, LayoutContext& ctx);
static void layoutChords3(const MStyle& style, const std::vector<Chord*>&, const std::vector<Note*>&, const Staff*, LayoutContext& ctx);
static void getNoteListForDots(Chord* c, std::vector<Note*>&, std::vector<Note*>&, std::vector<int>&);
static void updateGraceNotes(Measure* measure, LayoutContext& ctx);
static void repositionGraceNotesAfter(Segment* segment, size_t tracks);
static void appendGraceNotes(Chord* chord);
@ -83,6 +84,8 @@ private:
static void layoutTablature(Chord* item, LayoutContext& ctx);
static void layoutNote2(Note* note, LayoutContext& ctx);
static void placeDots(const std::vector<Chord*>& chords, const std::vector<Note*>& notes);
};
}

View File

@ -1533,7 +1533,12 @@ void TLayout::layout(Expression* item, LayoutContext& ctx)
item->setSnappedDynamic(nullptr);
if (!item->autoplace() || !item->snapToDynamics()) {
if (!item->autoplace()) {
return;
}
if (!item->snapToDynamics()) {
item->autoplaceSegmentElement();
return;
}
@ -2395,7 +2400,9 @@ void TLayout::layout(HairpinSegment* item, LayoutContext& ctx)
Dynamic* sd = nullptr;
Dynamic* ed = nullptr;
double dymax = item->hairpin()->placeBelow() ? -10000.0 : 10000.0;
if (item->autoplace() && !ctx.isPaletteMode()) {
if (item->autoplace() && !ctx.isPaletteMode()
&& item->explicitParent() // TODO: remove this line (this might happen when Ctrl+Shift+Dragging an item)
) {
Segment* start = item->hairpin()->startSegment();
Segment* end = item->hairpin()->endSegment();
// Try to fit between adjacent dynamics
@ -2552,6 +2559,15 @@ void TLayout::layout(HairpinSegment* item, LayoutContext& ctx)
item->pointsRef()[3] = l2.p2();
item->npointsRef() = 4;
item->polygonRef().clear();
if (item->spannerSegmentType() != SpannerSegmentType::MIDDLE) {
if (type == HairpinType::DECRESC_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::BEGIN) {
item->polygonRef() << item->pointsRef()[0] << item->pointsRef()[1] << item->pointsRef()[2]; // [top-left, joint, bottom-left]
} else if (type == HairpinType::CRESC_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::END) {
item->polygonRef() << item->pointsRef()[1] << item->pointsRef()[0] << item->pointsRef()[3]; // [top-right, joint, bottom-right]
}
}
RectF r = RectF(l1.p1(), l1.p2()).normalized().united(RectF(l2.p1(), l2.p2()).normalized());
if (!item->text()->empty()) {
r.unite(item->text()->bbox());
@ -2658,8 +2674,10 @@ void TLayout::layout(HairpinSegment* item, LayoutContext& ctx)
}
if (ed->ipos().y() != ny) {
ed->setPosY(ny);
if (ed->snappedExpression()) {
ed->snappedExpression()->setPosY(ny);
Expression* snappedExpression = ed->snappedExpression();
if (snappedExpression) {
double yOffsetDiff = snappedExpression->offset().y() - ed->offset().y();
snappedExpression->setPosY(ny - yOffsetDiff);
}
if (ed->addToSkyline()) {
Segment* s = ed->segment();
@ -2718,7 +2736,6 @@ void TLayout::layout(Harmony* item, LayoutContext& ctx)
// setOffset(propertyDefault(Pid::OFFSET).value<PointF>());
layout1(item, ctx);
item->setPos(calculateBoundingRect(item, ctx));
}
void TLayout::layout1(Harmony* item, LayoutContext& ctx)
@ -2731,13 +2748,14 @@ void TLayout::layout1(Harmony* item, LayoutContext& ctx)
item->textBlockList().push_back(TextBlock());
}
calculateBoundingRect(item, ctx);
auto positionPoint = calculateBoundingRect(item, ctx);
if (item->hasFrame()) {
item->layoutFrame();
}
ctx.addRefresh(item->canvasBoundingRect());
item->setPos(positionPoint);
}
PointF TLayout::calculateBoundingRect(Harmony* item, LayoutContext& ctx)
@ -3593,6 +3611,10 @@ void TLayout::layout(Ornament* item, LayoutContext& ctx)
Chord* parentChord = toChord(item->parentItem());
Chord* cueNoteChord = item->cueNoteChord();
if (!cueNoteChord) {
return;
}
Note* cueNote = cueNoteChord->notes().front();
ChordLayout::layoutChords3(ctx.style(), { cueNoteChord }, { cueNote }, item->staff(), ctx);
layout(cueNoteChord, ctx);
@ -4194,13 +4216,14 @@ void TLayout::layout(StemSlash* item, LayoutContext& ctx)
static constexpr double heightReduction = 0.66;
static constexpr double angleIncrease = 1.2;
static constexpr double lengthIncrease = 1.1;
const double mag = c->mag();
double up = c->up() ? -1 : 1;
double stemTipY = c->up() ? stem->bbox().translated(stem->pos()).top() : stem->bbox().translated(stem->pos()).bottom();
double leftHang = ctx.noteHeadWidth() * ctx.style().styleD(Sid::graceNoteMag) / 2;
double leftHang = ctx.noteHeadWidth() * mag / 2;
double angle = ctx.style().styleD(Sid::stemSlashAngle) * M_PI / 180; // converting to radians
bool straight = ctx.style().styleB(Sid::useStraightNoteFlags);
double graceNoteMag = ctx.style().styleD(Sid::graceNoteMag);
double graceNoteMag = mag;
double startX = stem->bbox().translated(stem->pos()).right() - leftHang;

View File

@ -1111,20 +1111,22 @@ int Chord::calcMinStemLength()
int outSidePadding = score()->styleMM(Sid::tremoloOutSidePadding).val() / _spatium * 4.0;
int noteSidePadding = score()->styleMM(Sid::tremoloNoteSidePadding).val() / _spatium * 4.0;
Note* lineNote = m_up ? upNote() : downNote();
if (lineNote->line() == INVALID_LINE) {
lineNote->updateLine();
}
int line = lineNote->line();
line *= 2; // convert to quarter spaces
int outsideStaffOffset = 0;
if (!m_up && line < -2) {
outsideStaffOffset = -line;
} else if (m_up && line > staff()->lines(tick()) * 4) {
outsideStaffOffset = line - (staff()->lines(tick()) * 4) + 4;
}
if (!staff()->isTabStaff(tick())) {
Note* lineNote = m_up ? upNote() : downNote();
if (lineNote->line() == INVALID_LINE) {
lineNote->updateLine();
}
int line = lineNote->line();
line *= 2; // convert to quarter spaces
if (!m_up && line < -2) {
outsideStaffOffset = -line;
} else if (m_up && line > staff()->lines(tick()) * 4) {
outsideStaffOffset = line - (staff()->lines(tick()) * 4) + 4;
}
}
minStemLength += (outSidePadding + std::max(noteSidePadding, outsideStaffOffset));
if (_hook) {

View File

@ -272,6 +272,13 @@ public:
Ornament* findOrnament() const;
const std::set<Spanner*>& startingSpanners() const { return _startingSpanners; }
const std::set<Spanner*>& endingSpanners() const { return _endingSpanners; }
void addStartingSpanner(Spanner* spanner) { _startingSpanners.insert(spanner); }
void removeStartingSpanner(Spanner* spanner) { _startingSpanners.erase(spanner); }
void addEndingSpanner(Spanner* spanner) { _endingSpanners.insert(spanner); }
void removeEndingSpanner(Spanner* spanner) { _endingSpanners.erase(spanner); }
private:
friend class Factory;
@ -346,7 +353,8 @@ private:
}
} _startEndSlurs;
// StartEndSlurs& startEndSlurs() { return _startEndSlurs; }
std::set<Spanner*> _startingSpanners; ///< spanners starting on this item
std::set<Spanner*> _endingSpanners; ///< spanners ending on this item
bool _allowKerningAbove = true;
bool _allowKerningBelow = true;

View File

@ -198,12 +198,11 @@ std::vector<PointF> ChordLine::gripsPositions(const EditData&) const
return {};
}
double sp = spatium();
auto n = m_path.elementCount();
PointF cp(pagePos());
if (m_straight) {
// limit the number of grips to one
double offset = 0.5 * sp;
double offset = 0.5;
PointF p;
if (m_chordLineType == ChordLineType::FALL) {
@ -217,7 +216,7 @@ std::vector<PointF> ChordLine::gripsPositions(const EditData&) const
}
// translate on the length and height - stops the grips from going past boundaries of slide
p += (cp + PointF(m_path.elementAt(1).x * sp, m_path.elementAt(1).y * sp));
p += (cp + PointF(m_path.elementAt(1).x, m_path.elementAt(1).y));
return { p };
} else {
std::vector<PointF> grips(n);

View File

@ -400,6 +400,7 @@ EngravingItem* ChordRest::drop(EditData& data)
delete ks;
} else {
// apply to all staves, at the beginning of the measure
data.pos = canvasPos(); // measure->drop() expects to receive canvas pos
return m->drop(data);
}
}
@ -923,6 +924,7 @@ EngravingItem* ChordRest::nextElement()
}
switch (e->type()) {
case ElementType::ARTICULATION:
case ElementType::ORNAMENT:
case ElementType::LYRICS: {
EngravingItem* next = nextArticulationOrLyric(e);
if (next) {
@ -957,6 +959,7 @@ EngravingItem* ChordRest::prevElement()
}
switch (e->type()) {
case ElementType::ARTICULATION:
case ElementType::ORNAMENT:
case ElementType::LYRICS: {
EngravingItem* prev = prevArticulationOrLyric(e);
if (prev) {

View File

@ -35,6 +35,7 @@
#include "chordline.h"
#include "clef.h"
#include "excerpt.h"
#include "expression.h"
#include "factory.h"
#include "fingering.h"
#include "glissando.h"
@ -736,9 +737,7 @@ TextBase* Score::addText(TextStyleType type, EngravingItem* destinationElement,
if (!chordRest) {
break;
}
textBox = Factory::createStaffText(dummy()->segment(), TextStyleType::EXPRESSION);
textBox->setPlacement(PlacementV::BELOW);
textBox->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::UNSTYLED);
textBox = Factory::createExpression(dummy()->segment());
chordRest->undoAddAnnotation(textBox);
break;
}
@ -3374,14 +3373,15 @@ ChordRest* Score::deleteRange(Segment* s1, Segment* s2, track_idx_t track1, trac
void Score::cmdDeleteSelection()
{
ChordRest* cr = 0; // select something after deleting notes
ChordRest* crSelectedAfterDeletion = 0; // select something after deleting notes
if (selection().isRange()) {
Segment* s1 = selection().startSegment();
Segment* s2 = selection().endSegment();
const Fraction stick1 = selection().tickStart();
const Fraction stick2 = selection().tickEnd();
cr = deleteRange(s1, s2, staff2track(selection().staffStart()), staff2track(selection().staffEnd()), selectionFilter());
crSelectedAfterDeletion = deleteRange(s1, s2, staff2track(selection().staffStart()),
staff2track(selection().staffEnd()), selectionFilter());
s1 = tick2segment(stick1);
s2 = tick2segment(stick2, true);
if (s1 == 0 || s2 == 0) {
@ -3403,7 +3403,6 @@ void Score::cmdDeleteSelection()
// so we don't try to delete them twice if they are also in selection
std::set<Spanner*> deletedSpanners;
bool allertInstrChange = true;
for (EngravingItem* e : el) {
// these are the linked elements we are about to delete
std::list<EngravingObject*> links;
@ -3416,7 +3415,7 @@ void Score::cmdDeleteSelection()
// or of spanner or parent if that is more valid
Fraction tick = { -1, 1 };
track_idx_t track = mu::nidx;
if (!cr) {
if (!crSelectedAfterDeletion) {
if (e->isNote()) {
tick = toNote(e)->chord()->tick();
} else if (e->isRest() || e->isMMRest()) {
@ -3449,30 +3448,31 @@ void Score::cmdDeleteSelection()
//else tick < 0
track = e->track();
}
// find element to select
if (!cr && tick >= Fraction(0, 1) && track != mu::nidx) {
cr = findCR(tick, track);
}
bool needFindCR = !crSelectedAfterDeletion && tick >= Fraction(0, 1) && track != mu::nidx;
// We should not allow deleting the very first keySig of the piece, because it is
// logically incorrect and leads to a state of undefined key/transposition.
// Also instrument change key signatures should be undeletable.
// The correct action is for the user to set an atonal/custom keySig as needed.
if (e->isKeySig()) {
if (e->tick() == Fraction(0, 1)) {
continue;
} else if (toKeySig(e)->forInstrumentChange()) {
if (allertInstrChange) {
MessageBox::warning(mtrc("engraving", "Instrument change key signature cannot be deleted").toStdString(),
mtrc("engraving",
"Please replace it with a key signature from the palettes instead.").toStdString(),
{ MessageBox::Ok });
allertInstrChange = false;
if (e->tick() == Fraction(0, 1) || toKeySig(e)->forInstrumentChange()) {
MScore::setError(MsError::CANNOT_REMOVE_KEY_SIG);
if (needFindCR) {
crSelectedAfterDeletion = findCR(tick, track);
}
continue;
}
}
// Don't allow deleting the trill cue note
if (e->isNote() && toNote(e)->isTrillCueNote()) {
if (needFindCR) {
crSelectedAfterDeletion = findCR(tick, track);
}
continue;
}
// delete element if we have not done so already
if (deletedElements.find(e) == deletedElements.end()) {
// do not delete two spanner segments from the same spanner
@ -3494,6 +3494,11 @@ void Score::cmdDeleteSelection()
}
deleteItem(e);
}
if (needFindCR) {
crSelectedAfterDeletion = findCR(tick, track);
}
// add these linked elements to list of already-deleted elements
for (EngravingObject* se : links) {
deletedElements.insert(se);
@ -3504,17 +3509,17 @@ void Score::cmdDeleteSelection()
deselectAll();
// make new selection if appropriate
if (noteEntryMode()) {
if (cr) {
_is.setSegment(cr->segment());
if (crSelectedAfterDeletion) {
_is.setSegment(crSelectedAfterDeletion->segment());
} else {
cr = _is.cr();
crSelectedAfterDeletion = _is.cr();
}
}
if (cr) {
if (cr->isChord()) {
select(toChord(cr)->upNote(), SelectType::SINGLE);
if (crSelectedAfterDeletion) {
if (crSelectedAfterDeletion->isChord()) {
select(toChord(crSelectedAfterDeletion)->upNote(), SelectType::SINGLE);
} else {
select(cr, SelectType::SINGLE);
select(crSelectedAfterDeletion, SelectType::SINGLE);
}
}
}
@ -4975,6 +4980,7 @@ void Score::undoChangeFretting(Note* note, int pitch, int string, int fret, int
void Score::undoChangeKeySig(Staff* ostaff, const Fraction& tick, KeySigEvent key)
{
KeySig* lks = 0;
bool needsUpdate = false;
for (Staff* staff : ostaff->staffList()) {
if (staff->isDrumStaff(tick)) {
@ -4995,12 +5001,14 @@ void Score::undoChangeKeySig(Staff* ostaff, const Fraction& tick, KeySigEvent ke
KeySig* ks = toKeySig(s->element(track));
Interval interval = staff->part()->instrument(tick)->transpose();
Interval oldStaffInterval = staff->transpose(tick);
KeySigEvent nkey = key;
bool concertPitch = score->styleB(Sid::concertPitch);
if (interval.chromatic && !concertPitch && !nkey.isAtonal()) {
interval.flip();
nkey.setKey(transposeKey(key.concertKey(), interval, staff->part()->preferSharpFlat()));
interval.flip();
}
updateInstrumentChangeTranspositions(key, staff, tick);
@ -5019,6 +5027,13 @@ void Score::undoChangeKeySig(Staff* ostaff, const Fraction& tick, KeySigEvent ke
lks = nks;
}
}
if (interval != staff->transpose(tick) || interval != oldStaffInterval) {
needsUpdate = true;
}
}
if (needsUpdate) {
Fraction tickEnd = Fraction::fromTicks(ostaff->keyList()->nextKeyTick(tick.ticks()));
transpositionChanged(ostaff->part(), ostaff->transpose(tick), tick, tickEnd);
}
}
@ -6300,8 +6315,6 @@ void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
SegmentType segmentType = SegmentType::ChordRest;
Tuplet* crTuplet = cr->tuplet();
// For linked staves the length of staffList is always > 1 since the list contains the staff itself too!
const bool linked = ostaff->staffList().size() > 1;
@ -6370,41 +6383,26 @@ void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
}
}
#endif
if (crTuplet && staff != ostaff) {
// In case of nested tuplets, get the parent tuplet.
Tuplet* parTuplet { nullptr };
if (crTuplet->tuplet()) {
// Look for a tuplet, linked to the parent tuplet of crTuplet but
// which is on the same staff as the new ChordRest.
for (auto e : crTuplet->tuplet()->linkList()) {
Tuplet* t = toTuplet(e);
if (t->staff() == newcr->staff()) {
parTuplet = t;
break;
}
}
// Climb up the (possibly nested) tuplets from this chordRest
// Make sure all tuplets are cloned and correctly nested
DurationElement* elementBelow = cr;
Tuplet* tupletAbove = elementBelow->tuplet();
while (tupletAbove) {
DurationElement* linkedElementBelow = (DurationElement*)elementBelow->findLinkedInScore(score);
if (!linkedElementBelow) { // shouldn't happen
break;
}
// Look for a tuplet linked to crTuplet but is on the same staff as
// the new ChordRest. Create a new tuplet if not found.
Tuplet* newTuplet { nullptr };
for (auto e : crTuplet->linkList()) {
Tuplet* t = toTuplet(e);
if (t->staff() == newcr->staff()) {
newTuplet = t;
break;
}
Tuplet* linkedTuplet = (Tuplet*)tupletAbove->findLinkedInScore(score);
if (!linkedTuplet) {
linkedTuplet = toTuplet(tupletAbove->linkedClone());
linkedTuplet->setScore(score);
linkedTuplet->setTrack(newcr->track());
linkedTuplet->setParent(m);
}
linkedElementBelow->setTuplet(linkedTuplet);
if (!newTuplet) {
newTuplet = toTuplet(crTuplet->linkedClone());
newTuplet->setTuplet(parTuplet);
newTuplet->setScore(score);
newTuplet->setTrack(newcr->track());
newTuplet->setParent(m);
}
newcr->setTuplet(newTuplet);
elementBelow = tupletAbove;
tupletAbove = tupletAbove->tuplet();
}
if (newcr->isRest() && (toRest(newcr)->isGap()) && !(toRest(newcr)->track() % VOICES)) {

View File

@ -154,9 +154,6 @@ class EngravingItem : public EngravingObject
bool m_colorsInversionEnabled = true;
std::vector<Spanner*> _startingSpanners; ///< spanners starting on this item
std::vector<Spanner*> _endingSpanners; ///< spanners ending on this item
protected:
mutable int _z;
mu::draw::Color _color; ///< element color attribute
@ -551,9 +548,6 @@ public:
std::pair<int, float> barbeat() const;
std::vector<Spanner*>& startingSpanners() { return _startingSpanners; }
std::vector<Spanner*>& endingSpanners() { return _endingSpanners; }
private:
#ifndef ENGRAVING_NO_ACCESSIBILITY
void doInitAccessible();

View File

@ -656,6 +656,12 @@ void Excerpt::cloneSpanner(Spanner* s, Score* score, track_idx_t dstTrack, track
if (!ns->endElement()) {
LOGD("clone Slur: no end element");
}
} else if (ns->isTrill()) {
EngravingItem* startElement = (EngravingItem*)s->startElement()->findLinkedInScore(score);
if (startElement && startElement->isChord()) {
ns->setStartElement(startElement);
toChord(startElement)->addStartingSpanner(ns);
}
}
if (!ns->startElement() || !ns->endElement()) {

View File

@ -93,23 +93,25 @@ void Expression::undoChangeProperty(Pid id, const PropertyValue& v, PropertyFlag
bool Expression::acceptDrop(EditData& ed) const
{
return ed.dropElement->type() == ElementType::DYNAMIC;
return ed.dropElement->type() == ElementType::DYNAMIC || TextBase::acceptDrop(ed);
}
EngravingItem* Expression::drop(EditData& ed)
{
EngravingItem* item = ed.dropElement;
if (!item->isDynamic()) {
return nullptr;
if (item->isDynamic()) {
if (m_snappedDynamic) {
return m_snappedDynamic->drop(ed);
}
item->setTrack(track());
item->setParent(segment());
score()->undoAddElement(item);
item->undoChangeProperty(Pid::PLACEMENT, placement(), PropertyFlags::UNSTYLED);
return item;
}
if (m_snappedDynamic) {
return m_snappedDynamic->drop(ed);
}
item->setTrack(track());
item->setParent(segment());
score()->undoAddElement(item);
item->undoChangeProperty(Pid::PLACEMENT, placement(), PropertyFlags::UNSTYLED);
return item;
return TextBase::drop(ed);
}
PropertyValue Expression::getProperty(Pid propertyId) const

View File

@ -712,3 +712,5 @@ PlayTechAnnotation* Factory::createPlayTechAnnotation(Segment * parent, PlayingT
return annotation;
}
CREATE_ITEM_IMPL(Capo, ElementType::CAPO, Segment, isAccessibleEnabled)

View File

@ -268,6 +268,8 @@ public:
static PlayTechAnnotation* createPlayTechAnnotation(Segment* parent, PlayingTechniqueType techniqueType, TextStyleType styleType,
bool isAccessibleEnabled = true);
static Capo* createCapo(Segment* parent, bool isAccessibleEnabled = true);
private:
static EngravingItem* doCreateItem(ElementType type, EngravingItem* parent);
};

View File

@ -504,23 +504,25 @@ PropertyValue Hairpin::propertyDefault(Pid id) const
case Pid::BEGIN_TEXT:
if (_hairpinType == HairpinType::CRESC_LINE) {
return String(u"cresc.");
return score()->styleV(Sid::hairpinCrescText);
}
if (_hairpinType == HairpinType::DECRESC_LINE) {
return String(u"dim.");
return score()->styleV(Sid::hairpinDecrescText);
}
return String();
case Pid::CONTINUE_TEXT:
case Pid::END_TEXT:
if (_hairpinType == HairpinType::CRESC_LINE) {
return String(u"(cresc.)");
return score()->styleV(Sid::hairpinCrescContText);
}
if (_hairpinType == HairpinType::DECRESC_LINE) {
return String(u"(dim.)");
return score()->styleV(Sid::hairpinDecrescContText);
}
return String();
case Pid::END_TEXT:
return String();
case Pid::BEGIN_TEXT_PLACE:
case Pid::CONTINUE_TEXT_PLACE:
return TextPlace::LEFT;

View File

@ -175,7 +175,7 @@ String HarpPedalDiagram::createDiagramText()
PedalPosition::UNSET, PedalPosition::UNSET };
std::array<PedalPosition, HARP_STRING_NO> prevState;
HarpPedalDiagram* prevDiagram = nullptr;
if (part()) {
if (part() && segment()) {
prevDiagram = part()->prevHarpDiagram(segment()->tick());
}

View File

@ -143,6 +143,7 @@ PropertyValue LetRing::propertyDefault(Pid propertyId) const
case Pid::LINE_VISIBLE:
return true;
case Pid::BEGIN_TEXT_OFFSET:
case Pid::CONTINUE_TEXT_OFFSET:
case Pid::END_TEXT_OFFSET:
return PropertyValue::fromValue(PointF(0, 0));

View File

@ -54,6 +54,16 @@ MeasureRepeat::MeasureRepeat(Segment* parent)
}
}
void MeasureRepeat::setNumMeasures(int n)
{
IF_ASSERT_FAILED(n <= MAX_NUM_MEASURES) {
m_numMeasures = MAX_NUM_MEASURES;
return;
}
m_numMeasures = n;
}
//---------------------------------------------------------
// firstMeasureOfGroup
//---------------------------------------------------------
@ -63,15 +73,26 @@ Measure* MeasureRepeat::firstMeasureOfGroup() const
return measure()->firstOfMeasureRepeatGroup(staffIdx());
}
const Measure* MeasureRepeat::referringMeasure() const
const Measure* MeasureRepeat::referringMeasure(const Measure* measure) const
{
Measure* firstMeasureRepeat = firstMeasureOfGroup();
if (!firstMeasureRepeat) {
IF_ASSERT_FAILED(measure) {
return nullptr;
}
return firstMeasureRepeat->prevMeasure();
const Measure* firstMeasure = firstMeasureOfGroup();
if (!firstMeasure) {
return nullptr;
}
const Measure* referringMeasure = firstMeasure->prevMeasure();
int measuresBack = m_numMeasures - measure->measureRepeatCount(staffIdx());
for (int i = 0; i < measuresBack && referringMeasure; ++i) {
referringMeasure = referringMeasure->prevMeasure();
}
return referringMeasure;
}
//---------------------------------------------------------

View File

@ -43,7 +43,9 @@ public:
MeasureRepeat* clone() const override { return new MeasureRepeat(*this); }
EngravingItem* linkedClone() override { return EngravingItem::linkedClone(); }
void setNumMeasures(int n) { m_numMeasures = n; }
static constexpr int MAX_NUM_MEASURES = 4;
void setNumMeasures(int n);
int numMeasures() const { return m_numMeasures; }
void setSymId(SymId id) { m_symId = id; }
SymId symId() const { return m_symId; }
@ -55,7 +57,7 @@ public:
double numberPos() const { return m_numberPos; }
Measure* firstMeasureOfGroup() const;
const Measure* referringMeasure() const;
const Measure* referringMeasure(const Measure* measure) const;
void draw(mu::draw::Painter*) const override;

View File

@ -150,6 +150,7 @@ std::string MScore::errorToString(MsError err)
case MsError::CANNOT_CHANGE_LOCAL_TIMESIG_MEASURE_NOT_EMPTY: return "CANNOT_CHANGE_LOCAL_TIMESIG_MEASURE_NOT_EMPTY";
case MsError::CANNOT_CHANGE_LOCAL_TIMESIG_HAS_EXCERPTS: return "CANNOT_CHANGE_LOCAL_TIMESIG_HAS_EXCERPTS";
case MsError::CORRUPTED_MEASURE: return "CORRUPTED_MEASURE";
case MsError::CANNOT_REMOVE_KEY_SIG: return "CANNOT_REMOVE_KEY_SIG";
}
return {};

View File

@ -161,6 +161,7 @@ enum class MsError {
CANNOT_CHANGE_LOCAL_TIMESIG_MEASURE_NOT_EMPTY,
CANNOT_CHANGE_LOCAL_TIMESIG_HAS_EXCERPTS,
CORRUPTED_MEASURE,
CANNOT_REMOVE_KEY_SIG,
};
/// \cond PLUGIN_API \private \endcond

View File

@ -913,7 +913,12 @@ int Note::playingTpc() const
int result = tpc();
if (!concertPitch() && transposition()) {
result = transposeTpc(result);
int tpc1 = this->tpc1();
if (tpc1 == Tpc::TPC_INVALID) {
result = transposeTpc(result);
} else {
result = tpc1;
}
}
int steps = ottaveCapoFret();
@ -1980,78 +1985,15 @@ void Note::setHeadHasParentheses(bool hasParentheses)
}
}
//---------------------------------------------------------
// getNoteListForDots
// This method populates three lists: one for chord notes that need to be checked from the top down,
// one for chords from the bottom up, and one for spaces (where the dot will be in that space)
//---------------------------------------------------------
void Note::getNoteListForDots(std::vector<Note*>& topDownNotes, std::vector<Note*>& bottomUpNotes, std::vector<int>& anchoredDots)
{
Chord* c = chord();
bool hasVoices = c->measure()->hasVoices(c->staffIdx(), c->tick(), c->ticks());
if (!hasVoices) {
// only this voice, so topDownNotes is just the notes in the chord
for (Note* note : c->notes()) {
if (note->line() & 1) {
int newOffset = 0;
bool adjustDown = (c->voice() & 1) && !c->up();
if (!anchoredDots.empty() && anchoredDots.back() == note->line()) {
if (anchoredDots.size() >= 2 && anchoredDots[anchoredDots.size() - 2] == note->line() + (adjustDown ? 2 : -2)) {
newOffset = adjustDown ? -2 : 2;
} else {
newOffset = adjustDown ? 2 : -2;
}
}
anchoredDots.push_back(note->line() + newOffset);
} else {
topDownNotes.push_back(note);
}
}
} else {
// Get a list of notes in this staff that adjust dots from top down,
// bottom up, and also start our locked-in dot list by adding all lines where dots are
// guaranteed
Measure* m = c->measure();
size_t firstVoice = c->track() - c->voice();
for (size_t i = firstVoice; i < firstVoice + VOICES; ++i) {
if (Chord* voiceChord = m->findChord(c->tick(), i)) {
bool startFromTop = !((voiceChord->voice() & 1) && !voiceChord->up());
if (startFromTop) {
for (Note* note : voiceChord->notes()) {
if (note->line() & 1) {
anchoredDots.push_back(note->line());
} else {
topDownNotes.push_back(note);
}
}
} else {
for (Note* note : voiceChord->notes()) {
if (note->line() & 1) {
anchoredDots.push_back(note->line());
} else {
bottomUpNotes.push_back(note);
}
}
}
}
}
}
// our two lists now contain only notes that are on lines
std::sort(topDownNotes.begin(), topDownNotes.end(),
[](Note* n1, Note* n2) { return n1->line() < n2->line(); });
std::sort(bottomUpNotes.begin(), bottomUpNotes.end(),
[](Note* n1, Note* n2) { return n1->line() > n2->line(); });
}
//---------------------------------------------------------
// setDotY
// dotMove is number of staff spaces/lines to move from the note's
// space or line
//---------------------------------------------------------
void Note::setDotY(DirectionV pos)
void Note::setDotRelativeLine(int dotMove)
{
double y = 0;
bool onLine = !(line() & 1);
double y = dotMove / 2.0;
if (staff()->isTabStaff(chord()->tick())) {
// with TAB's, dotPosX is not set:
// get dot X from width of fret text and use TAB default spacing
@ -2063,11 +2005,9 @@ void Note::setDotY(DirectionV pos)
// if fret marks above lines, raise the dots by half line distance
y = -0.5;
}
if (pos == DirectionV::AUTO) {
if (dotMove == 0) {
bool oddVoice = voice() & 1;
y = oddVoice ? 0.5 : -0.5;
} else if (pos == DirectionV::UP) {
y = -0.5;
} else {
y = 0.5;
}
@ -2076,56 +2016,7 @@ void Note::setDotY(DirectionV pos)
else {
return;
}
} else if (onLine) {
// NON-TAB
std::vector<Note*> topDownNotes;
std::vector<Note*> bottomUpNotes;
std::vector<int> anchoredDots;
// construct combined chords using the notes from overlapping chords
getNoteListForDots(topDownNotes, bottomUpNotes, anchoredDots);
bool finished = false;
for (Note* note : topDownNotes) {
int dotMove = -1;
if (mu::contains(anchoredDots, note->line() + dotMove)) {
dotMove = 1; // if the desired space is taken, adjust downwards
}
if (note == this) {
y = dotMove / 2.0;
finished = true;
break;
}
anchoredDots.push_back(note->line() + dotMove);
}
if (!finished) {
for (Note* note : bottomUpNotes) {
int dotMove = 1;
if (mu::contains(anchoredDots, note->line() + dotMove)) {
dotMove = -1; // if the desired space is taken, adjust upwards
}
if (note == this) {
y = dotMove / 2.0;
finished = true;
break;
}
anchoredDots.push_back(note->line() + dotMove);
}
}
} else {
// on a space; usually this means the dot is on this same line, but there is an exception
// for a unison within the same chord.
for (Note* note : chord()->notes()) {
if (note == this) {
break;
}
if (note->line() == m_line) {
bool adjustDown = (chord()->voice() & 1) && !chord()->up();
y = adjustDown ? 1.0 : -1.0;
}
}
}
y *= spatium() * staff()->lineDistance(tick());
// apply to dots

View File

@ -332,6 +332,8 @@ public:
DirectionV userDotPosition() const { return m_userDotPosition; }
void setUserDotPosition(DirectionV d) { m_userDotPosition = d; }
DirectionV dotPosition() const { return m_dotPosition; }
void setDotPosition(DirectionV d) { m_dotPosition = d; }
bool dotIsUp() const; // actual dot position
void reset() override;
@ -387,7 +389,7 @@ public:
bool mark() const { return m_mark; }
void setMark(bool v) const { m_mark = v; }
void setScore(Score* s) override;
void setDotY(DirectionV);
void setDotRelativeLine(int);
void setHeadHasParentheses(bool hasParentheses);
bool headHasParentheses() const { return m_hasHeadParentheses; }
@ -492,8 +494,9 @@ private:
SlideType m_slideToType = SlideType::Undefined;
SlideType m_slideFromType = SlideType::Undefined;
DirectionH m_userMirror = DirectionH::AUTO; // user override of mirror
DirectionV m_userDotPosition = DirectionV::AUTO; // user override of dot position
DirectionH m_userMirror = DirectionH::AUTO; ///< user override of mirror
DirectionV m_userDotPosition = DirectionV::AUTO; ///< user override of dot position
DirectionV m_dotPosition = DirectionV::AUTO; // used as an intermediate step when resolving dot conflicts
NoteHeadScheme m_headScheme = NoteHeadScheme::HEAD_AUTO;
NoteHeadGroup m_headGroup = NoteHeadGroup::HEAD_NORMAL;

View File

@ -445,7 +445,7 @@ Ret Score::putNote(const Position& p, bool replace)
ChordRest* cr = _is.cr();
auto checkTied = [&](){
if (!cr->isChord()) {
if (!cr || !cr->isChord()) {
return false;
}
auto ch = toChord(cr);
@ -722,6 +722,20 @@ Ret Score::insertChordByInsertingTime(const Position& pos)
return make_ret(Ret::Code::UnknownError);
}
// remove all two-note tremolos that end on this tick
for (EngravingItem* e : seg->_elist) {
if (!e || !e->isChord()) {
continue;
}
Chord* c = toChord(e);
Tremolo* t = c->tremolo();
if (t && t->twoNotes() && t->chord2() == c) {
// we have to remove this tremolo because we are adding time in the middle of it
// (if c is chord1 then we're inserting before the trem so it's fine)
undoRemoveElement(t);
}
}
const TDuration duration = _is.duration();
const Fraction fraction = duration.fraction();
const Fraction len = fraction;

View File

@ -36,8 +36,8 @@ namespace mu::engraving {
Ornament::Ornament(ChordRest* parent)
: Articulation(parent, ElementType::ORNAMENT)
{
_intervalAbove = OrnamentInterval(IntervalStep::SECOND, IntervalType::AUTO);
_intervalBelow = OrnamentInterval(IntervalStep::SECOND, IntervalType::AUTO);
_intervalAbove = DEFAULT_ORNAMENT_INTERVAL;
_intervalBelow = DEFAULT_ORNAMENT_INTERVAL;
_showAccidental = OrnamentShowAccidental::DEFAULT;
_startOnUpperNote = false;
}
@ -49,6 +49,20 @@ Ornament::Ornament(const Ornament& o)
_intervalBelow = o._intervalBelow;
_showAccidental = o._showAccidental;
_startOnUpperNote = o._startOnUpperNote;
if (o._cueNoteChord) {
_cueNoteChord = o._cueNoteChord->clone();
}
for (size_t i = 0; i < _accidentalsAboveAndBelow.size(); ++i) {
Accidental* oldAccidental = o._accidentalsAboveAndBelow[i];
if (!oldAccidental) {
continue;
}
Accidental* newAccidental = oldAccidental->clone();
newAccidental->setParent(this);
_accidentalsAboveAndBelow[i] = newAccidental;
}
}
Ornament::~Ornament()
@ -135,7 +149,7 @@ PropertyValue Ornament::propertyDefault(Pid id) const
switch (id) {
case Pid::INTERVAL_ABOVE:
case Pid::INTERVAL_BELOW:
return OrnamentInterval(IntervalStep::SECOND, IntervalType::AUTO);
return DEFAULT_ORNAMENT_INTERVAL;
case Pid::ORNAMENT_SHOW_ACCIDENTAL:
return OrnamentShowAccidental::DEFAULT;
case Pid::START_ON_UPPER_NOTE:
@ -229,6 +243,10 @@ void Ornament::computeNotesAboveAndBelow(AccidentalState* accState)
}
Note*& note = _notesAboveAndBelow.at(i);
if (!note && above && _cueNoteChord) {
note = _cueNoteChord->upNote();
}
if (!note) {
note = mainNote->clone();
} else {

View File

@ -167,6 +167,7 @@ PropertyValue PalmMute::propertyDefault(Pid propertyId) const
case Pid::LINE_VISIBLE:
return true;
case Pid::BEGIN_TEXT_OFFSET:
case Pid::CONTINUE_TEXT_OFFSET:
case Pid::END_TEXT_OFFSET:
return PropertyValue::fromValue(PointF(0, 0));

View File

@ -33,6 +33,9 @@ using namespace mu;
namespace mu::engraving {
static const ElementStyle pedalStyle {
{ Sid::pedalText, Pid::BEGIN_TEXT },
{ Sid::pedalContinueText, Pid::CONTINUE_TEXT },
{ Sid::pedalEndText, Pid::END_TEXT },
{ Sid::pedalFontFace, Pid::BEGIN_FONT_FACE },
{ Sid::pedalFontFace, Pid::CONTINUE_FONT_FACE },
{ Sid::pedalFontFace, Pid::END_FONT_FACE },
@ -92,9 +95,6 @@ Pedal::Pedal(EngravingItem* parent)
{
initElementStyle(&pedalStyle);
setLineVisible(true);
resetProperty(Pid::BEGIN_TEXT);
resetProperty(Pid::CONTINUE_TEXT);
resetProperty(Pid::END_TEXT);
resetProperty(Pid::LINE_WIDTH);
resetProperty(Pid::LINE_STYLE);
@ -137,15 +137,24 @@ engraving::PropertyValue Pedal::propertyDefault(Pid propertyId) const
return score()->styleV(Sid::pedalLineStyle);
case Pid::BEGIN_TEXT:
return score()->styleV(Sid::pedalText);
case Pid::CONTINUE_TEXT:
return score()->styleV(Sid::pedalContinueText);
case Pid::END_TEXT:
return "";
return score()->styleV(Sid::pedalEndText);
case Pid::BEGIN_TEXT_PLACE:
case Pid::CONTINUE_TEXT_PLACE:
case Pid::END_TEXT_PLACE:
return TextPlace::LEFT;
case Pid::BEGIN_TEXT_OFFSET:
case Pid::CONTINUE_TEXT_OFFSET:
case Pid::END_TEXT_OFFSET:
return PropertyValue::fromValue(PointF(0, 0));
case Pid::BEGIN_HOOK_TYPE:
case Pid::END_HOOK_TYPE:
return HookType::NONE;

View File

@ -522,6 +522,10 @@ int Rest::computeWholeRestOffset(int voiceOffset, int lines)
}
Chord* chord = toChord(item);
Shape chordShape = chord->shape().translated(chord->pos());
chordShape.removeInvisibles();
if (chordShape.empty()) {
continue;
}
if (track < thisTrack) {
hasNotesAbove = true;
bottomY = std::max(bottomY, chordShape.bottom());

View File

@ -4448,11 +4448,17 @@ void Score::removeSpanner(Spanner* s)
{
_spanner.removeSpanner(s);
s->removed();
if (s->startElement()) {
mu::remove_if(s->startElement()->startingSpanners(), [s](Spanner* sp) { return sp == s; });
EngravingItem* startElement = s->startElement();
Chord* startChord = startElement && startElement->isChord() ? toChord(startElement) : nullptr;
if (startChord) {
startChord->removeStartingSpanner(s);
}
if (s->endElement()) {
mu::remove_if(s->endElement()->endingSpanners(), [s](Spanner* sp) { return sp == s; });
EngravingItem* endElement = s->endElement();
Chord* endChord = endElement && endElement->isChord() ? toChord(endElement) : nullptr;
if (endChord) {
endChord->removeEndingSpanner(s);
}
}
@ -5374,21 +5380,26 @@ std::set<staff_idx_t> Score::staffIdxSetFromRange(const track_idx_t trackFrom, c
{
std::set<staff_idx_t> result;
staff_idx_t staffIdxFrom = track2staff(trackFrom);
staff_idx_t staffIdxTo = std::min(track2staff(trackTo) + 1, _staves.size());
for (staff_idx_t idx = staffIdxFrom; idx < staffIdxTo; ++idx) {
const Staff* staff = _staves.at(idx);
if (!staff) {
for (const Part* part : m_score->parts()) {
if (trackTo < part->startTrack() || trackFrom >= part->endTrack()) {
continue;
}
if (staffAccepted) {
std::set<staff_idx_t> staffIdxList = part->staveIdxList();
if (!staffAccepted) {
result.insert(staffIdxList.cbegin(), staffIdxList.cend());
continue;
}
for (staff_idx_t idx : staffIdxList) {
const Staff* staff = m_score->staff(idx);
if (!staff) {
continue;
}
if (staffAccepted(*staff)) {
result.insert(idx);
}
} else {
result.insert(idx);
}
}

View File

@ -389,7 +389,7 @@ void ScoreOrder::setBracketsAndBarlines(Score* score)
bool prvThnBracket { false };
bool prvBarLineSpan { false };
String prvSection;
int prvInstrument { 0 };
int prvInstrument { -1 };
Staff* prvStaff { nullptr };
Staff* thkBracketStaff { nullptr };
@ -440,7 +440,7 @@ void ScoreOrder::setBracketsAndBarlines(Score* score)
thkBracketSpan += static_cast<int>(part->nstaves());
}
if (!staffIdx || (ii.instrIndex != prvInstrument)) {
if (prvInstrument == -1 || (ii.instrIndex != prvInstrument)) {
if (thnBracketStaff && (thnBracketSpan > 1)) {
score->undoAddBracket(thnBracketStaff, 1, BracketType::SQUARE, thnBracketSpan);
}

View File

@ -374,6 +374,13 @@ void Shape::remove(const Shape& s)
}
}
void Shape::removeInvisibles()
{
mu::remove_if(*this, [](ShapeElement& shapeElement) {
return !shapeElement.toItem || !shapeElement.toItem->visible();
});
}
//---------------------------------------------------------
// contains
//---------------------------------------------------------

View File

@ -77,6 +77,7 @@ public:
void remove(const mu::RectF&);
void remove(const Shape&);
void removeInvisibles();
void addHorizontalSpacing(EngravingItem* item, double left, double right);

View File

@ -675,8 +675,9 @@ void Spanner::computeStartElement()
break;
}
if (m_startElement && !mu::contains(m_startElement->startingSpanners(), this)) {
m_startElement->startingSpanners().push_back(this);
Chord* startChord = m_startElement && m_startElement->isChord() ? toChord(m_startElement) : nullptr;
if (startChord) {
startChord->addStartingSpanner(this);
}
}
@ -763,8 +764,9 @@ void Spanner::computeEndElement()
break;
}
if (m_endElement && !mu::contains(m_endElement->endingSpanners(), this)) {
m_endElement->endingSpanners().push_back(this);
Chord* endChord = m_endElement && m_endElement->isChord() ? toChord(m_endElement) : nullptr;
if (endChord) {
endChord->addEndingSpanner(this);
}
}

View File

@ -963,22 +963,31 @@ bool Staff::isPrimaryStaff() const
if (!_links) {
return true;
}
std::vector<Staff*> s;
std::vector<Staff*> ss;
for (auto e : *_links) {
Staff* staff = toStaff(e);
std::vector<const Staff*> linkedStavesInThisScore;
std::vector<const Staff*> linkedNonTabStavesInThisScore;
for (const EngravingObject* linked : *_links) {
const Staff* staff = toStaff(linked);
if (staff->score() == score()) {
s.push_back(staff);
linkedStavesInThisScore.push_back(staff);
if (!staff->isTabStaff(Fraction(0, 1))) {
ss.push_back(staff);
linkedNonTabStavesInThisScore.push_back(staff);
}
}
}
if (s.size() == 1) { // the linked staves are in different scores
return s.front() == this;
} else { // return a non tab linked staff in this score
return ss.front() == this;
IF_ASSERT_FAILED(!linkedStavesInThisScore.empty()) {
return true;
}
if (!linkedNonTabStavesInThisScore.empty()) {
return linkedNonTabStavesInThisScore.front() == this;
}
return linkedStavesInThisScore.front() == this;
}
//---------------------------------------------------------

View File

@ -2755,6 +2755,8 @@ Sid TextBase::offsetSid() const
switch (textStyleType()) {
case TextStyleType::DYNAMICS:
return above ? Sid::dynamicsPosAbove : Sid::dynamicsPosBelow;
case TextStyleType::EXPRESSION:
return above ? Sid::expressionPosAbove : Sid::expressionPosBelow;
case TextStyleType::LYRICS_ODD:
case TextStyleType::LYRICS_EVEN:
return above ? Sid::lyricsPosAbove : Sid::lyricsPosBelow;

View File

@ -838,6 +838,11 @@ EngravingItem* TextBase::drop(EditData& ed)
String s = toFSymbol(e)->toString();
delete e;
CharFormat* currentFormat = cursor->format();
if (currentFormat->fontFamily() == u"ScoreText") {
currentFormat->setFontFamily(propertyDefault(Pid::FONT_FACE).value<String>());
}
deleteSelectedText(ed);
score()->undo(new InsertText(cursor, s), &ed);
}

View File

@ -149,9 +149,13 @@ void TextLineBaseSegment::draw(mu::draw::Painter* painter) const
pen.setDashPattern({ dash, gap });
}
pen.setJoinStyle(PenJoinStyle::BevelJoin);
painter->setPen(pen);
painter->drawLines(&m_points[0], 1);
painter->drawLines(&m_points[2], 1);
if (!m_joinedHairpin.empty() && !isNonSolid) {
painter->drawPolyline(m_joinedHairpin);
} else {
painter->drawLines(&m_points[0], 2);
}
return;
}

View File

@ -67,6 +67,7 @@ public:
Text* endText() const { return m_endText; }
mu::PointF* pointsRef() { return &m_points[0]; }
mu::PolygonF& polygonRef() { return m_joinedHairpin; }
int& npointsRef() { return m_npoints; }
double lineLength() const { return m_lineLength; }
@ -79,6 +80,7 @@ protected:
Text* m_text = nullptr;
Text* m_endText = nullptr;
mu::PointF m_points[6];
mu::PolygonF m_joinedHairpin;
int m_npoints = 0;
double m_lineLength = 0;
bool m_twoLines = false;

View File

@ -122,7 +122,7 @@ void Tremolo::createBeamSegments()
// inset trem from stems for default style
double slope = (endAnchor.y() - startAnchor.y()) / (endAnchor.x() - startAnchor.x());
double gapSp = stemGapSp;
if (defaultStyle) {
if (defaultStyle || _style == TremoloStyle::TRADITIONAL_ALTERNATE) {
// we can eat into the stemGapSp margin if the anchorpoints are sufficiently close together
double widthSp = (endAnchor.x() - startAnchor.x()) / spatium() - (stemGapSp * 2);
if (!RealIsEqualOrMore(widthSp, 0.6)) {
@ -132,14 +132,24 @@ void Tremolo::createBeamSegments()
} else {
gapSp = 0.0;
}
double offset = gapSp * spatium();
startAnchor.rx() += offset;
endAnchor.rx() -= offset;
startAnchor.ry() += offset * slope;
endAnchor.ry() -= offset * slope;
BeamSegment* mainStroke = new BeamSegment(this);
PointF xOffset = PointF(gapSp * spatium(), 0);
PointF yOffset = PointF(0, gapSp * spatium() * slope);
if (_style == TremoloStyle::TRADITIONAL_ALTERNATE) {
mainStroke->line = LineF(startAnchor, endAnchor);
startAnchor += xOffset;
endAnchor -= xOffset;
startAnchor += yOffset;
endAnchor -= yOffset;
} else {
startAnchor += xOffset;
endAnchor -= xOffset;
startAnchor += yOffset;
endAnchor -= yOffset;
mainStroke->line = LineF(startAnchor, endAnchor);
}
mainStroke->level = 0;
mainStroke->line = LineF(startAnchor, endAnchor);
_beamSegments.push_back(mainStroke);
double bboxTop = _up ? std::min(mainStroke->line.y1(), mainStroke->line.y2()) : std::max(mainStroke->line.y1(), mainStroke->line.y2());
double halfWidth = score()->styleMM(Sid::beamWidth).val() / 2. * (_up ? -1. : 1.);
@ -590,6 +600,26 @@ void Tremolo::triggerLayout() const
}
}
bool Tremolo::needStartEditingAfterSelecting() const
{
return twoNotes();
}
int Tremolo::gripsCount() const
{
return twoNotes() ? 3 : 0;
}
Grip Tremolo::initialEditModeGrip() const
{
return twoNotes() ? Grip::END : Grip::NO_GRIP;
}
Grip Tremolo::defaultGrip() const
{
return twoNotes() ? Grip::MIDDLE : Grip::NO_GRIP;
}
//---------------------------------------------------------
// gripsPositions
//---------------------------------------------------------

View File

@ -132,15 +132,13 @@ public:
void setUp(bool up) { _up = up; }
// only need grips for two-note trems
bool needStartEditingAfterSelecting() const override { return twoNotes(); }
int gripsCount() const override { return 3; }
Grip initialEditModeGrip() const override { return Grip::END; }
Grip defaultGrip() const override { return Grip::MIDDLE; }
bool needStartEditingAfterSelecting() const override;
int gripsCount() const override;
Grip initialEditModeGrip() const;
Grip defaultGrip() const override;
std::vector<mu::PointF> gripsPositions(const EditData&) const override;
bool isMovable() const override { return true; }
void startDrag(EditData&) override {}
bool isEditable() const override { return true; }
void startEdit(EditData&) override {}
void endEdit(EditData&) override;
void editDrag(EditData&) override;

View File

@ -318,7 +318,7 @@ void PlaybackModel::updateContext(const InstrumentTrackId& trackId)
}
void PlaybackModel::processSegment(const int tickPositionOffset, const Segment* segment, const std::set<staff_idx_t>& staffIdxSet,
ChangedTrackIdSet* trackChanges)
bool isFirstSegmentOfMeasure, ChangedTrackIdSet* trackChanges)
{
int segmentStartTick = segment->tick().ticks();
@ -369,21 +369,23 @@ void PlaybackModel::processSegment(const int tickPositionOffset, const Segment*
continue;
}
if (item->type() == ElementType::MEASURE_REPEAT) {
const MeasureRepeat* measureRepeat = toMeasureRepeat(item);
const Measure* currentMeasure = measureRepeat->measure();
const Measure* referringMeasure = measureRepeat->referringMeasure();
if (isFirstSegmentOfMeasure) {
if (item->isMeasureRepeat()) {
const MeasureRepeat* measureRepeat = toMeasureRepeat(item);
const Measure* currentMeasure = measureRepeat->measure();
processMeasureRepeat(tickPositionOffset, measureRepeat, currentMeasure, staffIdx, trackChanges);
if (!referringMeasure || !currentMeasure) {
continue;
}
} else {
const Measure* currentMeasure = segment->measure();
int currentMeasureTick = measureRepeat->measure()->tick().ticks();
int referringMeasureTick = referringMeasure->tick().ticks();
int repeatPositionTickOffset = currentMeasureTick - referringMeasureTick;
if (currentMeasure->measureRepeatCount(staffIdx) > 0) {
const MeasureRepeat* measureRepeat = currentMeasure->measureRepeatElement(staffIdx);
for (Segment* seg = referringMeasure->first(); seg; seg = seg->next()) {
processSegment(tickPositionOffset + repeatPositionTickOffset, seg, { staffIdx }, trackChanges);
processMeasureRepeat(tickPositionOffset, measureRepeat, currentMeasure, staffIdx, trackChanges);
continue;
}
}
}
@ -403,6 +405,34 @@ void PlaybackModel::processSegment(const int tickPositionOffset, const Segment*
}
}
void PlaybackModel::processMeasureRepeat(const int tickPositionOffset, const MeasureRepeat* measureRepeat, const Measure* currentMeasure,
const staff_idx_t staffIdx, ChangedTrackIdSet* trackChanges)
{
if (!measureRepeat || !currentMeasure) {
return;
}
const Measure* referringMeasure = measureRepeat->referringMeasure(currentMeasure);
if (!referringMeasure) {
return;
}
int currentMeasureTick = currentMeasure->tick().ticks();
int referringMeasureTick = referringMeasure->tick().ticks();
int repeatPositionTickOffset = currentMeasureTick - referringMeasureTick;
bool isFirstSegmentOfRepeatedMeasure = true;
for (const Segment* seg = referringMeasure->first(); seg; seg = seg->next()) {
if (!seg->isChordRestType()) {
continue;
}
processSegment(tickPositionOffset + repeatPositionTickOffset, seg, { staffIdx }, isFirstSegmentOfRepeatedMeasure, trackChanges);
isFirstSegmentOfRepeatedMeasure = false;
}
}
void PlaybackModel::updateEvents(const int tickFrom, const int tickTo, const track_idx_t trackFrom, const track_idx_t trackTo,
ChangedTrackIdSet* trackChanges)
{
@ -429,6 +459,8 @@ void PlaybackModel::updateEvents(const int tickFrom, const int tickTo, const tra
continue;
}
bool isFirstSegmentOfMeasure = true;
for (Segment* segment = measure->first(); segment; segment = segment->next()) {
if (!segment->isChordRestType()) {
continue;
@ -441,7 +473,8 @@ void PlaybackModel::updateEvents(const int tickFrom, const int tickTo, const tra
continue;
}
processSegment(tickPositionOffset, segment, staffToProcessIdxSet, trackChanges);
processSegment(tickPositionOffset, segment, staffToProcessIdxSet, isFirstSegmentOfMeasure, trackChanges);
isFirstSegmentOfMeasure = false;
}
m_renderer.renderMetronome(m_score, measureStartTick, measureEndTick, tickPositionOffset,
@ -479,13 +512,15 @@ bool PlaybackModel::hasToReloadTracks(const ScoreChangesRange& changesRange) con
return false;
}
const Measure* nextToLastMeasure = measureTo->nextMeasure();
const Measure* nextMeasure = measureTo->nextMeasure();
if (!nextToLastMeasure) {
return false;
for (int i = 0; i < MeasureRepeat::MAX_NUM_MEASURES && nextMeasure; ++i) {
if (nextMeasure->containsMeasureRepeat(changesRange.staffIdxFrom, changesRange.staffIdxTo)) {
return true;
}
nextMeasure = nextMeasure->nextMeasure();
}
return nextToLastMeasure->containsMeasureRepeat(changesRange.staffIdxFrom, changesRange.staffIdxTo);
}
return false;

View File

@ -109,7 +109,9 @@ private:
ChangedTrackIdSet* trackChanges = nullptr);
void processSegment(const int tickPositionOffset, const Segment* segment, const std::set<staff_idx_t>& staffIdxSet,
ChangedTrackIdSet* trackChanges);
bool isFirstSegmentOfMeasure, ChangedTrackIdSet* trackChanges);
void processMeasureRepeat(const int tickPositionOffset, const MeasureRepeat* measureRepeat, const Measure* currentMeasure,
const staff_idx_t staffIdx, ChangedTrackIdSet* trackChanges);
bool hasToReloadTracks(const ScoreChangesRange& changesRange) const;
bool hasToReloadScore(const std::unordered_set<ElementType>& changedTypes) const;

View File

@ -310,6 +310,8 @@ void OrnamentsRenderer::doRender(const EngravingItem* item, const ArticulationTy
IntervalsInfo intervalsInfo;
if (const Ornament* ornament = chord->findOrnament()) {
intervalsInfo = makeIntervalsInfo(ornament->intervalBelow(), ornament->intervalAbove());
} else {
intervalsInfo = makeIntervalsInfo(DEFAULT_ORNAMENT_INTERVAL, DEFAULT_ORNAMENT_INTERVAL);
}
const DisclosurePattern& nominalPattern = search->second;

View File

@ -30,6 +30,8 @@
#include "libmscore/score.h"
#include "libmscore/excerpt.h"
#include "libmscore/part.h"
#include "libmscore/stem.h"
#include "libmscore/tremolo.h"
#include "libmscore/linkedobjects.h"
#include "libmscore/measure.h"
#include "libmscore/factory.h"
@ -38,6 +40,7 @@
#include "libmscore/stafftext.h"
#include "libmscore/stafftextbase.h"
#include "libmscore/playtechannotation.h"
#include "libmscore/capo.h"
#include "types/string.h"
@ -72,6 +75,8 @@ const std::set<SymId> CompatUtils::ORNAMENT_IDS {
void CompatUtils::doCompatibilityConversions(MasterScore* masterScore)
{
TRACEFUNC;
if (!masterScore) {
return;
}
@ -85,6 +90,10 @@ void CompatUtils::doCompatibilityConversions(MasterScore* masterScore)
replaceOldWithNewExpressions(masterScore);
replaceOldWithNewOrnaments(masterScore);
resetRestVerticalOffset(masterScore);
splitArticulations(masterScore);
resetArticulationOffsets(masterScore);
resetStemLengthsForTwoNoteTrems(masterScore);
replaceStaffTextWithCapo(masterScore);
}
}
@ -341,6 +350,104 @@ void CompatUtils::reconstructTypeOfCustomDynamics(MasterScore* score)
}
}
void CompatUtils::splitArticulations(MasterScore* masterScore)
{
std::set<Articulation*> toRemove;
for (Measure* meas = masterScore->firstMeasure(); meas; meas = meas->nextMeasure()) {
for (Segment& seg : meas->segments()) {
if (!seg.isChordRestType()) {
continue;
}
for (EngravingItem* item : seg.elist()) {
if (!item || !item->isChord()) {
continue;
}
Chord* chord = toChord(item);
for (Articulation* a : chord->articulations()) {
if (a->isLinked()) {
continue; // only worry about main artics, links will be done later
}
std::set<SymId> ids = mu::engraving::splitArticulations({ a->symId() });
if (ids.size() <= 1) {
continue;
}
toRemove.insert(a);
}
}
}
}
// separate into individual articulations
for (Articulation* combinedArtic : toRemove) {
auto components = mu::engraving::splitArticulations({ combinedArtic->symId() });
Chord* parentChord = toChord(combinedArtic->parentItem());
for (SymId id : components) {
Articulation* newArtic = Factory::createArticulation(masterScore->dummy()->chord());
newArtic->setSymId(id);
if (parentChord->hasArticulation(newArtic)) {
delete newArtic;
continue;
}
newArtic->setParent(parentChord);
newArtic->setTrack(combinedArtic->track());
newArtic->setPos(combinedArtic->pos());
newArtic->setDirection(combinedArtic->direction());
newArtic->setAnchor(combinedArtic->anchor());
newArtic->setColor(combinedArtic->color());
newArtic->setPlayArticulation(combinedArtic->playArticulation());
newArtic->setVisible(combinedArtic->visible());
newArtic->setOrnamentStyle(combinedArtic->ornamentStyle());
LinkedObjects* links = new LinkedObjects(masterScore);
links->push_back(newArtic);
newArtic->setLinks(links);
parentChord->add(newArtic);
// newArtic is the main articulation
LinkedObjects* oldLinks = combinedArtic->links();
if (!oldLinks || oldLinks->empty()) {
continue;
}
for (EngravingObject* linkedItem : *oldLinks) {
IF_ASSERT_FAILED(linkedItem && linkedItem->isArticulation()) {
continue;
}
if (linkedItem == combinedArtic) {
continue;
}
Articulation* oldArtic = toArticulation(linkedItem);
Chord* oldParent = toChord(oldArtic->parentItem());
oldParent->add(newArtic->linkedClone());
}
}
}
// finally, remove the combined articulations
for (Articulation* combinedArtic : toRemove) {
LinkedObjects* links = combinedArtic->links();
if (!links || links->empty()) {
Chord* parentChord = toChord(combinedArtic->parentItem());
parentChord->remove(combinedArtic);
delete combinedArtic;
continue;
}
std::set<Articulation*> removeLinks;
for (auto linked : *links) {
IF_ASSERT_FAILED(linked && linked->isArticulation()) {
continue;
}
removeLinks.insert(toArticulation(linked));
}
for (Articulation* linkedArtic : removeLinks) {
if (linkedArtic != combinedArtic) {
Chord* linkedParent = toChord(linkedArtic->parentItem());
linkedParent->remove(linkedArtic);
delete linkedArtic;
}
}
Chord* parentChord = toChord(combinedArtic->parentItem());
parentChord->remove(combinedArtic);
delete combinedArtic;
}
}
DynamicType CompatUtils::reconstructDynamicTypeFromString(Dynamic* dynamic)
{
static std::vector<Dyn> sortedDynList; // copy of dynList sorted by string length
@ -406,3 +513,116 @@ void CompatUtils::resetRestVerticalOffset(MasterScore* masterScore)
}
}
}
void CompatUtils::resetArticulationOffsets(MasterScore* masterScore)
{
for (Score* score : masterScore->scoreList()) {
for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
for (Segment& segment : measure->segments()) {
if (!segment.isChordRestType()) {
continue;
}
for (EngravingItem* item : segment.elist()) {
if (!item || !item->isChord()) {
continue;
}
Chord* chord = toChord(item);
for (Articulation* artic : chord->articulations()) {
if (!artic) {
continue;
}
artic->setProperty(Pid::OFFSET, PointF());
}
}
}
}
}
}
void CompatUtils::resetStemLengthsForTwoNoteTrems(MasterScore* masterScore)
{
for (Score* score : masterScore->scoreList()) {
for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
for (Segment& segment : measure->segments()) {
if (!segment.isChordRestType()) {
continue;
}
for (EngravingItem* item : segment.elist()) {
if (!item || !item->isChord()) {
continue;
}
Chord* chord = toChord(item);
Tremolo* trem = chord->tremolo();
Stem* stem = chord->stem();
if (stem && trem && trem->twoNotes()) {
if (stem->userLength() != Millimetre(0)) {
stem->setUserLength(Millimetre(0));
}
}
}
}
}
}
}
void CompatUtils::replaceStaffTextWithCapo(MasterScore* score)
{
TRACEFUNC;
std::set<StaffTextBase*> oldCapoSet;
for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
for (Segment* segment = measure->first(); segment; segment = segment->next()) {
for (EngravingItem* annotation : segment->annotations()) {
if (!annotation || !annotation->isStaffTextBase()) {
continue;
}
StaffTextBase* text = toStaffTextBase(annotation);
if (text->capo() > 0) {
oldCapoSet.insert(text);
} else {
continue;
}
LinkedObjects* links = text->links();
if (!links || links->empty()) {
continue;
}
for (EngravingObject* linked : *links) {
if (linked != text && linked && linked->isStaffTextBase()) {
oldCapoSet.insert(toStaffTextBase(linked));
}
}
}
}
}
for (StaffTextBase* oldCapo : oldCapoSet) {
Segment* parentSegment = oldCapo->segment();
Capo* newCapo = Factory::createCapo(parentSegment);
int capoFretPosition = oldCapo->capo() - 1;
CapoParams params;
params.active = capoFretPosition > 0;
params.fretPosition = capoFretPosition;
newCapo->setTrack(oldCapo->track());
newCapo->setParams(params);
newCapo->setProperty(Pid::PLACEMENT, oldCapo->placement());
LinkedObjects* links = oldCapo->links();
newCapo->setLinks(links);
if (links) {
links->push_back(newCapo);
}
parentSegment->add(newCapo);
parentSegment->removeAnnotation(oldCapo);
delete oldCapo;
}
}

View File

@ -50,8 +50,12 @@ private:
static void replaceOldWithNewOrnaments(MasterScore* score);
static void replaceOldWithNewExpressions(MasterScore* score);
static void reconstructTypeOfCustomDynamics(MasterScore* score);
static void splitArticulations(MasterScore* score);
static DynamicType reconstructDynamicTypeFromString(Dynamic* dynamic);
static void resetRestVerticalOffset(MasterScore* masterScore);
static void resetArticulationOffsets(MasterScore* masterScore);
static void resetStemLengthsForTwoNoteTrems(MasterScore* masterScore);
static void replaceStaffTextWithCapo(MasterScore* masterScore);
};
}
#endif // MU_ENGRAVING_COMPATUTILS_H

View File

@ -1379,18 +1379,21 @@ static void readPedal114(XmlReader& e, ReadContext& ctx, Pedal* pedal)
text.at(0).isDigit()
? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
: text));
pedal->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
} else if (tag == "continueSymbol") {
String text(e.readText());
pedal->setContinueText(String(u"<sym>%1</sym>").arg(
text.at(0).isDigit()
? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
: text));
pedal->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
} else if (tag == "endSymbol") {
String text(e.readText());
pedal->setEndText(String(u"<sym>%1</sym>").arg(
text.at(0).isDigit()
? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
: text));
pedal->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
} else if (tag == "beginSymbolOffset") { // obsolete
e.readPoint();
} else if (tag == "continueSymbolOffset") { // obsolete

View File

@ -1987,16 +1987,19 @@ static bool readTextLineProperties(XmlReader& e, ReadContext& ctx, TextLineBase*
Text* text = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
readText206(e, ctx, text, tl);
tl->setBeginText(text->xmlText());
tl->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
delete text;
} else if (tag == "continueText") {
Text* text = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
readText206(e, ctx, text, tl);
tl->setContinueText(text->xmlText());
tl->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
delete text;
} else if (tag == "endText") {
Text* text = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
readText206(e, ctx, text, tl);
tl->setEndText(text->xmlText());
tl->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
delete text;
} else if (tag == "beginHook") {
tl->setBeginHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);

View File

@ -96,6 +96,8 @@ Err Read400::readScore(Score* score, XmlReader& e, rw::ReadInOutData* data)
ex->setTracksMapping(ctx.tracks());
}
ctx.clearOrphanedConnectors();
if (data) {
data->links = ctx.readLinks();
data->settingsCompat = ctx.settingCompat();

View File

@ -501,6 +501,56 @@ void ReadContext::reconnectBrokenConnectors()
doReconnectBrokenConnectors();
}
void ReadContext::clearOrphanedConnectors()
{
if (_connectors.empty() && _pendingConnectors.empty()) {
return;
}
LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
std::set<LinkedObjects*> deletedLinks;
auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
EngravingItem* conn = c ? c->releaseConnector() : nullptr;
if (!conn) {
return;
}
LinkedObjects* links = conn->links();
bool linksWillBeDeleted = links && links->size() == 1;
if (!conn->isTuplet()) { // tuplets are added to score even when not finished
if (linksWillBeDeleted) {
deletedLinks.insert(links);
}
delete conn;
}
};
if (!_connectors.empty()) {
for (auto& c : _connectors) {
deleteConnectors(c);
}
_connectors.clear();
}
if (!_pendingConnectors.empty()) {
for (auto& c : _pendingConnectors) {
deleteConnectors(c);
}
_pendingConnectors.clear();
}
for (auto& it : m_staffLinkedElements) {
std::vector<std::pair<LinkedObjects*, Location> >& vector = it.second;
mu::remove_if(vector, [&deletedLinks](std::pair<LinkedObjects*, Location>& pair){
return deletedLinks.count(pair.first);
});
}
}
void ReadContext::doReconnectBrokenConnectors()
{
if (_connectors.empty()) {

View File

@ -175,6 +175,7 @@ public:
void addConnectorInfoLater(std::shared_ptr<read400::ConnectorInfoReader> c); // add connector info to be checked after calling checkConnectors()
void checkConnectors();
void reconnectBrokenConnectors();
void clearOrphanedConnectors();
private:

View File

@ -209,7 +209,7 @@ PropertyValue TRead::readPropertyValue(Pid id, XmlReader& e, ReadContext& ctx)
case P_TYPE::ORNAMENT_STYLE:
return PropertyValue::fromValue(TConv::fromXml(e.readAsciiText(), OrnamentStyle::DEFAULT));
case P_TYPE::ORNAMENT_INTERVAL:
return PropertyValue(TConv::fromXml(e.readText(), OrnamentInterval()));
return PropertyValue(TConv::fromXml(e.readText(), DEFAULT_ORNAMENT_INTERVAL));
case P_TYPE::POINT:
return PropertyValue::fromValue(e.readPoint());
case P_TYPE::SCALE:
@ -1279,6 +1279,11 @@ void TRead::read(KeySig* s, XmlReader& e, ReadContext& ctx)
if (sig.custom() && sig.customKeyDefs().empty()) {
sig.setMode(KeyMode::NONE);
}
// if there are more than 6 accidentals in transposing key, it cannot be PreferSharpFlat::AUTO
if (p && !s->concertPitch() && (sig.key() > 6 || sig.key() < -6)
&& p->preferSharpFlat() == PreferSharpFlat::AUTO && !p->instrument(s->tick())->transpose().isZero()) {
p->setPreferSharpFlat(PreferSharpFlat::NONE);
}
s->setKeySigEvent(sig);
}
@ -2536,6 +2541,9 @@ void TRead::read(ChordLine* l, XmlReader& e, ReadContext& ctx)
int type = e.intAttribute("type");
double x = e.doubleAttribute("x");
double y = e.doubleAttribute("y");
double spatium = ctx.spatium();
x *= spatium;
y *= spatium;
switch (PainterPath::ElementType(type)) {
case PainterPath::ElementType::MoveToElement:
path.moveTo(x, y);

View File

@ -98,6 +98,8 @@ Err Read410::readScore(Score* score, XmlReader& e, rw::ReadInOutData* data)
ex->setTracksMapping(ctx.tracks());
}
ctx.clearOrphanedConnectors();
if (data) {
data->links = ctx.readLinks();
data->settingsCompat = ctx.settingCompat();

View File

@ -501,6 +501,56 @@ void ReadContext::reconnectBrokenConnectors()
doReconnectBrokenConnectors();
}
void ReadContext::clearOrphanedConnectors()
{
if (_connectors.empty() && _pendingConnectors.empty()) {
return;
}
LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
std::set<LinkedObjects*> deletedLinks;
auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
EngravingItem* conn = c ? c->releaseConnector() : nullptr;
if (!conn) {
return;
}
LinkedObjects* links = conn->links();
bool linksWillBeDeleted = links && links->size() == 1;
if (!conn->isTuplet()) { // tuplets are added to score even when not finished
if (linksWillBeDeleted) {
deletedLinks.insert(links);
}
delete conn;
}
};
if (!_connectors.empty()) {
for (auto& c : _connectors) {
deleteConnectors(c);
}
_connectors.clear();
}
if (!_pendingConnectors.empty()) {
for (auto& c : _pendingConnectors) {
deleteConnectors(c);
}
_pendingConnectors.clear();
}
for (auto& it : m_staffLinkedElements) {
std::vector<std::pair<LinkedObjects*, Location> >& vector = it.second;
mu::remove_if(vector, [&deletedLinks](std::pair<LinkedObjects*, Location>& pair){
return deletedLinks.count(pair.first);
});
}
}
void ReadContext::doReconnectBrokenConnectors()
{
if (_connectors.empty()) {

View File

@ -175,6 +175,7 @@ public:
void addConnectorInfoLater(std::shared_ptr<ConnectorInfoReader> c); // add connector info to be checked after calling checkConnectors()
void checkConnectors();
void reconnectBrokenConnectors();
void clearOrphanedConnectors();
private:

View File

@ -210,7 +210,7 @@ PropertyValue TRead::readPropertyValue(Pid id, XmlReader& e, ReadContext& ctx)
case P_TYPE::ORNAMENT_STYLE:
return PropertyValue::fromValue(TConv::fromXml(e.readAsciiText(), OrnamentStyle::DEFAULT));
case P_TYPE::ORNAMENT_INTERVAL:
return PropertyValue(TConv::fromXml(e.readText(), OrnamentInterval()));
return PropertyValue(TConv::fromXml(e.readText(), DEFAULT_ORNAMENT_INTERVAL));
case P_TYPE::POINT:
return PropertyValue::fromValue(e.readPoint());
case P_TYPE::SCALE:
@ -1284,6 +1284,11 @@ void TRead::read(KeySig* s, XmlReader& e, ReadContext& ctx)
if (sig.custom() && sig.customKeyDefs().empty()) {
sig.setMode(KeyMode::NONE);
}
// if there are more than 6 accidentals in transposing key, it cannot be PreferSharpFlat::AUTO
if (p && !s->concertPitch() && (sig.key() > 6 || sig.key() < -6)
&& p->preferSharpFlat() == PreferSharpFlat::AUTO && !p->instrument(s->tick())->transpose().isZero()) {
p->setPreferSharpFlat(PreferSharpFlat::NONE);
}
s->setKeySigEvent(sig);
}
@ -2525,6 +2530,9 @@ void TRead::read(ChordLine* l, XmlReader& e, ReadContext& ctx)
int type = e.intAttribute("type");
double x = e.doubleAttribute("x");
double y = e.doubleAttribute("y");
double spatium = ctx.spatium();
x *= spatium;
y *= spatium;
switch (PainterPath::ElementType(type)) {
case PainterPath::ElementType::MoveToElement:
path.moveTo(x, y);

View File

@ -857,7 +857,10 @@ void TWrite::write(const ChordLine* item, XmlWriter& xml, WriteContext& ctx)
xml.startElement("Path");
for (size_t i = 0; i < n; ++i) {
const PainterPath::Element& e = path.elementAt(i);
xml.tag("Element", { { "type", int(e.type) }, { "x", e.x }, { "y", e.y } });
double spatium = item->spatium();
double x = e.x / spatium;
double y = e.y / spatium;
xml.tag("Element", { { "type", int(e.type) }, { "x", x }, { "y", y } });
}
xml.endElement();
}
@ -2128,9 +2131,6 @@ void TWrite::write(const Pedal* item, XmlWriter& xml, WriteContext& ctx)
for (auto i : {
Pid::END_HOOK_TYPE,
Pid::BEGIN_TEXT,
Pid::CONTINUE_TEXT,
Pid::END_TEXT,
Pid::LINE_VISIBLE,
Pid::BEGIN_HOOK_TYPE
}) {

View File

@ -266,12 +266,19 @@ bool MStyle::readTextStyleValCompat(XmlReader& e)
return true;
}
void MStyle::readVersion(String versionTag)
{
versionTag.remove(u".");
m_version = versionTag.toInt();
}
bool MStyle::read(IODevice* device, bool ign)
{
UNUSED(ign);
XmlReader e(device);
while (e.readNextStartElement()) {
if (e.name() == "museScore") {
readVersion(e.attribute("version"));
while (e.readNextStartElement()) {
if (e.name() == "Style") {
read(e, nullptr);
@ -333,16 +340,16 @@ void MStyle::read(XmlReader& e, compat::ReadChordListHook* readChordListHook)
|| tag == "propertyDistanceHead"
|| tag == "propertyDistanceStem"
|| tag == "propertyDistance")
&& defaultStyleVersion() < 400) {
&& m_version < 400) {
// Ignoring pre-4.0 articulation style settings. Using the new defaults instead
e.skipCurrentElement();
} else if ((tag == "bracketDistance")
&& defaultStyleVersion() < 400) {
&& m_version < 400) {
// Ignoring pre-4.0 brackets distance settings. Using the new defaults instead.
e.skipCurrentElement();
} else if (tag == "pedalListStyle") { // pre-3.6.3/4.0 typo
set(Sid::pedalLineStyle, TConv::fromXml(e.readAsciiText(), LineType::SOLID));
} else if (tag == "chordlineThickness" && defaultStyleVersion() <= 400) {
} else if (tag == "chordlineThickness" && m_version < 410) {
// Ignoring pre-4.1 value as it was wrong (it wasn't user-editable anyway)
e.skipCurrentElement();
} else if (!readProperties(e)) {

Some files were not shown because too many files have changed in this diff Show More