Compare commits
322 Commits
master
...
various_fi
Author | SHA1 | Date |
---|---|---|
Michele Spagnolo | 84b2da24ba | |
Roman Pudashkin | 4845e85c92 | |
Michele Spagnolo | 0332c98049 | |
Michele Spagnolo | 19aebf4b24 | |
Michele Spagnolo | 8720b7e369 | |
Michele Spagnolo | 78de2aa45b | |
Michele Spagnolo | 4bd86ee768 | |
Roman Pudashkin | b041f08fbb | |
Roman Pudashkin | b63985fe36 | |
Roman Pudashkin | c89568e415 | |
Roman Pudashkin | b68bf5afe4 | |
Roman Pudashkin | 34909e9628 | |
Roman Pudashkin | b26d107c90 | |
Roman Pudashkin | 0318fead3a | |
Roman Pudashkin | cc433ebf4d | |
Roman Pudashkin | 52eba20f9b | |
Roman Pudashkin | d54ca91964 | |
Roman Pudashkin | 372a4a2e31 | |
Roman Pudashkin | eeeb361a0a | |
Roman Pudashkin | 0904b16ed9 | |
Eism | e925f0b433 | |
Eism | 8118cb1e8c | |
Eism | 35acc7ab78 | |
Elnur Ismailzada | e4d1ddf207 | |
Hemant Antony | 287a112587 | |
Elnur Ismailzada | 29aee24a08 | |
Eism | d761025ef0 | |
Elnur Ismailzada | 691f32fca5 | |
Michele Spagnolo | 2426a6c0cb | |
Michele Spagnolo | 4425e4c2ff | |
Michele Spagnolo | ef87e7bc18 | |
RomanPudashkin | dfdbbec1f8 | |
sammik | bc104082e0 | |
Michele Spagnolo | 66caccdd84 | |
RomanPudashkin | 4ec2d5aa5f | |
Casper Jeukendrup | c0eebd61aa | |
RomanPudashkin | e3bd01d50b | |
Roman Pudashkin | 6d151c9bcd | |
RomanPudashkin | 89f803a626 | |
DmitryArefiev | c727018bfb | |
DmitryArefiev | 9ab54e516d | |
Roman Pudashkin | 30226e7a9b | |
RomanPudashkin | 645173696d | |
RomanPudashkin | 9d26cde696 | |
Michele Spagnolo | 4af016592e | |
Michele Spagnolo | cfab3e94ef | |
RomanPudashkin | e8b56e1eb5 | |
Roman Pudashkin | baa7ab5c82 | |
RomanPudashkin | 83013d1758 | |
Casper Jeukendrup | ed3af415ef | |
Roman Pudashkin | d2db8d64eb | |
Casper Jeukendrup | 8f95eabbd7 | |
Casper Jeukendrup | 7bb6fd5e46 | |
Casper Jeukendrup | a600e50bd3 | |
RomanPudashkin | 8a22a567fe | |
Roman Pudashkin | 562c30440b | |
Roman Pudashkin | 287c42c488 | |
Roman Pudashkin | eb6885acb2 | |
Roman Pudashkin | b06fb2ab41 | |
Roman Pudashkin | eb11848e20 | |
Roman Pudashkin | 6c6be0b427 | |
Eism | c846375319 | |
Eism | 5963517a63 | |
Eism | 1d877eb508 | |
Eism | ae894f0487 | |
Eism | fd6f128a9d | |
Eism | 4184a43628 | |
Michele Spagnolo | bf9f22678e | |
RomanPudashkin | 80ec82e238 | |
Alexander Pavlov | 46ac49ee84 | |
RomanPudashkin | 2adc859831 | |
Casper Jeukendrup | ba8b8997d5 | |
Michele Spagnolo | f40e8eb0ad | |
adazem009 | 5af920ac1f | |
Eism | da513e13c7 | |
Roman Pudashkin | e3b5be2632 | |
Roman Pudashkin | a968fde3d5 | |
RomanPudashkin | 2e3a93abe6 | |
Roman Pudashkin | 0423c039fd | |
Casper Jeukendrup | 7809bd6827 | |
Casper Jeukendrup | d4b0566e18 | |
Casper Jeukendrup | 05b05c53a4 | |
Casper Jeukendrup | 451698f2f2 | |
Casper Jeukendrup | 4bcbfe67a7 | |
RomanPudashkin | 9b8469880e | |
RomanPudashkin | 18898935f0 | |
Elnur Ismailzada | af43a827cd | |
Eism | 8cee8c434a | |
Roman Pudashkin | eb9ede0954 | |
RomanPudashkin | eaa5398ddd | |
Michele Spagnolo | 4648c6fdf0 | |
Casper Jeukendrup | 6ffd47906c | |
RomanPudashkin | 3fcb045694 | |
Michele Spagnolo | bfb85e8d19 | |
Michele Spagnolo | 029f900a74 | |
Michele Spagnolo | bcae85ee83 | |
Michele Spagnolo | dae97c236a | |
RomanPudashkin | d983c71886 | |
RomanPudashkin | 7fcb906c67 | |
sammik | 3e43ade74f | |
sammik | b7d6b587d8 | |
Casper Jeukendrup | 0c23c22c35 | |
Casper Jeukendrup | a9348f4ac2 | |
Casper Jeukendrup | e03c17b134 | |
RomanPudashkin | eb4cd16e51 | |
Casper Jeukendrup | a46041f569 | |
RomanPudashkin | 5f333a2466 | |
Roman Pudashkin | 2c59d5db24 | |
RomanPudashkin | 0759bc9037 | |
Michele Spagnolo | bc1b2d1f8e | |
RomanPudashkin | aa456a35b2 | |
sammik | 294cf58d67 | |
RomanPudashkin | 193bce042a | |
RomanPudashkin | e782848712 | |
Roman Pudashkin | 6184db8142 | |
RomanPudashkin | 151e4cf294 | |
asattely | 8c11cb7421 | |
Eism | 9bd8886025 | |
RomanPudashkin | a4f87747a9 | |
Eism | 321b629ed5 | |
Eism | 0db45dd77a | |
Casper Jeukendrup | 690c09639d | |
cbjeukendrup | a23cba5926 | |
RomanPudashkin | f4cbb874a9 | |
RomanPudashkin | 22763af6ec | |
Eism | 9e1219033b | |
asattely | dffa31a96a | |
Casper Jeukendrup | ca2af7f5fe | |
Casper Jeukendrup | d18515dbb1 | |
Casper Jeukendrup | 95d0965617 | |
I Woithe | 40f190e3bc | |
Eism | e420fc8cdb | |
Roman Pudashkin | 4b0d5dbb1b | |
Roman Pudashkin | 67c6a5e5ac | |
Roman Pudashkin | 8eb364b127 | |
Roman Pudashkin | 1314082159 | |
Roman Pudashkin | 207c1dc79c | |
Roman Pudashkin | a60c7f05c6 | |
RomanPudashkin | 8b2ee61ef7 | |
RomanPudashkin | 54cb194808 | |
RomanPudashkin | c8031c28e7 | |
Casper Jeukendrup | 6f400f8050 | |
Casper Jeukendrup | ee9fefd074 | |
RomanPudashkin | 0d75514334 | |
RomanPudashkin | 7b8f7824e2 | |
Roman Pudashkin | afbfe78eb7 | |
Roman Pudashkin | dd0a019e93 | |
RomanPudashkin | 1a4eb551e6 | |
bakajikara | 38457a2e3d | |
Michele Spagnolo | 87568fd528 | |
RomanPudashkin | 59d706e50c | |
bakajikara | 6c454debcc | |
bakajikara | 7931f5068d | |
bakajikara | 2f4f9c2101 | |
bakajikara | 9ee7db3874 | |
bakajikara | 5a0bb72598 | |
bakajikara | 54055bf289 | |
RomanPudashkin | 4cffb3191d | |
RomanPudashkin | 351e7bd7a7 | |
RomanPudashkin | a672ddd58d | |
Hemant Antony | 483803afa7 | |
Casper Jeukendrup | e0840cdfd8 | |
Roman Pudashkin | d79cbfaf98 | |
RomanPudashkin | 1654979c9b | |
RomanPudashkin | b485208874 | |
Casper Jeukendrup | 97b069523d | |
RomanPudashkin | 9fb7ace5e7 | |
Casper Jeukendrup | 646d635010 | |
Casper Jeukendrup | 523586d4a4 | |
RomanPudashkin | 88bd571371 | |
RomanPudashkin | d68bf6b7ea | |
Casper Jeukendrup | 6c47335ee0 | |
RomanPudashkin | ba98347dec | |
Casper Jeukendrup | 548a9f988c | |
Roman Pudashkin | b3133635ff | |
RomanPudashkin | eba898734c | |
RomanPudashkin | 70d0a7c98b | |
RomanPudashkin | 73a4c8fda1 | |
RomanPudashkin | 2dafc60512 | |
Michele Spagnolo | 2b36756e18 | |
RomanPudashkin | d52a31cf04 | |
Casper Jeukendrup | 0e63b40017 | |
RomanPudashkin | 053f4991e3 | |
Eism | 750b492373 | |
Eism | 6b141b7aae | |
RomanPudashkin | 7bfcbece58 | |
RomanPudashkin | c6abf259d9 | |
Casper Jeukendrup | 9fb1d7e138 | |
Casper Jeukendrup | ead6c2141b | |
asattely | dcd1d3371c | |
asattely | 43539bf8a2 | |
RomanPudashkin | e1df0ae712 | |
RomanPudashkin | 2e42e6aac3 | |
Casper Jeukendrup | f2af14a914 | |
cbjeukendrup | d88e882c86 | |
Alexander Pavlov | 03c8f7d476 | |
Hemant Antony | 9891f275b6 | |
Hemant Antony | c820bd447b | |
RomanPudashkin | c2ca4674a9 | |
RomanPudashkin | 134c7f3af1 | |
Eism | c285343daa | |
Eism | aede10d67b | |
RomanPudashkin | 6ff8c4ff1b | |
RomanPudashkin | 89b5a47d7e | |
Roman Pudashkin | 3044e5ded4 | |
Roman Pudashkin | 5c63cd785f | |
Roman Pudashkin | b628a80e83 | |
Roman Pudashkin | 27ae291997 | |
RomanPudashkin | 7185eb8260 | |
asattely | 0158c5b176 | |
asattely | 64335fa7e6 | |
asattely | f476a00a21 | |
asattely | 9073b5ae40 | |
asattely | 6b5db2bcbd | |
asattely | 5e02ebcf02 | |
Casper Jeukendrup | baa047dd4a | |
RomanPudashkin | f32d5858bf | |
Peter Jonas | f7fab29515 | |
Michele Spagnolo | 8e9b37a2dc | |
Michele Spagnolo | 5f91a4cb73 | |
Michele Spagnolo | f02c043b06 | |
Michele Spagnolo | df4a0439bd | |
RomanPudashkin | 2d1c3b47d1 | |
RomanPudashkin | 5928dfd1e6 | |
Casper Jeukendrup | 2604906b5d | |
Casper Jeukendrup | 57553606e1 | |
Casper Jeukendrup | 63b3499932 | |
Casper Jeukendrup | ea50c6d538 | |
Hemant Antony | 8294b3b1e1 | |
RomanPudashkin | fa42edc4e4 | |
Casper Jeukendrup | 0e27fb3e82 | |
Casper Jeukendrup | 002eef3709 | |
RomanPudashkin | 11c4c02d07 | |
RomanPudashkin | ce4afc9a7d | |
Elnur Ismailzada | 161c74da28 | |
Roman Pudashkin | 96e0ab3b81 | |
Eism | 25dcb7b3f0 | |
Elnur Ismailzada | 28512d0761 | |
Eism | 4a77c51e6b | |
Eism | 0143883d28 | |
Eism | 976a6aaa53 | |
Eism | 66d0bd55f5 | |
Eism | f2b6b2d6f2 | |
Hemant Antony | 4fe1b0db70 | |
Elnur Ismailzada | 37b87934a4 | |
Marc Sabatella | 43f124a526 | |
Eism | 6fe1a1f376 | |
RomanPudashkin | dfc286f3b0 | |
RomanPudashkin | b3b8efe5b2 | |
Roman Pudashkin | c5c089c8bc | |
Roman Pudashkin | a16439b2c7 | |
RomanPudashkin | 61bb83eadb | |
Hemant Antony | 67e4e732f1 | |
RomanPudashkin | 41a5ebe53c | |
Elnur Ismailzada | e01f1e8407 | |
Eism | 7b48df5599 | |
Eism | fb3c06b162 | |
Eism | 340107b10c | |
Elnur Ismailzada | b15601b9ea | |
RomanPudashkin | e493c1fff4 | |
Michele Spagnolo | e3c8f2b2b6 | |
RomanPudashkin | 1eebaf164d | |
Casper Jeukendrup | 482efeffce | |
Casper Jeukendrup | 1061b3b3f1 | |
Casper Jeukendrup | 3e8d2ae430 | |
Casper Jeukendrup | 40c5607cd7 | |
Casper Jeukendrup | 083424b92e | |
cbjeukendrup | 8f9aeb545c | |
Peter Jonas | bc2b409e5d | |
Casper Jeukendrup | d2acf7aa10 | |
RomanPudashkin | 053e1e40b3 | |
Joachim Schmitz | c3a6e7698a | |
worldwideweary | 819d62deb2 | |
worldwideweary | c3c043c4c2 | |
RomanPudashkin | 0e6ef99cf3 | |
Casper Jeukendrup | c317225488 | |
Casper Jeukendrup | 99ed8fde46 | |
Casper Jeukendrup | 84abc7faa0 | |
Casper Jeukendrup | 508327e3d6 | |
Joachim Schmitz | 6474828563 | |
Michele Spagnolo | f153091bc7 | |
asattely | a75ae35e3a | |
asattely | 423fbc30bd | |
asattely | 7580df2b17 | |
Casper Jeukendrup | fcaa649665 | |
cbjeukendrup | ff474eb382 | |
Casper Jeukendrup | a3827ef162 | |
Casper Jeukendrup | a63ed089fa | |
RomanPudashkin | e1197fe673 | |
Elnur Ismailzada | 683aab3c5a | |
Elnur Ismailzada | c1d4900229 | |
Michele Spagnolo | 0428219356 | |
Michele Spagnolo | b988d36445 | |
Michele Spagnolo | 311378527c | |
Michele Spagnolo | a468a17cde | |
Eism | 4f70f46167 | |
Eism | fac7ce310c | |
RomanPudashkin | 36d428a282 | |
RomanPudashkin | 3695542cce | |
Michele Spagnolo | ec735242d6 | |
Joachim Schmitz | b6484de64c | |
Joachim Schmitz | 91492890e8 | |
Casper Jeukendrup | 90a28b5eae | |
Casper Jeukendrup | b0f1da5d55 | |
Casper Jeukendrup | d38d9cd364 | |
cbjeukendrup | ec96a22dac | |
RomanPudashkin | 7d15ce0a43 | |
RomanPudashkin | a8d4af44c1 | |
Joachim Schmitz | 39183b5fb5 | |
Joachim Schmitz | 814feb3878 | |
RomanPudashkin | e20fedb5c2 | |
Eism | 12ee4c3099 | |
Eism | e91138b6cb | |
Eism | 28182c1729 | |
Eism | 6a2065dfb9 | |
Eism | 792317a4fd | |
Eism | b0f1074be6 | |
Eism | 723049d43d | |
RomanPudashkin | 462dcdb3aa | |
Casper Jeukendrup | bb023d2d95 | |
Casper Jeukendrup | 1175e334f2 | |
Casper Jeukendrup | da38073eaa |
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.1
|
||||
|
||||
jobs:
|
||||
codestyle:
|
||||
|
|
|
@ -17,7 +17,7 @@ on:
|
|||
default: ''
|
||||
|
||||
env:
|
||||
CURRENT_RELEASE_BRANCH: 4.0.2
|
||||
CURRENT_RELEASE_BRANCH: 4.1.0
|
||||
|
||||
jobs:
|
||||
build_armhf:
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.1
|
||||
|
||||
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:
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.1
|
||||
|
||||
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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.1
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
|
|
@ -4,7 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.0.2
|
||||
- 4.1.1
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.1
|
||||
|
||||
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
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.1
|
||||
|
||||
jobs:
|
||||
run_tests:
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -80,6 +80,7 @@ apt_packages_runtime=(
|
|||
libegl1-mesa-dev
|
||||
libodbc1
|
||||
libpq-dev
|
||||
libssl-dev
|
||||
libxcomposite-dev
|
||||
libxcursor-dev
|
||||
libxi-dev
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
@ -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>
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ StyledDialogView {
|
|||
title: qsTrc("appshell/preferences", "Preferences")
|
||||
|
||||
contentWidth: 880
|
||||
contentHeight: 600
|
||||
contentHeight: 640
|
||||
resizable: true
|
||||
|
||||
property string currentPageId: ""
|
||||
|
|
|
@ -463,7 +463,21 @@ void DockBase::setFramePanelOrder(int order)
|
|||
return;
|
||||
}
|
||||
|
||||
m_dockWidget->frameVisualItem()->setProperty("titleBarNavigationPanelOrder", order);
|
||||
auto frame = static_cast<const KDDockWidgets::FrameQuick*>(m_dockWidget->frame());
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!frame->beingDeletedLater()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QQuickItem* frameVisualItem = frame->visualItem();
|
||||
if (!frameVisualItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
frameVisualItem->setProperty("titleBarNavigationPanelOrder", order);
|
||||
}
|
||||
|
||||
void DockBase::resetToDefault()
|
||||
|
|
|
@ -79,6 +79,7 @@ void NavigableAppMenuModel::load()
|
|||
void NavigableAppMenuModel::handleMenuItem(const QString& itemId)
|
||||
{
|
||||
resetNavigation();
|
||||
restoreMUNavigationSystemState();
|
||||
|
||||
AppMenuModel::handleMenuItem(itemId);
|
||||
}
|
||||
|
@ -463,18 +464,31 @@ void NavigableAppMenuModel::saveMUNavigationSystemState()
|
|||
bool muNavigationIsHighlight = navigationController()->isHighlight();
|
||||
m_needActivateLastMUNavigationControl = muNavigationIsHighlight;
|
||||
|
||||
ui::INavigationControl* activeControl = navigationController()->activeControl();
|
||||
if (activeControl) {
|
||||
m_lastActiveMUNavigationControl = activeControl;
|
||||
activeControl->setActive(false);
|
||||
INavigationSection* section = navigationController()->activeSection();
|
||||
INavigationPanel* panel = navigationController()->activePanel();
|
||||
INavigationControl* control = navigationController()->activeControl();
|
||||
m_lastActiveMUNavigationState = {
|
||||
section ? section->name().toStdString() : "",
|
||||
panel ? panel->name().toStdString() : "",
|
||||
control ? control->name().toStdString() : ""
|
||||
};
|
||||
|
||||
if (control) {
|
||||
control->setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
void NavigableAppMenuModel::restoreMUNavigationSystemState()
|
||||
{
|
||||
if (m_lastActiveMUNavigationControl) {
|
||||
m_lastActiveMUNavigationControl->requestActive();
|
||||
m_lastActiveMUNavigationControl = nullptr;
|
||||
if (m_lastActiveMUNavigationState.has_value()) {
|
||||
MUNavigationSystemState state = m_lastActiveMUNavigationState.value();
|
||||
|
||||
bool ok = navigationController()->requestActivateByName(state.sectionName, state.panelName, state.controlName);
|
||||
if (!ok) {
|
||||
navigationController()->resetNavigation();
|
||||
}
|
||||
|
||||
m_lastActiveMUNavigationState.reset();
|
||||
}
|
||||
|
||||
navigationController()->setIsHighlight(m_needActivateLastMUNavigationControl);
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#ifndef MU_APPSHELL_NAVIGABLEAPPMENUMODEL_H
|
||||
#define MU_APPSHELL_NAVIGABLEAPPMENUMODEL_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "appmenumodel.h"
|
||||
|
@ -86,6 +88,12 @@ private:
|
|||
void resetNavigation();
|
||||
void navigateToFirstMenu();
|
||||
|
||||
struct MUNavigationSystemState {
|
||||
std::string sectionName;
|
||||
std::string panelName;
|
||||
std::string controlName;
|
||||
};
|
||||
|
||||
void saveMUNavigationSystemState();
|
||||
void restoreMUNavigationSystemState();
|
||||
|
||||
|
@ -100,7 +108,7 @@ private:
|
|||
QString m_openedMenuId;
|
||||
|
||||
bool m_needActivateHighlight = true;
|
||||
ui::INavigationControl* m_lastActiveMUNavigationControl = nullptr;
|
||||
std::optional<MUNavigationSystemState> m_lastActiveMUNavigationState;
|
||||
bool m_needActivateLastMUNavigationControl = false;
|
||||
|
||||
QWindow* m_appWindow = nullptr;
|
||||
|
|
|
@ -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")
|
||||
};
|
||||
|
||||
|
|
|
@ -214,13 +214,12 @@ void updateTableForLyrics(std::string table)
|
|||
|
||||
std::string braille_translate(const char* table_name, std::string txt)
|
||||
{
|
||||
//cout << "braille_translate " << table_name << " " << txt << "\n";
|
||||
uint8_t* outputbuf;
|
||||
size_t outlen;
|
||||
uint8_t* outputbuf = nullptr;
|
||||
size_t outlen = 0;
|
||||
widechar inbuf[MAXSTRING];
|
||||
widechar transbuf[MAXSTRING];
|
||||
int inlen;
|
||||
int translen;
|
||||
int inlen = 0;
|
||||
int translen = 0;
|
||||
|
||||
inlen = _lou_extParseChars(txt.c_str(), inbuf);
|
||||
|
||||
|
@ -235,9 +234,13 @@ std::string braille_translate(const char* table_name, std::string txt)
|
|||
outputbuf = u16_to_u8(transbuf, translen, NULL, &outlen);
|
||||
#endif
|
||||
|
||||
if (!outputbuf) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string ret = std::string(outputbuf, outputbuf + outlen);
|
||||
free(outputbuf);
|
||||
//cout << " " << ret << "\n";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
@ -753,7 +756,7 @@ void BackendApi::switchToPageView(IMasterNotationPtr masterNotation)
|
|||
{
|
||||
//! NOTE: All operations must be done in page view mode
|
||||
masterNotation->notation()->setViewMode(ViewMode::PAGE);
|
||||
for (IExcerptNotationPtr excerpt : masterNotation->excerpts().val) {
|
||||
for (IExcerptNotationPtr excerpt : masterNotation->excerpts()) {
|
||||
excerpt->notation()->setViewMode(ViewMode::PAGE);
|
||||
}
|
||||
}
|
||||
|
@ -762,7 +765,7 @@ void BackendApi::renderExcerptsContents(IMasterNotationPtr masterNotation)
|
|||
{
|
||||
//! NOTE: Due to optimization, only the master score is layouted
|
||||
//! Let's layout all the scores of the excerpts
|
||||
for (IExcerptNotationPtr excerpt : masterNotation->excerpts().val) {
|
||||
for (IExcerptNotationPtr excerpt : masterNotation->excerpts()) {
|
||||
Score* score = excerpt->notation()->elements()->msScore();
|
||||
if (!score->autoLayoutEnabled()) {
|
||||
score->doLayout();
|
||||
|
@ -774,7 +777,7 @@ ExcerptNotationList BackendApi::allExcerpts(notation::IMasterNotationPtr masterN
|
|||
{
|
||||
initPotentialExcerpts(masterNotation);
|
||||
|
||||
ExcerptNotationList excerpts = masterNotation->excerpts().val;
|
||||
ExcerptNotationList excerpts = masterNotation->excerpts();
|
||||
ExcerptNotationList potentialExcerpts = masterNotation->potentialExcerpts();
|
||||
excerpts.insert(excerpts.end(), potentialExcerpts.begin(), potentialExcerpts.end());
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -240,7 +240,7 @@ mu::Ret ConverterController::convertScorePartsToPdf(INotationWriterPtr writer, I
|
|||
INotationPtrList notations;
|
||||
notations.push_back(masterNotation->notation());
|
||||
|
||||
for (IExcerptNotationPtr e : masterNotation->excerpts().val) {
|
||||
for (IExcerptNotationPtr e : masterNotation->excerpts()) {
|
||||
notations.push_back(e->notation());
|
||||
}
|
||||
|
||||
|
@ -275,7 +275,7 @@ mu::Ret ConverterController::convertScorePartsToPngs(INotationWriterPtr writer,
|
|||
}
|
||||
|
||||
INotationPtrList excerpts;
|
||||
for (IExcerptNotationPtr e : masterNotation->excerpts().val) {
|
||||
for (IExcerptNotationPtr e : masterNotation->excerpts()) {
|
||||
excerpts.push_back(e->notation());
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -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) {
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -143,6 +143,7 @@ MasterScore* EngravingProject::masterScore() const
|
|||
Ret EngravingProject::loadMscz(const MscReader& msc, SettingsCompat& settingsCompat, bool ignoreVersionError)
|
||||
{
|
||||
TRACEFUNC;
|
||||
|
||||
MScore::setError(MsError::MS_NO_ERROR);
|
||||
MscLoader loader;
|
||||
return loader.loadMscz(m_masterScore, msc, settingsCompat, ignoreVersionError);
|
||||
|
@ -151,13 +152,9 @@ Ret EngravingProject::loadMscz(const MscReader& msc, SettingsCompat& settingsCom
|
|||
bool EngravingProject::writeMscz(MscWriter& writer, bool onlySelection, bool createThumbnail)
|
||||
{
|
||||
TRACEFUNC;
|
||||
MscSaver saver;
|
||||
bool ok = saver.writeMscz(m_masterScore, writer, onlySelection, createThumbnail);
|
||||
if (ok && !onlySelection) {
|
||||
m_masterScore->update();
|
||||
}
|
||||
|
||||
return ok;
|
||||
MscSaver saver;
|
||||
return saver.writeMscz(m_masterScore, writer, onlySelection, createThumbnail);
|
||||
}
|
||||
|
||||
bool EngravingProject::isCorruptedUponLoading() const
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
@ -1857,7 +1882,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
|
|||
for (Chord* chord : chords) {
|
||||
Ornament* ornament = chord->findOrnament();
|
||||
if (ornament && ornament->showCueNote()) {
|
||||
TLayout::layout(ornament, ctx);
|
||||
TLayout::layoutOrnamentCueNote(ornament, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -2171,10 +2292,11 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
|
|||
int prevSubtype = 0;
|
||||
int prevLine = std::numeric_limits<int>::min();
|
||||
|
||||
bool isTab = staff->isTabStaff(tick);
|
||||
for (int i = nNotes - 1; i >= 0; --i) {
|
||||
Note* note = notes[i];
|
||||
Accidental* ac = note->accidental();
|
||||
if (ac && ac->subtype() == prevSubtype && note->line() == prevLine) {
|
||||
if (ac && (isTab || (ac->subtype() == prevSubtype && note->line() == prevLine))) { // TODO: probably this function shouldn't be called at all for tab staves
|
||||
// we shouldn't have two of the same accidental on the same line.
|
||||
// if we find one that is identical to the one before it, don't lay it out
|
||||
ac->setbbox(RectF());
|
||||
|
@ -2275,6 +2397,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 +2414,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 +2429,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 +2450,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 +2692,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 +2972,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 +3276,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 +3360,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()) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1777,31 +1777,29 @@ void MeasureLayout::addSystemTrailer(Measure* m, Measure* nm, LayoutContext& ctx
|
|||
m->add(s);
|
||||
}
|
||||
|
||||
if (staffIsPitchedAtNextMeas) {
|
||||
KeySig* ks = toKeySig(s->element(track));
|
||||
KeySigEvent key2 = staff->keySigEvent(m->endTick());
|
||||
KeySig* keySig = nullptr;
|
||||
EngravingItem* keySigElem = s->element(track);
|
||||
if (keySigElem && keySigElem->isKeySig()) {
|
||||
keySig = toKeySig(keySigElem);
|
||||
}
|
||||
|
||||
if (!ks) {
|
||||
ks = Factory::createKeySig(s);
|
||||
ks->setTrack(track);
|
||||
ks->setGenerated(true);
|
||||
ks->setParent(s);
|
||||
s->add(ks);
|
||||
KeySigEvent key2 = staff->keySigEvent(m->endTick());
|
||||
bool needsCourtesy = staff->key(m->tick()) != key2.key();
|
||||
|
||||
if (staffIsPitchedAtNextMeas && needsCourtesy) {
|
||||
if (!keySig) {
|
||||
keySig = Factory::createKeySig(s);
|
||||
keySig->setTrack(track);
|
||||
keySig->setGenerated(true);
|
||||
keySig->setParent(s);
|
||||
s->add(keySig);
|
||||
s->setTrailer(true);
|
||||
}
|
||||
//else if (!(ks->keySigEvent() == key2)) {
|
||||
// score()->undo(new ChangeKeySig(ks, key2, ks->showCourtesy()));
|
||||
// }
|
||||
ks->setKeySigEvent(key2);
|
||||
TLayout::layout(ks, ctx);
|
||||
keySig->setKeySigEvent(key2);
|
||||
TLayout::layout(keySig, ctx);
|
||||
//s->createShape(track / VOICES);
|
||||
s->setEnabled(true);
|
||||
} else { /// !staffIsPitchedAtNextMeas
|
||||
KeySig* keySig = nullptr;
|
||||
EngravingItem* keySigElem = s->element(track);
|
||||
if (keySigElem && keySigElem->isKeySig()) {
|
||||
keySig = toKeySig(keySigElem);
|
||||
}
|
||||
} else { /// !staffIsPitchedAtNextMeas || !needsCourtesy
|
||||
if (keySig) {
|
||||
s->remove(keySig);
|
||||
}
|
||||
|
|
|
@ -1556,8 +1556,14 @@ void SystemLayout::manageNarrowSpacing(System* system, LayoutContext& ctx, doubl
|
|||
// (empiric compromise between looking good and not taking too many iterations)
|
||||
static constexpr double squeezeLimit = 0.3; // For some spaces, do not go below 30%
|
||||
|
||||
Measure* firstMeasure = system->firstMeasure();
|
||||
if (!firstMeasure) {
|
||||
// Happens for a system that only consists of a frame, for example a too-wide horizontal frame
|
||||
return;
|
||||
}
|
||||
|
||||
// First, try to gradually reduce the duration stretch (i.e. flatten the spacing curve)
|
||||
double stretchCoeff = system->firstMeasure()->layoutStretch() - step;
|
||||
double stretchCoeff = firstMeasure->layoutStretch() - step;
|
||||
while (curSysWidth > targetSysWidth && RealIsEqualOrMore(stretchCoeff, 0.0)) {
|
||||
for (MeasureBase* mb : system->measures()) {
|
||||
if (!mb->isMeasure()) {
|
||||
|
|
|
@ -1516,15 +1516,15 @@ void TLayout::layout(Expression* item, LayoutContext& ctx)
|
|||
track_idx_t startTrack = track2staff(item->staffIdx());
|
||||
track_idx_t endTrack = startTrack + VOICES;
|
||||
for (track_idx_t track = startTrack; track < endTrack; ++track) {
|
||||
EngravingItem* engravingItem = segment->elementAt(track);
|
||||
if (engravingItem && engravingItem->isChord()) {
|
||||
chordToAlign = toChord(item);
|
||||
EngravingItem* e = segment->elementAt(track);
|
||||
if (e && e->isChord()) {
|
||||
chordToAlign = toChord(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (chordToAlign) {
|
||||
Note* note = chordToAlign->notes().at(0);
|
||||
if (chordToAlign && !chordToAlign->notes().empty()) {
|
||||
const Note* note = chordToAlign->notes().at(0);
|
||||
double headWidth = note->headWidth();
|
||||
bool center = item->align().horizontal == AlignH::HCENTER;
|
||||
item->movePosX(headWidth * (center ? 0.5 : 1));
|
||||
|
@ -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)
|
||||
|
@ -3568,24 +3586,24 @@ void TLayout::layout(Ornament* item, LayoutContext& ctx)
|
|||
double vertMargin = 0.35 * _spatium;
|
||||
static constexpr double ornamentAccidentalMag = 0.6; // TODO: style?
|
||||
|
||||
if (!item->showCueNote()) {
|
||||
for (size_t i = 0; i < item->accidentalsAboveAndBelow().size(); ++i) {
|
||||
bool above = (i == 0);
|
||||
Accidental* accidental = item->accidentalsAboveAndBelow()[i];
|
||||
if (!accidental) {
|
||||
continue;
|
||||
}
|
||||
accidental->computeMag();
|
||||
accidental->setMag(accidental->mag() * ornamentAccidentalMag);
|
||||
layout(accidental, ctx);
|
||||
Shape accidentalShape = accidental->shape();
|
||||
double minVertDist = above ? accidentalShape.minVerticalDistance(item->bbox()) : Shape(item->bbox()).minVerticalDistance(
|
||||
accidentalShape);
|
||||
accidental->setPos(-0.5 * accidental->width(), above ? (-minVertDist - vertMargin) : (minVertDist + vertMargin));
|
||||
for (size_t i = 0; i < item->accidentalsAboveAndBelow().size(); ++i) {
|
||||
bool above = (i == 0);
|
||||
Accidental* accidental = item->accidentalsAboveAndBelow()[i];
|
||||
if (!accidental) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
accidental->computeMag();
|
||||
accidental->setMag(accidental->mag() * ornamentAccidentalMag);
|
||||
layout(accidental, ctx);
|
||||
Shape accidentalShape = accidental->shape();
|
||||
double minVertDist = above ? accidentalShape.minVerticalDistance(item->bbox()) : Shape(item->bbox()).minVerticalDistance(
|
||||
accidentalShape);
|
||||
accidental->setPos(-0.5 * accidental->width(), above ? (-minVertDist - vertMargin) : (minVertDist + vertMargin));
|
||||
}
|
||||
}
|
||||
|
||||
void TLayout::layoutOrnamentCueNote(Ornament* item, LayoutContext& ctx)
|
||||
{
|
||||
if (!item->explicitParent()) {
|
||||
return;
|
||||
}
|
||||
|
@ -3593,9 +3611,20 @@ void TLayout::layout(Ornament* item, LayoutContext& ctx)
|
|||
Chord* parentChord = toChord(item->parentItem());
|
||||
Chord* cueNoteChord = item->cueNoteChord();
|
||||
|
||||
Note* cueNote = cueNoteChord->notes().front();
|
||||
if (!cueNoteChord) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<Note*>& notes = cueNoteChord->notes();
|
||||
Note* cueNote = notes.empty() ? nullptr : notes.front();
|
||||
|
||||
if (!cueNote) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChordLayout::layoutChords3(ctx.style(), { cueNoteChord }, { cueNote }, item->staff(), ctx);
|
||||
layout(cueNoteChord, ctx);
|
||||
|
||||
Shape noteShape = cueNoteChord->shape();
|
||||
Shape parentChordShape = parentChord->shape();
|
||||
double minDist = parentChordShape.minHorizontalDistance(noteShape);
|
||||
|
@ -4194,13 +4223,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;
|
||||
|
||||
|
@ -4494,7 +4524,7 @@ void TLayout::layout1TextBase(TextBase* item, LayoutContext& ctx)
|
|||
if (item->layoutToParentWidth()) {
|
||||
if (item->explicitParent()->isTBox()) {
|
||||
// hack: vertical alignment is always TOP
|
||||
item->setAlign(AlignV::TOP);
|
||||
item->setAlign({ item->align().horizontal, AlignV::TOP });
|
||||
} else if (item->explicitParent()->isBox()) {
|
||||
// consider inner margins of frame
|
||||
Box* b = toBox(item->explicitParent());
|
||||
|
|
|
@ -258,6 +258,7 @@ public:
|
|||
static void layout(NoteDot* item, LayoutContext& ctx);
|
||||
|
||||
static void layout(Ornament* item, LayoutContext& ctx);
|
||||
static void layoutOrnamentCueNote(Ornament* item, LayoutContext& ctx);
|
||||
|
||||
static void layout(Ottava* item, LayoutContext& ctx);
|
||||
static void layout(OttavaSegment* item, LayoutContext& ctx);
|
||||
|
|
|
@ -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) {
|
||||
|
@ -2792,7 +2794,7 @@ Shape Chord::shape() const
|
|||
for (LedgerLine* l = _ledgerLines; l; l = l->next()) {
|
||||
shape.add(l->shape().translate(l->pos()));
|
||||
}
|
||||
if (m_beamlet) {
|
||||
if (m_beamlet && _stem) {
|
||||
double xPos = m_beamlet->line.p1().x() - _stem->xpos();
|
||||
if (m_beamlet->isBefore && !m_up) {
|
||||
xPos -= _stem->width();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -255,7 +254,10 @@ static Note::SlideType slideType(ChordLineType type)
|
|||
void ChordLine::setNote(Note* note)
|
||||
{
|
||||
m_note = note;
|
||||
note->attachSlide(slideType(m_chordLineType));
|
||||
|
||||
if (note) {
|
||||
note->attachSlide(slideType(m_chordLineType));
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -329,9 +329,7 @@ void Score::undoRedo(bool undo, EditData* ed)
|
|||
range.changedStyleIdSet = std::move(changes.changedStyleIdSet);
|
||||
}
|
||||
|
||||
if (range.isValid()) {
|
||||
changesChannel().send(range);
|
||||
}
|
||||
changesChannel().send(range);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -371,12 +369,11 @@ void Score::endCmd(bool rollback, bool layoutAllParts)
|
|||
|
||||
if (dirty()) {
|
||||
masterScore()->setPlaylistDirty(); // TODO: flag individual operations
|
||||
masterScore()->setAutosaveDirty(true);
|
||||
}
|
||||
|
||||
cmdState().reset();
|
||||
|
||||
if (!rollback && range.isValid()) {
|
||||
if (!rollback) {
|
||||
changesChannel().send(range);
|
||||
}
|
||||
}
|
||||
|
@ -556,9 +553,9 @@ void Score::cmdAddSpanner(Spanner* spanner, const PointF& pos, bool systemStaves
|
|||
// used when applying a spanner to a selection
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Score::cmdAddSpanner(Spanner* spanner, staff_idx_t staffIdx, Segment* startSegment, Segment* endSegment)
|
||||
void Score::cmdAddSpanner(Spanner* spanner, staff_idx_t staffIdx, Segment* startSegment, Segment* endSegment, bool ctrlModifier)
|
||||
{
|
||||
track_idx_t track = spanner->systemFlag() ? 0 : staffIdx * VOICES; // system lines always on top staff
|
||||
track_idx_t track = staffIdx * VOICES;
|
||||
spanner->setTrack(track);
|
||||
spanner->setTrack2(track);
|
||||
for (auto ss : spanner->spannerSegments()) {
|
||||
|
@ -574,7 +571,7 @@ void Score::cmdAddSpanner(Spanner* spanner, staff_idx_t staffIdx, Segment* start
|
|||
tick2 = endSegment->tick();
|
||||
}
|
||||
spanner->setTick2(tick2);
|
||||
undoAddElement(spanner);
|
||||
undoAddElement(spanner, true, ctrlModifier);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -659,8 +656,14 @@ void Score::addInterval(int val, const std::vector<Note*>& nl)
|
|||
{
|
||||
// Prepare note selection in case there are not selected tied notes and sort them
|
||||
std::vector<Note*> tmpnl;
|
||||
bool shouldSelectFirstNote = nl.size() == 1 && nl[0]->tieFor();
|
||||
for (auto n : nl) {
|
||||
std::vector<Note*> _nl = nl;
|
||||
bool shouldSelectFirstNote = _nl.size() == 1 && _nl[0]->tieFor();
|
||||
|
||||
std::sort(_nl.begin(), _nl.end(), [](const Note* a, const Note* b) -> bool {
|
||||
return a->tick() < b->tick();
|
||||
});
|
||||
|
||||
for (auto n : _nl) {
|
||||
if (std::find(tmpnl.begin(), tmpnl.end(), n) != tmpnl.end()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -755,6 +758,7 @@ void Score::addInterval(int val, const std::vector<Note*>& nl)
|
|||
tie->setTick2(note->tick());
|
||||
note->setTieBack(tie);
|
||||
undoAddElement(tie);
|
||||
prevTied = nullptr;
|
||||
}
|
||||
if (on->tieFor()) {
|
||||
Tie* tie = Factory::createTie(this->dummy());
|
||||
|
@ -2010,9 +2014,7 @@ static void changeAccidental2(Note* n, int pitch, int tpc)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
Note* nn = n;
|
||||
while (nn->tieFor()) {
|
||||
nn = nn->tieFor()->endNote();
|
||||
for (Note* nn = n; nn && nn->tieFor(); nn = nn->tieFor()->endNote()) {
|
||||
score->undo(new ChangePitch(nn, pitch, tpc1, tpc2));
|
||||
}
|
||||
}
|
||||
|
@ -3051,9 +3053,17 @@ void Score::realtimeAdvance()
|
|||
return;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// cmdInsertClef
|
||||
//---------------------------------------------------------
|
||||
bool Score::canInsertClef(ClefType type) const
|
||||
{
|
||||
if (type == ClefType::INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Staff* staff = this->staff(inputTrack() / VOICES);
|
||||
const ChordRest* cr = inputState().cr();
|
||||
|
||||
return staff && cr;
|
||||
}
|
||||
|
||||
void Score::cmdInsertClef(ClefType type)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -2969,9 +2968,16 @@ void Score::deleteMeasures(MeasureBase* mbStart, MeasureBase* mbEnd, bool preser
|
|||
continue;
|
||||
}
|
||||
|
||||
if (mBeforeSel
|
||||
&& staff(staffIdx)->timeSig(mBeforeSel->tick())->sig() == lastDeletedForThisStaff->sig()) {
|
||||
continue;
|
||||
if (mBeforeSel) {
|
||||
const Staff* staff = this->staff(staffIdx);
|
||||
if (!staff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const TimeSig* timeSig = staff->timeSig(mBeforeSel->tick());
|
||||
if (!timeSig || timeSig->sig() == lastDeletedForThisStaff->sig()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s) {
|
||||
|
@ -3374,14 +3380,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 +3410,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 +3422,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 +3455,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 +3501,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 +3516,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 +4987,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 +5008,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 +5034,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5071,6 +5093,10 @@ void Score::updateInstrumentChangeTranspositions(KeySigEvent& key, Staff* staff,
|
|||
|
||||
void Score::undoChangeClef(Staff* ostaff, EngravingItem* e, ClefType ct, bool forInstrumentChange)
|
||||
{
|
||||
IF_ASSERT_FAILED(ostaff && e) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool moveClef = false;
|
||||
SegmentType st = SegmentType::Clef;
|
||||
if (e->isMeasure()) {
|
||||
|
@ -5696,13 +5722,13 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
|
|||
|| (et == ElementType::TEMPO_TEXT)
|
||||
|| isSystemLine
|
||||
) {
|
||||
std::vector<Staff* > staffList;
|
||||
std::list<Staff* > staffList;
|
||||
|
||||
if (!addToLinkedStaves || (ctrlModifier && isSystemLine)) {
|
||||
if (!addToLinkedStaves) {
|
||||
staffList.push_back(element->staff());
|
||||
if (ctrlModifier && isSystemLine) {
|
||||
element->setSystemFlag(false);
|
||||
}
|
||||
} else if (ctrlModifier && isSystemLine) {
|
||||
staffList = ostaff->staffList();
|
||||
element->setSystemFlag(false);
|
||||
} else {
|
||||
for (Score* s : scoreList()) {
|
||||
staffList.push_back(s->staff(0)); // system objects always appear on the top staff
|
||||
|
@ -5742,10 +5768,8 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
|
|||
if (isSystemLine) {
|
||||
Spanner* nsp = toSpanner(ne);
|
||||
Spanner* sp = toSpanner(element);
|
||||
staff_idx_t staffIdx1 = sp->track() / VOICES;
|
||||
staff_idx_t staffIdx2 = sp->track2() / VOICES;
|
||||
int diff = static_cast<int>(staffIdx2 - staffIdx1);
|
||||
nsp->setTrack2((staffIdx1 + diff) * VOICES + (sp->track2() % VOICES));
|
||||
int diff = sp->track2() - sp->track();
|
||||
nsp->setTrack2(nsp->track() + diff);
|
||||
nsp->computeStartElement();
|
||||
nsp->computeEndElement();
|
||||
undo(new AddElement(nsp));
|
||||
|
@ -6300,8 +6324,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 +6392,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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -553,7 +553,7 @@ EngravingObject* EngravingObject::findLinkedInScore(Score* score) const
|
|||
return nullptr;
|
||||
}
|
||||
auto findElem = std::find_if(_links->begin(), _links->end(),
|
||||
[score](EngravingObject* engObj) { return engObj && engObj->score() == score; });
|
||||
[this, score](EngravingObject* engObj) { return engObj && engObj != this && engObj->score() == score; });
|
||||
return findElem != _links->end() ? *findElem : nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -656,6 +656,8 @@ 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()) {
|
||||
ns->computeStartElement();
|
||||
}
|
||||
|
||||
if (!ns->startElement() || !ns->endElement()) {
|
||||
|
@ -1557,60 +1559,33 @@ void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& star
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<Excerpt*> Excerpt::createExcerptsFromParts(const std::vector<Part*>& parts)
|
||||
std::vector<Excerpt*> Excerpt::createExcerptsFromParts(const std::vector<Part*>& parts, MasterScore* score)
|
||||
{
|
||||
StringList allExcerptLowerNames;
|
||||
for (const Excerpt* e : score->excerpts()) {
|
||||
allExcerptLowerNames.push_back(e->name().toLower());
|
||||
}
|
||||
|
||||
std::vector<Excerpt*> result;
|
||||
|
||||
for (Part* part : parts) {
|
||||
Excerpt* excerpt = new Excerpt(part->masterScore());
|
||||
Excerpt* excerpt = new Excerpt(score);
|
||||
excerpt->parts().push_back(part);
|
||||
|
||||
for (track_idx_t i = part->startTrack(), j = 0; i < part->endTrack(); ++i, ++j) {
|
||||
track_idx_t startTrack = part->startTrack();
|
||||
track_idx_t endTrack = part->endTrack();
|
||||
|
||||
for (track_idx_t i = startTrack, j = 0; i < endTrack; ++i, ++j) {
|
||||
excerpt->m_tracksMapping.insert({ i, j });
|
||||
}
|
||||
|
||||
String name = formatName(part->partName(), result);
|
||||
String name = formatUniqueExcerptName(part->partName(), allExcerptLowerNames);
|
||||
excerpt->setName(name);
|
||||
excerpt->setInitialPartId(part->id());
|
||||
|
||||
allExcerptLowerNames.push_back(name.toLower());
|
||||
result.push_back(excerpt);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Excerpt* Excerpt::createExcerptFromPart(Part* part)
|
||||
{
|
||||
Excerpt* excerpt = createExcerptsFromParts({ part }).front();
|
||||
excerpt->setName(part->partName());
|
||||
|
||||
return excerpt;
|
||||
}
|
||||
|
||||
String Excerpt::formatName(const String& partName, const std::vector<Excerpt*>& allExcerpts)
|
||||
{
|
||||
String name = partName.simplified();
|
||||
int count = 0; // no of occurrences of partName
|
||||
|
||||
for (Excerpt* e : allExcerpts) {
|
||||
// if <partName> already exists, change <partName> to <partName 1>
|
||||
String excName = e->name();
|
||||
if (excName == name) {
|
||||
e->setName(excName + u" 1");
|
||||
}
|
||||
|
||||
std::string excNameU8 = excName.toStdString();
|
||||
std::regex regex("^(.+)\\s\\d+$");
|
||||
std::smatch match;
|
||||
std::regex_search(excNameU8, match, regex);
|
||||
if (!match.empty() && match[0].str() == name.toStdString()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
name += String(u" %1").arg(count + 1);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -78,8 +78,7 @@ public:
|
|||
|
||||
void setVoiceVisible(Staff* staff, voice_idx_t voiceIndex, bool visible);
|
||||
|
||||
static std::vector<Excerpt*> createExcerptsFromParts(const std::vector<Part*>& parts);
|
||||
static Excerpt* createExcerptFromPart(Part* part);
|
||||
static std::vector<Excerpt*> createExcerptsFromParts(const std::vector<Part*>& parts, MasterScore* score);
|
||||
|
||||
static void createExcerpt(Excerpt*);
|
||||
static void cloneStaves(Score* sourceScore, Score* dstScore, const std::vector<staff_idx_t>& sourceStavesIndexes,
|
||||
|
@ -89,8 +88,6 @@ public:
|
|||
static void cloneStaff2(Staff* ostaff, Staff* nstaff, const Fraction& startTick, const Fraction& endTick);
|
||||
static void cloneSpanner(Spanner* s, Score* score, track_idx_t dstTrack, track_idx_t dstTrack2);
|
||||
|
||||
static String formatName(const String& partName, const std::vector<Excerpt*>& allExcerpts);
|
||||
|
||||
private:
|
||||
friend class MasterScore;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -712,3 +712,5 @@ PlayTechAnnotation* Factory::createPlayTechAnnotation(Segment * parent, PlayingT
|
|||
|
||||
return annotation;
|
||||
}
|
||||
|
||||
CREATE_ITEM_IMPL(Capo, ElementType::CAPO, Segment, isAccessibleEnabled)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ void InstrumentChange::setupInstrument(const Instrument* instrument)
|
|||
Segment* seg = segment()->prev1(SegmentType::KeySig);
|
||||
voice_idx_t voice = part->staff(i)->idx() * VOICES;
|
||||
KeySig* ksig = toKeySig(seg->element(voice));
|
||||
bool forInstChange = ksig && ksig->tick() != tickStart;
|
||||
bool forInstChange = !(ksig && ksig->tick() == tickStart && !ksig->generated());
|
||||
ks.setForInstrumentChange(forInstChange);
|
||||
Key cKey = part->staff(i)->concertKey(tickStart);
|
||||
ks.setConcertKey(cKey);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -143,16 +143,6 @@ void MasterScore::setSaved(bool v)
|
|||
m_saved = v;
|
||||
}
|
||||
|
||||
bool MasterScore::autosaveDirty() const
|
||||
{
|
||||
return m_autosaveDirty;
|
||||
}
|
||||
|
||||
void MasterScore::setAutosaveDirty(bool v)
|
||||
{
|
||||
m_autosaveDirty = v;
|
||||
}
|
||||
|
||||
String MasterScore::name() const
|
||||
{
|
||||
return fileInfo()->displayName();
|
||||
|
|
|
@ -107,7 +107,6 @@ class MasterScore : public Score
|
|||
IFileInfoProviderPtr m_fileInfoProvider;
|
||||
|
||||
bool m_saved { false };
|
||||
bool m_autosaveDirty { true };
|
||||
|
||||
void reorderMidiMapping();
|
||||
void rebuildExcerptsMidiMapping();
|
||||
|
@ -222,9 +221,6 @@ public:
|
|||
bool saved() const;
|
||||
void setSaved(bool v);
|
||||
|
||||
bool autosaveDirty() const;
|
||||
void setAutosaveDirty(bool v);
|
||||
|
||||
String name() const override;
|
||||
|
||||
Ret sanityCheck();
|
||||
|
|
|
@ -2864,7 +2864,12 @@ MeasureRepeat* Measure::measureRepeatElement(staff_idx_t staffIdx) const
|
|||
}
|
||||
}
|
||||
}
|
||||
m = m->nextMeasure();
|
||||
Measure* nm = m->nextMeasure();
|
||||
if (nm && nm->measureRepeatCount(staffIdx) == m->measureRepeatCount(staffIdx) + 1) {
|
||||
m = nm;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,8 +243,16 @@ void Ornament::computeNotesAboveAndBelow(AccidentalState* accState)
|
|||
}
|
||||
|
||||
Note*& note = _notesAboveAndBelow.at(i);
|
||||
if (!note && above && _cueNoteChord) {
|
||||
note = _cueNoteChord->upNote();
|
||||
}
|
||||
|
||||
if (!note) {
|
||||
note = mainNote->clone();
|
||||
Tie* tie = note->tieFor();
|
||||
if (tie) {
|
||||
score()->undoRemoveElement((EngravingItem*)tie);
|
||||
}
|
||||
} else {
|
||||
note->setTpc1(mainNote->tpc1());
|
||||
note->setTpc2(mainNote->tpc2());
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -78,7 +78,9 @@ void RehearsalMark::applyTypeStyle()
|
|||
{
|
||||
const auto& elemStyleMap = (_type == Type::Main ? mainRehearsalMarkStyle : additionalRehearsalMarkStyle);
|
||||
for (const auto& elem : elemStyleMap) {
|
||||
setProperty(elem.pid, score()->styleV(elem.sid));
|
||||
if (propertyFlags(elem.pid) == PropertyFlags::STYLED) {
|
||||
setProperty(elem.pid, score()->styleV(elem.sid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -606,6 +606,7 @@ public:
|
|||
void resetUserStretch();
|
||||
void cmdResetBeamMode();
|
||||
void cmdResetTextStyleOverrides();
|
||||
bool canInsertClef(ClefType) const;
|
||||
void cmdInsertClef(ClefType);
|
||||
void removeChordRest(ChordRest* cr, bool clearSegment);
|
||||
ChordRest* nextMeasure(ChordRest* element, bool selectBehavior = false, bool mmRest = false);
|
||||
|
@ -1149,7 +1150,7 @@ public:
|
|||
void removeSpanner(Spanner*);
|
||||
void addSpanner(Spanner*);
|
||||
void cmdAddSpanner(Spanner* spanner, const mu::PointF& pos, bool systemStavesOnly = false);
|
||||
void cmdAddSpanner(Spanner* spanner, staff_idx_t staffIdx, Segment* startSegment, Segment* endSegment);
|
||||
void cmdAddSpanner(Spanner* spanner, staff_idx_t staffIdx, Segment* startSegment, Segment* endSegment, bool ctrlModifier = false);
|
||||
void checkSpanner(const Fraction& startTick, const Fraction& lastTick, bool removeOrphans = true);
|
||||
const std::set<Spanner*> unmanagedSpanners() { return _unmanagedSpanner; }
|
||||
void addUnmanagedSpanner(Spanner*);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
|
||||
void remove(const mu::RectF&);
|
||||
void remove(const Shape&);
|
||||
void removeInvisibles();
|
||||
|
||||
void addHorizontalSpacing(EngravingItem* item, double left, double right);
|
||||
|
||||
|
|
|
@ -651,6 +651,9 @@ void Spanner::computeStartElement()
|
|||
m_startElement = startSegment();
|
||||
} else {
|
||||
Segment* seg = score()->tick2segmentMM(tick(), false, SegmentType::ChordRest);
|
||||
if (!seg || seg->empty()) {
|
||||
seg = score()->tick2segment(tick(), false, SegmentType::ChordRest);
|
||||
}
|
||||
track_idx_t strack = (track() / VOICES) * VOICES;
|
||||
track_idx_t etrack = strack + VOICES;
|
||||
m_startElement = 0;
|
||||
|
@ -675,8 +678,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 +767,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,26 +111,30 @@ void SpannerMap::collectIntervals(IntervalList& regularIntervals, IntervalList&
|
|||
constexpr int collidingSpannersPadding = 1;
|
||||
|
||||
for (const auto& pair : *this) {
|
||||
int newSpannerStartTick = pair.second->tick().ticks();
|
||||
int newSpannerEndTick = pair.second->tick2().ticks();
|
||||
Spanner* spanner = pair.second;
|
||||
|
||||
IntervalsByType& intervalsByType = intervalsByPart[pair.second->part()->id()];
|
||||
IntervalList& intervalList = intervalsByType[pair.second->type()];
|
||||
int newSpannerStartTick = spanner->tick().ticks();
|
||||
int newSpannerEndTick = spanner->tick2().ticks();
|
||||
|
||||
IntervalsByType& intervalsByType = intervalsByPart[spanner->part()->id()];
|
||||
IntervalList& intervalList = intervalsByType[spanner->type()];
|
||||
|
||||
if (!intervalList.empty()) {
|
||||
auto lastIntervalIt = intervalList.rbegin();
|
||||
if (lastIntervalIt->stop >= newSpannerStartTick) {
|
||||
lastIntervalIt->stop = newSpannerStartTick - collidingSpannersPadding;
|
||||
if (!lastIntervalIt->value->isLinked(spanner)) {
|
||||
lastIntervalIt->stop = newSpannerStartTick - collidingSpannersPadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intervalList.emplace_back(interval_tree::Interval<Spanner*>(newSpannerStartTick,
|
||||
newSpannerEndTick,
|
||||
pair.second));
|
||||
spanner));
|
||||
|
||||
regularIntervals.push_back(interval_tree::Interval(newSpannerStartTick,
|
||||
newSpannerEndTick,
|
||||
pair.second));
|
||||
spanner));
|
||||
}
|
||||
|
||||
for (const auto& pair : intervalsByPart) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue