Compare commits
245 Commits
Author | SHA1 | Date |
---|---|---|
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.0
|
||||
|
||||
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.0
|
||||
|
||||
schedule:
|
||||
- cron: '0 3 */1 */1 *' # At 03:00 on every day-of-month for master
|
||||
|
@ -24,7 +25,7 @@ on:
|
|||
default: ''
|
||||
|
||||
env:
|
||||
CURRENT_RELEASE_BRANCH: 4.0.2
|
||||
CURRENT_RELEASE_BRANCH: 4.1.0
|
||||
|
||||
jobs:
|
||||
build_mu4:
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.0
|
||||
|
||||
schedule:
|
||||
- cron: '0 3 */1 */1 *' # At 03:00 on every day-of-month for master
|
||||
|
@ -24,7 +25,7 @@ on:
|
|||
default: ''
|
||||
|
||||
env:
|
||||
CURRENT_RELEASE_BRANCH: 4.0.2
|
||||
CURRENT_RELEASE_BRANCH: 4.1.0
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer
|
||||
|
||||
jobs:
|
||||
|
|
|
@ -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.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
|
|
@ -4,7 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.0.2
|
||||
- 4.1.0
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.0
|
||||
|
||||
schedule:
|
||||
- cron: '0 3 */1 */1 *' # At 03:00 on every day-of-month for master
|
||||
|
@ -24,7 +25,7 @@ on:
|
|||
default: ''
|
||||
|
||||
env:
|
||||
CURRENT_RELEASE_BRANCH: 4.0.2
|
||||
CURRENT_RELEASE_BRANCH: 4.1.0
|
||||
|
||||
jobs:
|
||||
build_mu4_x64:
|
||||
|
@ -112,8 +113,10 @@ jobs:
|
|||
fi
|
||||
|
||||
if [ $DO_BUILD == 'false' ]; then
|
||||
DO_UPLOAD_SYMBOLS='false'
|
||||
DO_PUBLISH='false'
|
||||
DO_UPDATE_TS='false'
|
||||
DO_PLACEHOLDER_TRANSLATIONS='false'
|
||||
DO_UPLOAD_SYMBOLS='false'
|
||||
fi
|
||||
|
||||
ADD_INFO="_${GITHUB_REF#refs/heads/}"
|
||||
|
@ -249,11 +252,37 @@ jobs:
|
|||
if [[ "$BUILD_MODE" == "testing_build" || "$BUILD_MODE" == "stable_build" ]]; then
|
||||
DO_BUILD='true'
|
||||
fi
|
||||
if [[ "$BUILD_MODE" == "nightly_build" && "${{ github.repository }}" == "musescore/MuseScore" ]]; then
|
||||
DO_BUILD='true'
|
||||
fi
|
||||
|
||||
DO_PUBLISH='false'
|
||||
if [[ "${{ github.event.inputs.publish }}" == "on" || "$BUILD_MODE" == "nightly_build" ]]; then
|
||||
DO_PUBLISH='true'
|
||||
if [ -z "${{ secrets.OSUOSL_SSH_ENCRYPT_SECRET }}" ]; then
|
||||
echo "warning: not set OSUOSL_SSH_ENCRYPT_SECRET, publish disabled"
|
||||
DO_PUBLISH='false'
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${{ github.event_name }}" == "pull_request" ]; then PR_INFO="_${{ github.event.pull_request.number }}_${pull_request_title}"; fi
|
||||
UPLOAD_ARTIFACT_NAME="$(tr '":<>|*?/\\' '_' <<<"MU4_${BUILD_NUMBER}_Win${PR_INFO}_portable")"
|
||||
DO_UPDATE_TS='false'
|
||||
if [[ "$BUILD_MODE" == "testing_build" || "$BUILD_MODE" == "stable_build" ]]; then
|
||||
DO_UPDATE_TS='true'
|
||||
if [ -z "${{ secrets.TRANSIFEX_API_TOKEN }}" ]; then
|
||||
echo "warning: not set TRANSIFEX_API_TOKEN, update .ts disabled"
|
||||
DO_UPDATE_TS='false'
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $DO_BUILD == 'false' ]; then
|
||||
DO_PUBLISH='false'
|
||||
DO_UPDATE_TS='false'
|
||||
fi
|
||||
|
||||
ADD_INFO="_${GITHUB_REF#refs/heads/}"
|
||||
if [ "${{ github.event_name }}" == "schedule" ] && [ "${{ github.event.schedule }}" == "0 5 */1 */1 *" ]; then ADD_INFO="_${CURRENT_RELEASE_BRANCH}"; fi
|
||||
if [ "${{ github.event_name }}" == "pull_request" ]; then ADD_INFO="_${{ github.event.pull_request.number }}_${pull_request_title}"; fi
|
||||
UPLOAD_ARTIFACT_NAME="$(tr '":<>|*?/\\' '_' <<<"MU4_${BUILD_NUMBER}_Win_portable${ADD_INFO}")"
|
||||
|
||||
echo "github.repository: ${{ github.repository }}"
|
||||
echo "BUILD_MODE=$BUILD_MODE" >> $GITHUB_ENV
|
||||
|
@ -262,8 +291,10 @@ jobs:
|
|||
echo "BUILD_NUMBER: $BUILD_NUMBER"
|
||||
echo "DO_BUILD=$DO_BUILD" >> $GITHUB_ENV
|
||||
echo "DO_BUILD: $DO_BUILD"
|
||||
echo "DO_UPDATE_TS=$DO_UPDATE_TS" >> $GITHUB_ENV
|
||||
echo "DO_UPDATE_TS: $DO_UPDATE_TS"
|
||||
echo "DO_PUBLISH=$DO_PUBLISH" >> $GITHUB_ENV
|
||||
echo "DO_PUBLISH: $DO_PUBLISH"
|
||||
echo "DO_PUBLISH: $DO_PUBLISH"
|
||||
echo "UPLOAD_ARTIFACT_NAME=$UPLOAD_ARTIFACT_NAME" >> $GITHUB_ENV
|
||||
echo "UPLOAD_ARTIFACT_NAME: $UPLOAD_ARTIFACT_NAME"
|
||||
|
||||
|
@ -276,6 +307,17 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
bash ./build/ci/windows/make_environment.sh
|
||||
- name: Update .ts files
|
||||
if: env.DO_UPDATE_TS == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
bash ./build/ci/translation/tx_install.sh -t ${{ secrets.TRANSIFEX_API_TOKEN }} -s windows
|
||||
bash ./build/ci/translation/tx_pull.sh
|
||||
- name: Generate .qm files
|
||||
if: env.DO_BUILD == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
bash ./build/ci/translation/run_lrelease.sh
|
||||
- name: Build
|
||||
if: env.DO_BUILD == 'true'
|
||||
shell: cmd
|
||||
|
@ -287,16 +329,18 @@ jobs:
|
|||
if: env.DO_BUILD == 'true'
|
||||
shell: cmd
|
||||
run: |
|
||||
build\ci\windows\package.bat --portable ON
|
||||
IF ${{ secrets.WIN_SIGN_CERTIFICATE_ENCRYPT_SECRET != 0 }} == true ( SET S_S=${{ secrets.WIN_SIGN_CERTIFICATE_ENCRYPT_SECRET }} ) ELSE ( SET S_S="''" )
|
||||
IF ${{ secrets.WIN_SIGN_CERTIFICATE_PASSWORD != 0 }} == true ( SET S_P=${{ secrets.WIN_SIGN_CERTIFICATE_PASSWORD }} ) ELSE ( SET S_P="''" )
|
||||
build\ci\windows\package.bat --portable ON --signsecret %S_S% --signpass %S_P%
|
||||
- name: Checksum
|
||||
if: env.DO_BUILD == 'true'
|
||||
run: |
|
||||
bash ./build/ci/tools/checksum.sh
|
||||
- name: Publish package
|
||||
if: env.DO_PUBLISH == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
build\ci\windows\publish.bat --secret ${{ secrets.OSUOSL_SSH_ENCRYPT_SECRET }}
|
||||
shell: cmd
|
||||
bash ./build/ci/tools/osuosl/publish.sh -s ${{ secrets.OSUOSL_SSH_ENCRYPT_SECRET }} --os windows -v 4 --arch x86_64-portable
|
||||
- name: Upload artifacts on GitHub
|
||||
if: env.DO_BUILD == 'true'
|
||||
uses: actions/upload-artifact@v3
|
||||
|
|
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 4.1.0
|
||||
|
||||
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: ""
|
||||
|
|
|
@ -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")
|
||||
};
|
||||
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
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.");
|
||||
|
|
|
@ -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();
|
||||
|
@ -2136,6 +2161,102 @@ static std::pair<double, double> layoutAccidental(const MStyle& style, AcEl* me,
|
|||
return std::pair<double, double>(me->x, me->x + me->width);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// placeDots
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ChordLayout::placeDots(const std::vector<Chord*>& chords, const std::vector<Note*>& notes)
|
||||
{
|
||||
Chord* chord = nullptr;
|
||||
for (Chord* c : chords) {
|
||||
if (c->dots() > 0) {
|
||||
chord = c;
|
||||
break;
|
||||
} else {
|
||||
for (Note* note : c->notes()) {
|
||||
note->setDotRelativeLine(0); // this manages the deletion of dots
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!chord || chord->staff()->isTabStaff(chord->tick())) {
|
||||
return;
|
||||
}
|
||||
std::vector<Note*> topDownNotes;
|
||||
std::vector<Note*> bottomUpNotes;
|
||||
std::vector<int> anchoredDots;
|
||||
// construct combined chords using the notes from overlapping chords
|
||||
getNoteListForDots(chord, topDownNotes, bottomUpNotes, anchoredDots);
|
||||
|
||||
for (Note* note : notes) {
|
||||
bool onLine = !(note->line() & 1);
|
||||
if (onLine) {
|
||||
std::unordered_map<int, Note*> alreadyAdded;
|
||||
bool finished = false;
|
||||
for (Note* otherNote : bottomUpNotes) {
|
||||
int dotMove = otherNote->dotPosition() == DirectionV::UP ? -1 : 1;
|
||||
int otherDotLoc = otherNote->line() + dotMove;
|
||||
bool added = alreadyAdded.count(otherDotLoc);
|
||||
if (!added && mu::contains(anchoredDots, otherDotLoc)) {
|
||||
dotMove = -dotMove; // if the desired space is taken, adjust opposite
|
||||
} else if (added && alreadyAdded[otherDotLoc] != otherNote) {
|
||||
dotMove = -dotMove;
|
||||
}
|
||||
// set y for this note
|
||||
if (note == otherNote) {
|
||||
note->setDotRelativeLine(dotMove);
|
||||
finished = true;
|
||||
anchoredDots.push_back(note->line() + dotMove);
|
||||
alreadyAdded[otherNote->line() + dotMove] = otherNote;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!finished) {
|
||||
alreadyAdded.clear();
|
||||
for (Note* otherNote : topDownNotes) {
|
||||
int dotMove = otherNote->dotPosition() == DirectionV::DOWN ? 1 : -1;
|
||||
int otherDotLoc = otherNote->line() + dotMove;
|
||||
bool added = alreadyAdded.count(otherDotLoc);
|
||||
if (!added && mu::contains(anchoredDots, otherDotLoc)) {
|
||||
dotMove = -dotMove;
|
||||
} else if (added && alreadyAdded[otherDotLoc] != otherNote) {
|
||||
dotMove = -dotMove;
|
||||
}
|
||||
// set y for this note
|
||||
if (note == otherNote) {
|
||||
note->setDotRelativeLine(dotMove);
|
||||
finished = true;
|
||||
anchoredDots.push_back(note->line() + dotMove);
|
||||
break;
|
||||
}
|
||||
if (!added) {
|
||||
alreadyAdded[otherNote->line() + dotMove] = otherNote;
|
||||
}
|
||||
}
|
||||
}
|
||||
IF_ASSERT_FAILED(finished)
|
||||
{
|
||||
// this should never happen
|
||||
// the note is on a line and topDownNotes and bottomUpNotes are all of the lined notes
|
||||
note->setDotRelativeLine(0);
|
||||
}
|
||||
} else {
|
||||
// on a space; usually this means the dot is on this same line, but there is an exception
|
||||
// for a unison within the same chord.
|
||||
for (Note* otherNote : note->chord()->notes()) {
|
||||
if (note == otherNote) {
|
||||
note->setDotRelativeLine(0); // same space as notehead
|
||||
break;
|
||||
}
|
||||
if (note->line() == otherNote->line()) {
|
||||
bool adjustDown = (note->chord()->voice() & 1) && !note->chord()->up();
|
||||
note->setDotRelativeLine(adjustDown ? 2 : -2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// layoutChords3
|
||||
// - calculate positions of notes, accidentals, dots
|
||||
|
@ -2275,6 +2396,11 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
|
|||
|
||||
double xx = x + note->headBodyWidth() + chord->pos().x();
|
||||
|
||||
//---------------------------------------------------
|
||||
// layout dots simply
|
||||
// we will check for conflicts after all the notes have been processed
|
||||
//---------------------------------------------------
|
||||
|
||||
DirectionV dotPosition = note->userDotPosition();
|
||||
if (chord->dots()) {
|
||||
if (chord->up()) {
|
||||
|
@ -2287,12 +2413,12 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
|
|||
// resolve dot conflicts
|
||||
int line = note->line();
|
||||
Note* above = (i < nNotes - 1) ? notes[i + 1] : 0;
|
||||
if (above && (!above->visible() || above->dotsHidden())) {
|
||||
if (above && (!above->visible() || above->dotsHidden() || above->chord()->dots() == 0)) {
|
||||
above = 0;
|
||||
}
|
||||
int intervalAbove = above ? line - above->line() : 1000;
|
||||
Note* below = (i > 0) ? notes[i - 1] : 0;
|
||||
if (below && (!below->visible() || below->dotsHidden())) {
|
||||
if (below && (!below->visible() || below->dotsHidden() || below->chord()->dots() == 0)) {
|
||||
below = 0;
|
||||
}
|
||||
int intervalBelow = below ? below->line() - line : 1000;
|
||||
|
@ -2302,22 +2428,19 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
|
|||
dotPosition = DirectionV::DOWN;
|
||||
} else if (intervalBelow == 1 && intervalAbove != 1) {
|
||||
dotPosition = DirectionV::UP;
|
||||
} else if (intervalAbove == 0 && above->chord()->dots()) {
|
||||
} else if (intervalAbove == 0 || intervalBelow == 0) {
|
||||
// unison
|
||||
if (((above->voice() & 1) == (note->voice() & 1))) {
|
||||
above->setDotY(DirectionV::UP);
|
||||
dotPosition = DirectionV::DOWN;
|
||||
}
|
||||
dotPosition = DirectionV::AUTO; // unison conflicts taken care of later
|
||||
}
|
||||
} else {
|
||||
// space
|
||||
if (intervalAbove == 0 && above->chord()->dots()) {
|
||||
// unison
|
||||
if (!(note->voice() & 1)) {
|
||||
dotPosition = DirectionV::UP;
|
||||
dotPosition = DirectionV::UP; // space, doesn't matter
|
||||
} else {
|
||||
if (!(above->voice() & 1)) {
|
||||
above->setDotY(DirectionV::UP);
|
||||
above->setDotPosition(DirectionV::UP);
|
||||
} else {
|
||||
dotPosition = DirectionV::DOWN;
|
||||
}
|
||||
|
@ -2326,8 +2449,13 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
|
|||
}
|
||||
}
|
||||
}
|
||||
note->setDotY(dotPosition); // also removes invalid dots
|
||||
if (dotPosition == DirectionV::AUTO) {
|
||||
dotPosition = note->voice() & 1 ? DirectionV::DOWN : DirectionV::UP;
|
||||
}
|
||||
note->setDotPosition(dotPosition);
|
||||
}
|
||||
// Now, we can resolve note conflicts as a superchord
|
||||
placeDots(chords, notes);
|
||||
|
||||
// if there are no non-mirrored notes in a downstem chord,
|
||||
// then use the stem X position as X origin for accidental layout
|
||||
|
@ -2563,6 +2691,70 @@ void ChordLayout::layoutChords3(const MStyle& style, const std::vector<Chord*>&
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// getNoteListForDots
|
||||
// This method populates three lists: one for chord notes that need to be checked from the top down,
|
||||
// one for chords from the bottom up, and one for spaces (where the dot will be in that space)
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ChordLayout::getNoteListForDots(Chord* c, std::vector<Note*>& topDownNotes, std::vector<Note*>& bottomUpNotes,
|
||||
std::vector<int>& anchoredDots)
|
||||
{
|
||||
bool hasVoices = c->measure()->hasVoices(c->staffIdx(), c->tick(), c->ticks());
|
||||
if (!hasVoices) {
|
||||
// only this voice, so topDownNotes is just the notes in the chord
|
||||
for (Note* note : c->notes()) {
|
||||
if (note->line() & 1) {
|
||||
int newOffset = 0;
|
||||
bool adjustDown = (c->voice() & 1) && !c->up();
|
||||
if (!anchoredDots.empty() && anchoredDots.back() == note->line()) {
|
||||
if (anchoredDots.size() >= 2 && anchoredDots[anchoredDots.size() - 2] == note->line() + (adjustDown ? 2 : -2)) {
|
||||
newOffset = adjustDown ? -2 : 2;
|
||||
} else {
|
||||
newOffset = adjustDown ? 2 : -2;
|
||||
}
|
||||
}
|
||||
anchoredDots.push_back(note->line() + newOffset);
|
||||
} else {
|
||||
topDownNotes.push_back(note);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Get a list of notes in this staff that adjust dots from top down,
|
||||
// bottom up, and also start our locked-in dot list by adding all lines where dots are
|
||||
// guaranteed
|
||||
Measure* m = c->measure();
|
||||
size_t firstVoice = c->track() - c->voice();
|
||||
for (size_t i = firstVoice; i < firstVoice + VOICES; ++i) {
|
||||
if (Chord* voiceChord = m->findChord(c->tick(), i)) {
|
||||
bool startFromTop = !((voiceChord->voice() & 1) && !voiceChord->up());
|
||||
if (startFromTop) {
|
||||
for (Note* note : voiceChord->notes()) {
|
||||
if (note->line() & 1) {
|
||||
anchoredDots.push_back(note->line());
|
||||
} else {
|
||||
topDownNotes.push_back(note);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Note* note : voiceChord->notes()) {
|
||||
if (note->line() & 1) {
|
||||
anchoredDots.push_back(note->line());
|
||||
} else {
|
||||
bottomUpNotes.push_back(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// our two lists now contain only notes that are on lines
|
||||
std::sort(topDownNotes.begin(), topDownNotes.end(),
|
||||
[](Note* n1, Note* n2) { return n1->line() < n2->line(); });
|
||||
std::sort(bottomUpNotes.begin(), bottomUpNotes.end(),
|
||||
[](Note* n1, Note* n2) { return n1->line() > n2->line(); });
|
||||
}
|
||||
|
||||
/* updateGraceNotes()
|
||||
* Processes a full measure, making sure that all grace notes are
|
||||
* attacched to the correct segment. Has to be performed after
|
||||
|
@ -2779,10 +2971,15 @@ void ChordLayout::resolveRestVSChord(std::vector<Rest*>& rests, std::vector<Chor
|
|||
bool ignoreYOffset = (restAbove && restYOffset > 0) || (!restAbove && restYOffset < 0);
|
||||
PointF offset = ignoreYOffset ? PointF(0, restYOffset) : PointF(0, 0);
|
||||
|
||||
Shape chordShape = chord->shape().translated(chord->pos());
|
||||
chordShape.removeInvisibles();
|
||||
if (chordShape.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double clearance = 0.0;
|
||||
Shape restShape = rest->shape().translated(rest->pos() - offset);
|
||||
if (chord->segment() == rest->segment()) {
|
||||
Shape chordShape = chord->shape().translated(chord->pos());
|
||||
clearance = restAbove ? restShape.verticalClearance(chordShape) : chordShape.verticalClearance(restShape);
|
||||
} else {
|
||||
Note* limitNote = restAbove ? chord->upNote() : chord->downNote();
|
||||
|
@ -3078,8 +3275,8 @@ void ChordLayout::layoutNote2(Note* item, LayoutContext& ctx)
|
|||
if (isTabStaff && staffType->stemThrough()) {
|
||||
// with TAB's, dot Y is not calculated during layoutChords3(),
|
||||
// as layoutChords3() is not even called for TAB's;
|
||||
// setDotY() actually also manages creation/deletion of NoteDot's
|
||||
item->setDotY(DirectionV::AUTO);
|
||||
// setDotRelativeLine() actually also manages creation/deletion of NoteDot's
|
||||
item->setDotRelativeLine(0);
|
||||
|
||||
// use TAB default note-to-dot spacing
|
||||
dd = STAFFTYPE_TAB_DEFAULTDOTDIST_X * item->spatium();
|
||||
|
@ -3162,11 +3359,7 @@ void ChordLayout::checkStartEndSlurs(Chord* chord, LayoutContext& ctx)
|
|||
if (!slur->endChord()) {
|
||||
continue;
|
||||
}
|
||||
std::vector<Spanner*>& endingSp = slur->endChord()->endingSpanners();
|
||||
if (std::find(endingSp.begin(), endingSp.end(), slur) == endingSp.end()) {
|
||||
// Slur not added. Add it now.
|
||||
endingSp.push_back(slur);
|
||||
}
|
||||
slur->endChord()->addEndingSpanner(slur);
|
||||
}
|
||||
for (Spanner* spanner : chord->_endingSpanners) {
|
||||
if (!spanner->isSlur()) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1533,7 +1533,12 @@ void TLayout::layout(Expression* item, LayoutContext& ctx)
|
|||
|
||||
item->setSnappedDynamic(nullptr);
|
||||
|
||||
if (!item->autoplace() || !item->snapToDynamics()) {
|
||||
if (!item->autoplace()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item->snapToDynamics()) {
|
||||
item->autoplaceSegmentElement();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2395,7 +2400,9 @@ void TLayout::layout(HairpinSegment* item, LayoutContext& ctx)
|
|||
Dynamic* sd = nullptr;
|
||||
Dynamic* ed = nullptr;
|
||||
double dymax = item->hairpin()->placeBelow() ? -10000.0 : 10000.0;
|
||||
if (item->autoplace() && !ctx.isPaletteMode()) {
|
||||
if (item->autoplace() && !ctx.isPaletteMode()
|
||||
&& item->explicitParent() // TODO: remove this line (this might happen when Ctrl+Shift+Dragging an item)
|
||||
) {
|
||||
Segment* start = item->hairpin()->startSegment();
|
||||
Segment* end = item->hairpin()->endSegment();
|
||||
// Try to fit between adjacent dynamics
|
||||
|
@ -2552,6 +2559,15 @@ void TLayout::layout(HairpinSegment* item, LayoutContext& ctx)
|
|||
item->pointsRef()[3] = l2.p2();
|
||||
item->npointsRef() = 4;
|
||||
|
||||
item->polygonRef().clear();
|
||||
if (item->spannerSegmentType() != SpannerSegmentType::MIDDLE) {
|
||||
if (type == HairpinType::DECRESC_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::BEGIN) {
|
||||
item->polygonRef() << item->pointsRef()[0] << item->pointsRef()[1] << item->pointsRef()[2]; // [top-left, joint, bottom-left]
|
||||
} else if (type == HairpinType::CRESC_HAIRPIN && item->spannerSegmentType() != SpannerSegmentType::END) {
|
||||
item->polygonRef() << item->pointsRef()[1] << item->pointsRef()[0] << item->pointsRef()[3]; // [top-right, joint, bottom-right]
|
||||
}
|
||||
}
|
||||
|
||||
RectF r = RectF(l1.p1(), l1.p2()).normalized().united(RectF(l2.p1(), l2.p2()).normalized());
|
||||
if (!item->text()->empty()) {
|
||||
r.unite(item->text()->bbox());
|
||||
|
@ -2658,8 +2674,10 @@ void TLayout::layout(HairpinSegment* item, LayoutContext& ctx)
|
|||
}
|
||||
if (ed->ipos().y() != ny) {
|
||||
ed->setPosY(ny);
|
||||
if (ed->snappedExpression()) {
|
||||
ed->snappedExpression()->setPosY(ny);
|
||||
Expression* snappedExpression = ed->snappedExpression();
|
||||
if (snappedExpression) {
|
||||
double yOffsetDiff = snappedExpression->offset().y() - ed->offset().y();
|
||||
snappedExpression->setPosY(ny - yOffsetDiff);
|
||||
}
|
||||
if (ed->addToSkyline()) {
|
||||
Segment* s = ed->segment();
|
||||
|
@ -2718,7 +2736,6 @@ void TLayout::layout(Harmony* item, LayoutContext& ctx)
|
|||
// setOffset(propertyDefault(Pid::OFFSET).value<PointF>());
|
||||
|
||||
layout1(item, ctx);
|
||||
item->setPos(calculateBoundingRect(item, ctx));
|
||||
}
|
||||
|
||||
void TLayout::layout1(Harmony* item, LayoutContext& ctx)
|
||||
|
@ -2731,13 +2748,14 @@ void TLayout::layout1(Harmony* item, LayoutContext& ctx)
|
|||
item->textBlockList().push_back(TextBlock());
|
||||
}
|
||||
|
||||
calculateBoundingRect(item, ctx);
|
||||
auto positionPoint = calculateBoundingRect(item, ctx);
|
||||
|
||||
if (item->hasFrame()) {
|
||||
item->layoutFrame();
|
||||
}
|
||||
|
||||
ctx.addRefresh(item->canvasBoundingRect());
|
||||
item->setPos(positionPoint);
|
||||
}
|
||||
|
||||
PointF TLayout::calculateBoundingRect(Harmony* item, LayoutContext& ctx)
|
||||
|
@ -3593,6 +3611,10 @@ void TLayout::layout(Ornament* item, LayoutContext& ctx)
|
|||
Chord* parentChord = toChord(item->parentItem());
|
||||
Chord* cueNoteChord = item->cueNoteChord();
|
||||
|
||||
if (!cueNoteChord) {
|
||||
return;
|
||||
}
|
||||
|
||||
Note* cueNote = cueNoteChord->notes().front();
|
||||
ChordLayout::layoutChords3(ctx.style(), { cueNoteChord }, { cueNote }, item->staff(), ctx);
|
||||
layout(cueNoteChord, ctx);
|
||||
|
@ -4194,13 +4216,14 @@ void TLayout::layout(StemSlash* item, LayoutContext& ctx)
|
|||
static constexpr double heightReduction = 0.66;
|
||||
static constexpr double angleIncrease = 1.2;
|
||||
static constexpr double lengthIncrease = 1.1;
|
||||
const double mag = c->mag();
|
||||
|
||||
double up = c->up() ? -1 : 1;
|
||||
double stemTipY = c->up() ? stem->bbox().translated(stem->pos()).top() : stem->bbox().translated(stem->pos()).bottom();
|
||||
double leftHang = ctx.noteHeadWidth() * ctx.style().styleD(Sid::graceNoteMag) / 2;
|
||||
double leftHang = ctx.noteHeadWidth() * mag / 2;
|
||||
double angle = ctx.style().styleD(Sid::stemSlashAngle) * M_PI / 180; // converting to radians
|
||||
bool straight = ctx.style().styleB(Sid::useStraightNoteFlags);
|
||||
double graceNoteMag = ctx.style().styleD(Sid::graceNoteMag);
|
||||
double graceNoteMag = mag;
|
||||
|
||||
double startX = stem->bbox().translated(stem->pos()).right() - leftHang;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "chordline.h"
|
||||
#include "clef.h"
|
||||
#include "excerpt.h"
|
||||
#include "expression.h"
|
||||
#include "factory.h"
|
||||
#include "fingering.h"
|
||||
#include "glissando.h"
|
||||
|
@ -736,9 +737,7 @@ TextBase* Score::addText(TextStyleType type, EngravingItem* destinationElement,
|
|||
if (!chordRest) {
|
||||
break;
|
||||
}
|
||||
textBox = Factory::createStaffText(dummy()->segment(), TextStyleType::EXPRESSION);
|
||||
textBox->setPlacement(PlacementV::BELOW);
|
||||
textBox->setPropertyFlags(Pid::PLACEMENT, PropertyFlags::UNSTYLED);
|
||||
textBox = Factory::createExpression(dummy()->segment());
|
||||
chordRest->undoAddAnnotation(textBox);
|
||||
break;
|
||||
}
|
||||
|
@ -3374,14 +3373,15 @@ ChordRest* Score::deleteRange(Segment* s1, Segment* s2, track_idx_t track1, trac
|
|||
|
||||
void Score::cmdDeleteSelection()
|
||||
{
|
||||
ChordRest* cr = 0; // select something after deleting notes
|
||||
ChordRest* crSelectedAfterDeletion = 0; // select something after deleting notes
|
||||
|
||||
if (selection().isRange()) {
|
||||
Segment* s1 = selection().startSegment();
|
||||
Segment* s2 = selection().endSegment();
|
||||
const Fraction stick1 = selection().tickStart();
|
||||
const Fraction stick2 = selection().tickEnd();
|
||||
cr = deleteRange(s1, s2, staff2track(selection().staffStart()), staff2track(selection().staffEnd()), selectionFilter());
|
||||
crSelectedAfterDeletion = deleteRange(s1, s2, staff2track(selection().staffStart()),
|
||||
staff2track(selection().staffEnd()), selectionFilter());
|
||||
s1 = tick2segment(stick1);
|
||||
s2 = tick2segment(stick2, true);
|
||||
if (s1 == 0 || s2 == 0) {
|
||||
|
@ -3403,7 +3403,6 @@ void Score::cmdDeleteSelection()
|
|||
// so we don't try to delete them twice if they are also in selection
|
||||
std::set<Spanner*> deletedSpanners;
|
||||
|
||||
bool allertInstrChange = true;
|
||||
for (EngravingItem* e : el) {
|
||||
// these are the linked elements we are about to delete
|
||||
std::list<EngravingObject*> links;
|
||||
|
@ -3416,7 +3415,7 @@ void Score::cmdDeleteSelection()
|
|||
// or of spanner or parent if that is more valid
|
||||
Fraction tick = { -1, 1 };
|
||||
track_idx_t track = mu::nidx;
|
||||
if (!cr) {
|
||||
if (!crSelectedAfterDeletion) {
|
||||
if (e->isNote()) {
|
||||
tick = toNote(e)->chord()->tick();
|
||||
} else if (e->isRest() || e->isMMRest()) {
|
||||
|
@ -3449,30 +3448,31 @@ void Score::cmdDeleteSelection()
|
|||
//else tick < 0
|
||||
track = e->track();
|
||||
}
|
||||
// find element to select
|
||||
if (!cr && tick >= Fraction(0, 1) && track != mu::nidx) {
|
||||
cr = findCR(tick, track);
|
||||
}
|
||||
|
||||
bool needFindCR = !crSelectedAfterDeletion && tick >= Fraction(0, 1) && track != mu::nidx;
|
||||
|
||||
// We should not allow deleting the very first keySig of the piece, because it is
|
||||
// logically incorrect and leads to a state of undefined key/transposition.
|
||||
// Also instrument change key signatures should be undeletable.
|
||||
// The correct action is for the user to set an atonal/custom keySig as needed.
|
||||
if (e->isKeySig()) {
|
||||
if (e->tick() == Fraction(0, 1)) {
|
||||
continue;
|
||||
} else if (toKeySig(e)->forInstrumentChange()) {
|
||||
if (allertInstrChange) {
|
||||
MessageBox::warning(mtrc("engraving", "Instrument change key signature cannot be deleted").toStdString(),
|
||||
mtrc("engraving",
|
||||
"Please replace it with a key signature from the palettes instead.").toStdString(),
|
||||
{ MessageBox::Ok });
|
||||
allertInstrChange = false;
|
||||
if (e->tick() == Fraction(0, 1) || toKeySig(e)->forInstrumentChange()) {
|
||||
MScore::setError(MsError::CANNOT_REMOVE_KEY_SIG);
|
||||
if (needFindCR) {
|
||||
crSelectedAfterDeletion = findCR(tick, track);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't allow deleting the trill cue note
|
||||
if (e->isNote() && toNote(e)->isTrillCueNote()) {
|
||||
if (needFindCR) {
|
||||
crSelectedAfterDeletion = findCR(tick, track);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// delete element if we have not done so already
|
||||
if (deletedElements.find(e) == deletedElements.end()) {
|
||||
// do not delete two spanner segments from the same spanner
|
||||
|
@ -3494,6 +3494,11 @@ void Score::cmdDeleteSelection()
|
|||
}
|
||||
deleteItem(e);
|
||||
}
|
||||
|
||||
if (needFindCR) {
|
||||
crSelectedAfterDeletion = findCR(tick, track);
|
||||
}
|
||||
|
||||
// add these linked elements to list of already-deleted elements
|
||||
for (EngravingObject* se : links) {
|
||||
deletedElements.insert(se);
|
||||
|
@ -3504,17 +3509,17 @@ void Score::cmdDeleteSelection()
|
|||
deselectAll();
|
||||
// make new selection if appropriate
|
||||
if (noteEntryMode()) {
|
||||
if (cr) {
|
||||
_is.setSegment(cr->segment());
|
||||
if (crSelectedAfterDeletion) {
|
||||
_is.setSegment(crSelectedAfterDeletion->segment());
|
||||
} else {
|
||||
cr = _is.cr();
|
||||
crSelectedAfterDeletion = _is.cr();
|
||||
}
|
||||
}
|
||||
if (cr) {
|
||||
if (cr->isChord()) {
|
||||
select(toChord(cr)->upNote(), SelectType::SINGLE);
|
||||
if (crSelectedAfterDeletion) {
|
||||
if (crSelectedAfterDeletion->isChord()) {
|
||||
select(toChord(crSelectedAfterDeletion)->upNote(), SelectType::SINGLE);
|
||||
} else {
|
||||
select(cr, SelectType::SINGLE);
|
||||
select(crSelectedAfterDeletion, SelectType::SINGLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4975,6 +4980,7 @@ void Score::undoChangeFretting(Note* note, int pitch, int string, int fret, int
|
|||
void Score::undoChangeKeySig(Staff* ostaff, const Fraction& tick, KeySigEvent key)
|
||||
{
|
||||
KeySig* lks = 0;
|
||||
bool needsUpdate = false;
|
||||
|
||||
for (Staff* staff : ostaff->staffList()) {
|
||||
if (staff->isDrumStaff(tick)) {
|
||||
|
@ -4995,12 +5001,14 @@ void Score::undoChangeKeySig(Staff* ostaff, const Fraction& tick, KeySigEvent ke
|
|||
KeySig* ks = toKeySig(s->element(track));
|
||||
|
||||
Interval interval = staff->part()->instrument(tick)->transpose();
|
||||
Interval oldStaffInterval = staff->transpose(tick);
|
||||
KeySigEvent nkey = key;
|
||||
bool concertPitch = score->styleB(Sid::concertPitch);
|
||||
|
||||
if (interval.chromatic && !concertPitch && !nkey.isAtonal()) {
|
||||
interval.flip();
|
||||
nkey.setKey(transposeKey(key.concertKey(), interval, staff->part()->preferSharpFlat()));
|
||||
interval.flip();
|
||||
}
|
||||
|
||||
updateInstrumentChangeTranspositions(key, staff, tick);
|
||||
|
@ -5019,6 +5027,13 @@ void Score::undoChangeKeySig(Staff* ostaff, const Fraction& tick, KeySigEvent ke
|
|||
lks = nks;
|
||||
}
|
||||
}
|
||||
if (interval != staff->transpose(tick) || interval != oldStaffInterval) {
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
if (needsUpdate) {
|
||||
Fraction tickEnd = Fraction::fromTicks(ostaff->keyList()->nextKeyTick(tick.ticks()));
|
||||
transpositionChanged(ostaff->part(), ostaff->transpose(tick), tick, tickEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6300,8 +6315,6 @@ void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
|
|||
|
||||
SegmentType segmentType = SegmentType::ChordRest;
|
||||
|
||||
Tuplet* crTuplet = cr->tuplet();
|
||||
|
||||
// For linked staves the length of staffList is always > 1 since the list contains the staff itself too!
|
||||
const bool linked = ostaff->staffList().size() > 1;
|
||||
|
||||
|
@ -6370,41 +6383,26 @@ void Score::undoAddCR(ChordRest* cr, Measure* measure, const Fraction& tick)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
if (crTuplet && staff != ostaff) {
|
||||
// In case of nested tuplets, get the parent tuplet.
|
||||
Tuplet* parTuplet { nullptr };
|
||||
if (crTuplet->tuplet()) {
|
||||
// Look for a tuplet, linked to the parent tuplet of crTuplet but
|
||||
// which is on the same staff as the new ChordRest.
|
||||
for (auto e : crTuplet->tuplet()->linkList()) {
|
||||
Tuplet* t = toTuplet(e);
|
||||
if (t->staff() == newcr->staff()) {
|
||||
parTuplet = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Climb up the (possibly nested) tuplets from this chordRest
|
||||
// Make sure all tuplets are cloned and correctly nested
|
||||
DurationElement* elementBelow = cr;
|
||||
Tuplet* tupletAbove = elementBelow->tuplet();
|
||||
while (tupletAbove) {
|
||||
DurationElement* linkedElementBelow = (DurationElement*)elementBelow->findLinkedInScore(score);
|
||||
if (!linkedElementBelow) { // shouldn't happen
|
||||
break;
|
||||
}
|
||||
|
||||
// Look for a tuplet linked to crTuplet but is on the same staff as
|
||||
// the new ChordRest. Create a new tuplet if not found.
|
||||
Tuplet* newTuplet { nullptr };
|
||||
for (auto e : crTuplet->linkList()) {
|
||||
Tuplet* t = toTuplet(e);
|
||||
if (t->staff() == newcr->staff()) {
|
||||
newTuplet = t;
|
||||
break;
|
||||
}
|
||||
Tuplet* linkedTuplet = (Tuplet*)tupletAbove->findLinkedInScore(score);
|
||||
if (!linkedTuplet) {
|
||||
linkedTuplet = toTuplet(tupletAbove->linkedClone());
|
||||
linkedTuplet->setScore(score);
|
||||
linkedTuplet->setTrack(newcr->track());
|
||||
linkedTuplet->setParent(m);
|
||||
}
|
||||
linkedElementBelow->setTuplet(linkedTuplet);
|
||||
|
||||
if (!newTuplet) {
|
||||
newTuplet = toTuplet(crTuplet->linkedClone());
|
||||
newTuplet->setTuplet(parTuplet);
|
||||
newTuplet->setScore(score);
|
||||
newTuplet->setTrack(newcr->track());
|
||||
newTuplet->setParent(m);
|
||||
}
|
||||
|
||||
newcr->setTuplet(newTuplet);
|
||||
elementBelow = tupletAbove;
|
||||
tupletAbove = tupletAbove->tuplet();
|
||||
}
|
||||
|
||||
if (newcr->isRest() && (toRest(newcr)->isGap()) && !(toRest(newcr)->track() % VOICES)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -656,6 +656,12 @@ void Excerpt::cloneSpanner(Spanner* s, Score* score, track_idx_t dstTrack, track
|
|||
if (!ns->endElement()) {
|
||||
LOGD("clone Slur: no end element");
|
||||
}
|
||||
} else if (ns->isTrill()) {
|
||||
EngravingItem* startElement = (EngravingItem*)s->startElement()->findLinkedInScore(score);
|
||||
if (startElement && startElement->isChord()) {
|
||||
ns->setStartElement(startElement);
|
||||
toChord(startElement)->addStartingSpanner(ns);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ns->startElement() || !ns->endElement()) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,6 +243,10 @@ void Ornament::computeNotesAboveAndBelow(AccidentalState* accState)
|
|||
}
|
||||
|
||||
Note*& note = _notesAboveAndBelow.at(i);
|
||||
if (!note && above && _cueNoteChord) {
|
||||
note = _cueNoteChord->upNote();
|
||||
}
|
||||
|
||||
if (!note) {
|
||||
note = mainNote->clone();
|
||||
} else {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -675,8 +675,9 @@ void Spanner::computeStartElement()
|
|||
break;
|
||||
}
|
||||
|
||||
if (m_startElement && !mu::contains(m_startElement->startingSpanners(), this)) {
|
||||
m_startElement->startingSpanners().push_back(this);
|
||||
Chord* startChord = m_startElement && m_startElement->isChord() ? toChord(m_startElement) : nullptr;
|
||||
if (startChord) {
|
||||
startChord->addStartingSpanner(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -763,8 +764,9 @@ void Spanner::computeEndElement()
|
|||
break;
|
||||
}
|
||||
|
||||
if (m_endElement && !mu::contains(m_endElement->endingSpanners(), this)) {
|
||||
m_endElement->endingSpanners().push_back(this);
|
||||
Chord* endChord = m_endElement && m_endElement->isChord() ? toChord(m_endElement) : nullptr;
|
||||
if (endChord) {
|
||||
endChord->addEndingSpanner(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -122,7 +122,7 @@ void Tremolo::createBeamSegments()
|
|||
// inset trem from stems for default style
|
||||
double slope = (endAnchor.y() - startAnchor.y()) / (endAnchor.x() - startAnchor.x());
|
||||
double gapSp = stemGapSp;
|
||||
if (defaultStyle) {
|
||||
if (defaultStyle || _style == TremoloStyle::TRADITIONAL_ALTERNATE) {
|
||||
// we can eat into the stemGapSp margin if the anchorpoints are sufficiently close together
|
||||
double widthSp = (endAnchor.x() - startAnchor.x()) / spatium() - (stemGapSp * 2);
|
||||
if (!RealIsEqualOrMore(widthSp, 0.6)) {
|
||||
|
@ -132,14 +132,24 @@ void Tremolo::createBeamSegments()
|
|||
} else {
|
||||
gapSp = 0.0;
|
||||
}
|
||||
double offset = gapSp * spatium();
|
||||
startAnchor.rx() += offset;
|
||||
endAnchor.rx() -= offset;
|
||||
startAnchor.ry() += offset * slope;
|
||||
endAnchor.ry() -= offset * slope;
|
||||
BeamSegment* mainStroke = new BeamSegment(this);
|
||||
PointF xOffset = PointF(gapSp * spatium(), 0);
|
||||
PointF yOffset = PointF(0, gapSp * spatium() * slope);
|
||||
if (_style == TremoloStyle::TRADITIONAL_ALTERNATE) {
|
||||
mainStroke->line = LineF(startAnchor, endAnchor);
|
||||
startAnchor += xOffset;
|
||||
endAnchor -= xOffset;
|
||||
startAnchor += yOffset;
|
||||
endAnchor -= yOffset;
|
||||
} else {
|
||||
startAnchor += xOffset;
|
||||
endAnchor -= xOffset;
|
||||
startAnchor += yOffset;
|
||||
endAnchor -= yOffset;
|
||||
mainStroke->line = LineF(startAnchor, endAnchor);
|
||||
}
|
||||
mainStroke->level = 0;
|
||||
mainStroke->line = LineF(startAnchor, endAnchor);
|
||||
|
||||
_beamSegments.push_back(mainStroke);
|
||||
double bboxTop = _up ? std::min(mainStroke->line.y1(), mainStroke->line.y2()) : std::max(mainStroke->line.y1(), mainStroke->line.y2());
|
||||
double halfWidth = score()->styleMM(Sid::beamWidth).val() / 2. * (_up ? -1. : 1.);
|
||||
|
@ -590,6 +600,26 @@ void Tremolo::triggerLayout() const
|
|||
}
|
||||
}
|
||||
|
||||
bool Tremolo::needStartEditingAfterSelecting() const
|
||||
{
|
||||
return twoNotes();
|
||||
}
|
||||
|
||||
int Tremolo::gripsCount() const
|
||||
{
|
||||
return twoNotes() ? 3 : 0;
|
||||
}
|
||||
|
||||
Grip Tremolo::initialEditModeGrip() const
|
||||
{
|
||||
return twoNotes() ? Grip::END : Grip::NO_GRIP;
|
||||
}
|
||||
|
||||
Grip Tremolo::defaultGrip() const
|
||||
{
|
||||
return twoNotes() ? Grip::MIDDLE : Grip::NO_GRIP;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// gripsPositions
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -132,15 +132,13 @@ public:
|
|||
void setUp(bool up) { _up = up; }
|
||||
|
||||
// only need grips for two-note trems
|
||||
bool needStartEditingAfterSelecting() const override { return twoNotes(); }
|
||||
int gripsCount() const override { return 3; }
|
||||
Grip initialEditModeGrip() const override { return Grip::END; }
|
||||
Grip defaultGrip() const override { return Grip::MIDDLE; }
|
||||
bool needStartEditingAfterSelecting() const override;
|
||||
int gripsCount() const override;
|
||||
Grip initialEditModeGrip() const;
|
||||
Grip defaultGrip() const override;
|
||||
std::vector<mu::PointF> gripsPositions(const EditData&) const override;
|
||||
bool isMovable() const override { return true; }
|
||||
void startDrag(EditData&) override {}
|
||||
bool isEditable() const override { return true; }
|
||||
void startEdit(EditData&) override {}
|
||||
void endEdit(EditData&) override;
|
||||
void editDrag(EditData&) override;
|
||||
|
||||
|
|
|
@ -318,7 +318,7 @@ void PlaybackModel::updateContext(const InstrumentTrackId& trackId)
|
|||
}
|
||||
|
||||
void PlaybackModel::processSegment(const int tickPositionOffset, const Segment* segment, const std::set<staff_idx_t>& staffIdxSet,
|
||||
ChangedTrackIdSet* trackChanges)
|
||||
bool isFirstSegmentOfMeasure, ChangedTrackIdSet* trackChanges)
|
||||
{
|
||||
int segmentStartTick = segment->tick().ticks();
|
||||
|
||||
|
@ -369,21 +369,23 @@ void PlaybackModel::processSegment(const int tickPositionOffset, const Segment*
|
|||
continue;
|
||||
}
|
||||
|
||||
if (item->type() == ElementType::MEASURE_REPEAT) {
|
||||
const MeasureRepeat* measureRepeat = toMeasureRepeat(item);
|
||||
const Measure* currentMeasure = measureRepeat->measure();
|
||||
const Measure* referringMeasure = measureRepeat->referringMeasure();
|
||||
if (isFirstSegmentOfMeasure) {
|
||||
if (item->isMeasureRepeat()) {
|
||||
const MeasureRepeat* measureRepeat = toMeasureRepeat(item);
|
||||
const Measure* currentMeasure = measureRepeat->measure();
|
||||
|
||||
processMeasureRepeat(tickPositionOffset, measureRepeat, currentMeasure, staffIdx, trackChanges);
|
||||
|
||||
if (!referringMeasure || !currentMeasure) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const Measure* currentMeasure = segment->measure();
|
||||
|
||||
int currentMeasureTick = measureRepeat->measure()->tick().ticks();
|
||||
int referringMeasureTick = referringMeasure->tick().ticks();
|
||||
int repeatPositionTickOffset = currentMeasureTick - referringMeasureTick;
|
||||
if (currentMeasure->measureRepeatCount(staffIdx) > 0) {
|
||||
const MeasureRepeat* measureRepeat = currentMeasure->measureRepeatElement(staffIdx);
|
||||
|
||||
for (Segment* seg = referringMeasure->first(); seg; seg = seg->next()) {
|
||||
processSegment(tickPositionOffset + repeatPositionTickOffset, seg, { staffIdx }, trackChanges);
|
||||
processMeasureRepeat(tickPositionOffset, measureRepeat, currentMeasure, staffIdx, trackChanges);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,6 +405,34 @@ void PlaybackModel::processSegment(const int tickPositionOffset, const Segment*
|
|||
}
|
||||
}
|
||||
|
||||
void PlaybackModel::processMeasureRepeat(const int tickPositionOffset, const MeasureRepeat* measureRepeat, const Measure* currentMeasure,
|
||||
const staff_idx_t staffIdx, ChangedTrackIdSet* trackChanges)
|
||||
{
|
||||
if (!measureRepeat || !currentMeasure) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Measure* referringMeasure = measureRepeat->referringMeasure(currentMeasure);
|
||||
if (!referringMeasure) {
|
||||
return;
|
||||
}
|
||||
|
||||
int currentMeasureTick = currentMeasure->tick().ticks();
|
||||
int referringMeasureTick = referringMeasure->tick().ticks();
|
||||
int repeatPositionTickOffset = currentMeasureTick - referringMeasureTick;
|
||||
|
||||
bool isFirstSegmentOfRepeatedMeasure = true;
|
||||
|
||||
for (const Segment* seg = referringMeasure->first(); seg; seg = seg->next()) {
|
||||
if (!seg->isChordRestType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
processSegment(tickPositionOffset + repeatPositionTickOffset, seg, { staffIdx }, isFirstSegmentOfRepeatedMeasure, trackChanges);
|
||||
isFirstSegmentOfRepeatedMeasure = false;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackModel::updateEvents(const int tickFrom, const int tickTo, const track_idx_t trackFrom, const track_idx_t trackTo,
|
||||
ChangedTrackIdSet* trackChanges)
|
||||
{
|
||||
|
@ -429,6 +459,8 @@ void PlaybackModel::updateEvents(const int tickFrom, const int tickTo, const tra
|
|||
continue;
|
||||
}
|
||||
|
||||
bool isFirstSegmentOfMeasure = true;
|
||||
|
||||
for (Segment* segment = measure->first(); segment; segment = segment->next()) {
|
||||
if (!segment->isChordRestType()) {
|
||||
continue;
|
||||
|
@ -441,7 +473,8 @@ void PlaybackModel::updateEvents(const int tickFrom, const int tickTo, const tra
|
|||
continue;
|
||||
}
|
||||
|
||||
processSegment(tickPositionOffset, segment, staffToProcessIdxSet, trackChanges);
|
||||
processSegment(tickPositionOffset, segment, staffToProcessIdxSet, isFirstSegmentOfMeasure, trackChanges);
|
||||
isFirstSegmentOfMeasure = false;
|
||||
}
|
||||
|
||||
m_renderer.renderMetronome(m_score, measureStartTick, measureEndTick, tickPositionOffset,
|
||||
|
@ -479,13 +512,15 @@ bool PlaybackModel::hasToReloadTracks(const ScoreChangesRange& changesRange) con
|
|||
return false;
|
||||
}
|
||||
|
||||
const Measure* nextToLastMeasure = measureTo->nextMeasure();
|
||||
const Measure* nextMeasure = measureTo->nextMeasure();
|
||||
|
||||
if (!nextToLastMeasure) {
|
||||
return false;
|
||||
for (int i = 0; i < MeasureRepeat::MAX_NUM_MEASURES && nextMeasure; ++i) {
|
||||
if (nextMeasure->containsMeasureRepeat(changesRange.staffIdxFrom, changesRange.staffIdxTo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nextMeasure = nextMeasure->nextMeasure();
|
||||
}
|
||||
|
||||
return nextToLastMeasure->containsMeasureRepeat(changesRange.staffIdxFrom, changesRange.staffIdxTo);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -109,7 +109,9 @@ private:
|
|||
ChangedTrackIdSet* trackChanges = nullptr);
|
||||
|
||||
void processSegment(const int tickPositionOffset, const Segment* segment, const std::set<staff_idx_t>& staffIdxSet,
|
||||
ChangedTrackIdSet* trackChanges);
|
||||
bool isFirstSegmentOfMeasure, ChangedTrackIdSet* trackChanges);
|
||||
void processMeasureRepeat(const int tickPositionOffset, const MeasureRepeat* measureRepeat, const Measure* currentMeasure,
|
||||
const staff_idx_t staffIdx, ChangedTrackIdSet* trackChanges);
|
||||
|
||||
bool hasToReloadTracks(const ScoreChangesRange& changesRange) const;
|
||||
bool hasToReloadScore(const std::unordered_set<ElementType>& changedTypes) const;
|
||||
|
|
|
@ -310,6 +310,8 @@ void OrnamentsRenderer::doRender(const EngravingItem* item, const ArticulationTy
|
|||
IntervalsInfo intervalsInfo;
|
||||
if (const Ornament* ornament = chord->findOrnament()) {
|
||||
intervalsInfo = makeIntervalsInfo(ornament->intervalBelow(), ornament->intervalAbove());
|
||||
} else {
|
||||
intervalsInfo = makeIntervalsInfo(DEFAULT_ORNAMENT_INTERVAL, DEFAULT_ORNAMENT_INTERVAL);
|
||||
}
|
||||
|
||||
const DisclosurePattern& nominalPattern = search->second;
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "libmscore/score.h"
|
||||
#include "libmscore/excerpt.h"
|
||||
#include "libmscore/part.h"
|
||||
#include "libmscore/stem.h"
|
||||
#include "libmscore/tremolo.h"
|
||||
#include "libmscore/linkedobjects.h"
|
||||
#include "libmscore/measure.h"
|
||||
#include "libmscore/factory.h"
|
||||
|
@ -38,6 +40,7 @@
|
|||
#include "libmscore/stafftext.h"
|
||||
#include "libmscore/stafftextbase.h"
|
||||
#include "libmscore/playtechannotation.h"
|
||||
#include "libmscore/capo.h"
|
||||
|
||||
#include "types/string.h"
|
||||
|
||||
|
@ -72,6 +75,8 @@ const std::set<SymId> CompatUtils::ORNAMENT_IDS {
|
|||
|
||||
void CompatUtils::doCompatibilityConversions(MasterScore* masterScore)
|
||||
{
|
||||
TRACEFUNC;
|
||||
|
||||
if (!masterScore) {
|
||||
return;
|
||||
}
|
||||
|
@ -85,6 +90,10 @@ void CompatUtils::doCompatibilityConversions(MasterScore* masterScore)
|
|||
replaceOldWithNewExpressions(masterScore);
|
||||
replaceOldWithNewOrnaments(masterScore);
|
||||
resetRestVerticalOffset(masterScore);
|
||||
splitArticulations(masterScore);
|
||||
resetArticulationOffsets(masterScore);
|
||||
resetStemLengthsForTwoNoteTrems(masterScore);
|
||||
replaceStaffTextWithCapo(masterScore);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,6 +350,104 @@ void CompatUtils::reconstructTypeOfCustomDynamics(MasterScore* score)
|
|||
}
|
||||
}
|
||||
|
||||
void CompatUtils::splitArticulations(MasterScore* masterScore)
|
||||
{
|
||||
std::set<Articulation*> toRemove;
|
||||
for (Measure* meas = masterScore->firstMeasure(); meas; meas = meas->nextMeasure()) {
|
||||
for (Segment& seg : meas->segments()) {
|
||||
if (!seg.isChordRestType()) {
|
||||
continue;
|
||||
}
|
||||
for (EngravingItem* item : seg.elist()) {
|
||||
if (!item || !item->isChord()) {
|
||||
continue;
|
||||
}
|
||||
Chord* chord = toChord(item);
|
||||
for (Articulation* a : chord->articulations()) {
|
||||
if (a->isLinked()) {
|
||||
continue; // only worry about main artics, links will be done later
|
||||
}
|
||||
std::set<SymId> ids = mu::engraving::splitArticulations({ a->symId() });
|
||||
if (ids.size() <= 1) {
|
||||
continue;
|
||||
}
|
||||
toRemove.insert(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// separate into individual articulations
|
||||
for (Articulation* combinedArtic : toRemove) {
|
||||
auto components = mu::engraving::splitArticulations({ combinedArtic->symId() });
|
||||
Chord* parentChord = toChord(combinedArtic->parentItem());
|
||||
for (SymId id : components) {
|
||||
Articulation* newArtic = Factory::createArticulation(masterScore->dummy()->chord());
|
||||
newArtic->setSymId(id);
|
||||
if (parentChord->hasArticulation(newArtic)) {
|
||||
delete newArtic;
|
||||
continue;
|
||||
}
|
||||
newArtic->setParent(parentChord);
|
||||
newArtic->setTrack(combinedArtic->track());
|
||||
newArtic->setPos(combinedArtic->pos());
|
||||
newArtic->setDirection(combinedArtic->direction());
|
||||
newArtic->setAnchor(combinedArtic->anchor());
|
||||
newArtic->setColor(combinedArtic->color());
|
||||
newArtic->setPlayArticulation(combinedArtic->playArticulation());
|
||||
newArtic->setVisible(combinedArtic->visible());
|
||||
newArtic->setOrnamentStyle(combinedArtic->ornamentStyle());
|
||||
LinkedObjects* links = new LinkedObjects(masterScore);
|
||||
links->push_back(newArtic);
|
||||
newArtic->setLinks(links);
|
||||
parentChord->add(newArtic);
|
||||
|
||||
// newArtic is the main articulation
|
||||
LinkedObjects* oldLinks = combinedArtic->links();
|
||||
if (!oldLinks || oldLinks->empty()) {
|
||||
continue;
|
||||
}
|
||||
for (EngravingObject* linkedItem : *oldLinks) {
|
||||
IF_ASSERT_FAILED(linkedItem && linkedItem->isArticulation()) {
|
||||
continue;
|
||||
}
|
||||
if (linkedItem == combinedArtic) {
|
||||
continue;
|
||||
}
|
||||
Articulation* oldArtic = toArticulation(linkedItem);
|
||||
Chord* oldParent = toChord(oldArtic->parentItem());
|
||||
oldParent->add(newArtic->linkedClone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// finally, remove the combined articulations
|
||||
for (Articulation* combinedArtic : toRemove) {
|
||||
LinkedObjects* links = combinedArtic->links();
|
||||
if (!links || links->empty()) {
|
||||
Chord* parentChord = toChord(combinedArtic->parentItem());
|
||||
parentChord->remove(combinedArtic);
|
||||
delete combinedArtic;
|
||||
continue;
|
||||
}
|
||||
std::set<Articulation*> removeLinks;
|
||||
for (auto linked : *links) {
|
||||
IF_ASSERT_FAILED(linked && linked->isArticulation()) {
|
||||
continue;
|
||||
}
|
||||
removeLinks.insert(toArticulation(linked));
|
||||
}
|
||||
for (Articulation* linkedArtic : removeLinks) {
|
||||
if (linkedArtic != combinedArtic) {
|
||||
Chord* linkedParent = toChord(linkedArtic->parentItem());
|
||||
linkedParent->remove(linkedArtic);
|
||||
delete linkedArtic;
|
||||
}
|
||||
}
|
||||
Chord* parentChord = toChord(combinedArtic->parentItem());
|
||||
parentChord->remove(combinedArtic);
|
||||
delete combinedArtic;
|
||||
}
|
||||
}
|
||||
|
||||
DynamicType CompatUtils::reconstructDynamicTypeFromString(Dynamic* dynamic)
|
||||
{
|
||||
static std::vector<Dyn> sortedDynList; // copy of dynList sorted by string length
|
||||
|
@ -406,3 +513,116 @@ void CompatUtils::resetRestVerticalOffset(MasterScore* masterScore)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompatUtils::resetArticulationOffsets(MasterScore* masterScore)
|
||||
{
|
||||
for (Score* score : masterScore->scoreList()) {
|
||||
for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
|
||||
for (Segment& segment : measure->segments()) {
|
||||
if (!segment.isChordRestType()) {
|
||||
continue;
|
||||
}
|
||||
for (EngravingItem* item : segment.elist()) {
|
||||
if (!item || !item->isChord()) {
|
||||
continue;
|
||||
}
|
||||
Chord* chord = toChord(item);
|
||||
for (Articulation* artic : chord->articulations()) {
|
||||
if (!artic) {
|
||||
continue;
|
||||
}
|
||||
artic->setProperty(Pid::OFFSET, PointF());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompatUtils::resetStemLengthsForTwoNoteTrems(MasterScore* masterScore)
|
||||
{
|
||||
for (Score* score : masterScore->scoreList()) {
|
||||
for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
|
||||
for (Segment& segment : measure->segments()) {
|
||||
if (!segment.isChordRestType()) {
|
||||
continue;
|
||||
}
|
||||
for (EngravingItem* item : segment.elist()) {
|
||||
if (!item || !item->isChord()) {
|
||||
continue;
|
||||
}
|
||||
Chord* chord = toChord(item);
|
||||
Tremolo* trem = chord->tremolo();
|
||||
Stem* stem = chord->stem();
|
||||
if (stem && trem && trem->twoNotes()) {
|
||||
if (stem->userLength() != Millimetre(0)) {
|
||||
stem->setUserLength(Millimetre(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompatUtils::replaceStaffTextWithCapo(MasterScore* score)
|
||||
{
|
||||
TRACEFUNC;
|
||||
|
||||
std::set<StaffTextBase*> oldCapoSet;
|
||||
|
||||
for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
|
||||
for (Segment* segment = measure->first(); segment; segment = segment->next()) {
|
||||
for (EngravingItem* annotation : segment->annotations()) {
|
||||
if (!annotation || !annotation->isStaffTextBase()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StaffTextBase* text = toStaffTextBase(annotation);
|
||||
|
||||
if (text->capo() > 0) {
|
||||
oldCapoSet.insert(text);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
LinkedObjects* links = text->links();
|
||||
if (!links || links->empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (EngravingObject* linked : *links) {
|
||||
if (linked != text && linked && linked->isStaffTextBase()) {
|
||||
oldCapoSet.insert(toStaffTextBase(linked));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (StaffTextBase* oldCapo : oldCapoSet) {
|
||||
Segment* parentSegment = oldCapo->segment();
|
||||
Capo* newCapo = Factory::createCapo(parentSegment);
|
||||
|
||||
int capoFretPosition = oldCapo->capo() - 1;
|
||||
|
||||
CapoParams params;
|
||||
params.active = capoFretPosition > 0;
|
||||
params.fretPosition = capoFretPosition;
|
||||
|
||||
newCapo->setTrack(oldCapo->track());
|
||||
newCapo->setParams(params);
|
||||
newCapo->setProperty(Pid::PLACEMENT, oldCapo->placement());
|
||||
|
||||
LinkedObjects* links = oldCapo->links();
|
||||
newCapo->setLinks(links);
|
||||
if (links) {
|
||||
links->push_back(newCapo);
|
||||
}
|
||||
|
||||
parentSegment->add(newCapo);
|
||||
parentSegment->removeAnnotation(oldCapo);
|
||||
|
||||
delete oldCapo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,8 +50,12 @@ private:
|
|||
static void replaceOldWithNewOrnaments(MasterScore* score);
|
||||
static void replaceOldWithNewExpressions(MasterScore* score);
|
||||
static void reconstructTypeOfCustomDynamics(MasterScore* score);
|
||||
static void splitArticulations(MasterScore* score);
|
||||
static DynamicType reconstructDynamicTypeFromString(Dynamic* dynamic);
|
||||
static void resetRestVerticalOffset(MasterScore* masterScore);
|
||||
static void resetArticulationOffsets(MasterScore* masterScore);
|
||||
static void resetStemLengthsForTwoNoteTrems(MasterScore* masterScore);
|
||||
static void replaceStaffTextWithCapo(MasterScore* masterScore);
|
||||
};
|
||||
}
|
||||
#endif // MU_ENGRAVING_COMPATUTILS_H
|
||||
|
|
|
@ -1379,18 +1379,21 @@ static void readPedal114(XmlReader& e, ReadContext& ctx, Pedal* pedal)
|
|||
text.at(0).isDigit()
|
||||
? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
|
||||
: text));
|
||||
pedal->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
|
||||
} else if (tag == "continueSymbol") {
|
||||
String text(e.readText());
|
||||
pedal->setContinueText(String(u"<sym>%1</sym>").arg(
|
||||
text.at(0).isDigit()
|
||||
? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
|
||||
: text));
|
||||
pedal->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
|
||||
} else if (tag == "endSymbol") {
|
||||
String text(e.readText());
|
||||
pedal->setEndText(String(u"<sym>%1</sym>").arg(
|
||||
text.at(0).isDigit()
|
||||
? resolveSymCompatibility(SymId(text.toInt()), ctx.mscoreVersion())
|
||||
: text));
|
||||
pedal->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
|
||||
} else if (tag == "beginSymbolOffset") { // obsolete
|
||||
e.readPoint();
|
||||
} else if (tag == "continueSymbolOffset") { // obsolete
|
||||
|
|
|
@ -1987,16 +1987,19 @@ static bool readTextLineProperties(XmlReader& e, ReadContext& ctx, TextLineBase*
|
|||
Text* text = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
|
||||
readText206(e, ctx, text, tl);
|
||||
tl->setBeginText(text->xmlText());
|
||||
tl->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
|
||||
delete text;
|
||||
} else if (tag == "continueText") {
|
||||
Text* text = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
|
||||
readText206(e, ctx, text, tl);
|
||||
tl->setContinueText(text->xmlText());
|
||||
tl->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
|
||||
delete text;
|
||||
} else if (tag == "endText") {
|
||||
Text* text = Factory::createText(ctx.dummy(), TextStyleType::DEFAULT, false);
|
||||
readText206(e, ctx, text, tl);
|
||||
tl->setEndText(text->xmlText());
|
||||
tl->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
|
||||
delete text;
|
||||
} else if (tag == "beginHook") {
|
||||
tl->setBeginHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
|
||||
|
|
|
@ -96,6 +96,8 @@ Err Read400::readScore(Score* score, XmlReader& e, rw::ReadInOutData* data)
|
|||
ex->setTracksMapping(ctx.tracks());
|
||||
}
|
||||
|
||||
ctx.clearOrphanedConnectors();
|
||||
|
||||
if (data) {
|
||||
data->links = ctx.readLinks();
|
||||
data->settingsCompat = ctx.settingCompat();
|
||||
|
|
|
@ -501,6 +501,56 @@ void ReadContext::reconnectBrokenConnectors()
|
|||
doReconnectBrokenConnectors();
|
||||
}
|
||||
|
||||
void ReadContext::clearOrphanedConnectors()
|
||||
{
|
||||
if (_connectors.empty() && _pendingConnectors.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
|
||||
|
||||
std::set<LinkedObjects*> deletedLinks;
|
||||
|
||||
auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
|
||||
EngravingItem* conn = c ? c->releaseConnector() : nullptr;
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
LinkedObjects* links = conn->links();
|
||||
bool linksWillBeDeleted = links && links->size() == 1;
|
||||
|
||||
if (!conn->isTuplet()) { // tuplets are added to score even when not finished
|
||||
if (linksWillBeDeleted) {
|
||||
deletedLinks.insert(links);
|
||||
}
|
||||
|
||||
delete conn;
|
||||
}
|
||||
};
|
||||
|
||||
if (!_connectors.empty()) {
|
||||
for (auto& c : _connectors) {
|
||||
deleteConnectors(c);
|
||||
}
|
||||
_connectors.clear();
|
||||
}
|
||||
|
||||
if (!_pendingConnectors.empty()) {
|
||||
for (auto& c : _pendingConnectors) {
|
||||
deleteConnectors(c);
|
||||
}
|
||||
_pendingConnectors.clear();
|
||||
}
|
||||
|
||||
for (auto& it : m_staffLinkedElements) {
|
||||
std::vector<std::pair<LinkedObjects*, Location> >& vector = it.second;
|
||||
mu::remove_if(vector, [&deletedLinks](std::pair<LinkedObjects*, Location>& pair){
|
||||
return deletedLinks.count(pair.first);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ReadContext::doReconnectBrokenConnectors()
|
||||
{
|
||||
if (_connectors.empty()) {
|
||||
|
|
|
@ -175,6 +175,7 @@ public:
|
|||
void addConnectorInfoLater(std::shared_ptr<read400::ConnectorInfoReader> c); // add connector info to be checked after calling checkConnectors()
|
||||
void checkConnectors();
|
||||
void reconnectBrokenConnectors();
|
||||
void clearOrphanedConnectors();
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ PropertyValue TRead::readPropertyValue(Pid id, XmlReader& e, ReadContext& ctx)
|
|||
case P_TYPE::ORNAMENT_STYLE:
|
||||
return PropertyValue::fromValue(TConv::fromXml(e.readAsciiText(), OrnamentStyle::DEFAULT));
|
||||
case P_TYPE::ORNAMENT_INTERVAL:
|
||||
return PropertyValue(TConv::fromXml(e.readText(), OrnamentInterval()));
|
||||
return PropertyValue(TConv::fromXml(e.readText(), DEFAULT_ORNAMENT_INTERVAL));
|
||||
case P_TYPE::POINT:
|
||||
return PropertyValue::fromValue(e.readPoint());
|
||||
case P_TYPE::SCALE:
|
||||
|
@ -1279,6 +1279,11 @@ void TRead::read(KeySig* s, XmlReader& e, ReadContext& ctx)
|
|||
if (sig.custom() && sig.customKeyDefs().empty()) {
|
||||
sig.setMode(KeyMode::NONE);
|
||||
}
|
||||
// if there are more than 6 accidentals in transposing key, it cannot be PreferSharpFlat::AUTO
|
||||
if (p && !s->concertPitch() && (sig.key() > 6 || sig.key() < -6)
|
||||
&& p->preferSharpFlat() == PreferSharpFlat::AUTO && !p->instrument(s->tick())->transpose().isZero()) {
|
||||
p->setPreferSharpFlat(PreferSharpFlat::NONE);
|
||||
}
|
||||
|
||||
s->setKeySigEvent(sig);
|
||||
}
|
||||
|
@ -2536,6 +2541,9 @@ void TRead::read(ChordLine* l, XmlReader& e, ReadContext& ctx)
|
|||
int type = e.intAttribute("type");
|
||||
double x = e.doubleAttribute("x");
|
||||
double y = e.doubleAttribute("y");
|
||||
double spatium = ctx.spatium();
|
||||
x *= spatium;
|
||||
y *= spatium;
|
||||
switch (PainterPath::ElementType(type)) {
|
||||
case PainterPath::ElementType::MoveToElement:
|
||||
path.moveTo(x, y);
|
||||
|
|
|
@ -98,6 +98,8 @@ Err Read410::readScore(Score* score, XmlReader& e, rw::ReadInOutData* data)
|
|||
ex->setTracksMapping(ctx.tracks());
|
||||
}
|
||||
|
||||
ctx.clearOrphanedConnectors();
|
||||
|
||||
if (data) {
|
||||
data->links = ctx.readLinks();
|
||||
data->settingsCompat = ctx.settingCompat();
|
||||
|
|
|
@ -501,6 +501,56 @@ void ReadContext::reconnectBrokenConnectors()
|
|||
doReconnectBrokenConnectors();
|
||||
}
|
||||
|
||||
void ReadContext::clearOrphanedConnectors()
|
||||
{
|
||||
if (_connectors.empty() && _pendingConnectors.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD("XmlReader::~XmlReader: there are unpaired connectors left");
|
||||
|
||||
std::set<LinkedObjects*> deletedLinks;
|
||||
|
||||
auto deleteConnectors = [&deletedLinks](std::shared_ptr<ConnectorInfoReader> c) {
|
||||
EngravingItem* conn = c ? c->releaseConnector() : nullptr;
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
LinkedObjects* links = conn->links();
|
||||
bool linksWillBeDeleted = links && links->size() == 1;
|
||||
|
||||
if (!conn->isTuplet()) { // tuplets are added to score even when not finished
|
||||
if (linksWillBeDeleted) {
|
||||
deletedLinks.insert(links);
|
||||
}
|
||||
|
||||
delete conn;
|
||||
}
|
||||
};
|
||||
|
||||
if (!_connectors.empty()) {
|
||||
for (auto& c : _connectors) {
|
||||
deleteConnectors(c);
|
||||
}
|
||||
_connectors.clear();
|
||||
}
|
||||
|
||||
if (!_pendingConnectors.empty()) {
|
||||
for (auto& c : _pendingConnectors) {
|
||||
deleteConnectors(c);
|
||||
}
|
||||
_pendingConnectors.clear();
|
||||
}
|
||||
|
||||
for (auto& it : m_staffLinkedElements) {
|
||||
std::vector<std::pair<LinkedObjects*, Location> >& vector = it.second;
|
||||
mu::remove_if(vector, [&deletedLinks](std::pair<LinkedObjects*, Location>& pair){
|
||||
return deletedLinks.count(pair.first);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ReadContext::doReconnectBrokenConnectors()
|
||||
{
|
||||
if (_connectors.empty()) {
|
||||
|
|
|
@ -175,6 +175,7 @@ public:
|
|||
void addConnectorInfoLater(std::shared_ptr<ConnectorInfoReader> c); // add connector info to be checked after calling checkConnectors()
|
||||
void checkConnectors();
|
||||
void reconnectBrokenConnectors();
|
||||
void clearOrphanedConnectors();
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ PropertyValue TRead::readPropertyValue(Pid id, XmlReader& e, ReadContext& ctx)
|
|||
case P_TYPE::ORNAMENT_STYLE:
|
||||
return PropertyValue::fromValue(TConv::fromXml(e.readAsciiText(), OrnamentStyle::DEFAULT));
|
||||
case P_TYPE::ORNAMENT_INTERVAL:
|
||||
return PropertyValue(TConv::fromXml(e.readText(), OrnamentInterval()));
|
||||
return PropertyValue(TConv::fromXml(e.readText(), DEFAULT_ORNAMENT_INTERVAL));
|
||||
case P_TYPE::POINT:
|
||||
return PropertyValue::fromValue(e.readPoint());
|
||||
case P_TYPE::SCALE:
|
||||
|
@ -1284,6 +1284,11 @@ void TRead::read(KeySig* s, XmlReader& e, ReadContext& ctx)
|
|||
if (sig.custom() && sig.customKeyDefs().empty()) {
|
||||
sig.setMode(KeyMode::NONE);
|
||||
}
|
||||
// if there are more than 6 accidentals in transposing key, it cannot be PreferSharpFlat::AUTO
|
||||
if (p && !s->concertPitch() && (sig.key() > 6 || sig.key() < -6)
|
||||
&& p->preferSharpFlat() == PreferSharpFlat::AUTO && !p->instrument(s->tick())->transpose().isZero()) {
|
||||
p->setPreferSharpFlat(PreferSharpFlat::NONE);
|
||||
}
|
||||
|
||||
s->setKeySigEvent(sig);
|
||||
}
|
||||
|
@ -2525,6 +2530,9 @@ void TRead::read(ChordLine* l, XmlReader& e, ReadContext& ctx)
|
|||
int type = e.intAttribute("type");
|
||||
double x = e.doubleAttribute("x");
|
||||
double y = e.doubleAttribute("y");
|
||||
double spatium = ctx.spatium();
|
||||
x *= spatium;
|
||||
y *= spatium;
|
||||
switch (PainterPath::ElementType(type)) {
|
||||
case PainterPath::ElementType::MoveToElement:
|
||||
path.moveTo(x, y);
|
||||
|
|
|
@ -857,7 +857,10 @@ void TWrite::write(const ChordLine* item, XmlWriter& xml, WriteContext& ctx)
|
|||
xml.startElement("Path");
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const PainterPath::Element& e = path.elementAt(i);
|
||||
xml.tag("Element", { { "type", int(e.type) }, { "x", e.x }, { "y", e.y } });
|
||||
double spatium = item->spatium();
|
||||
double x = e.x / spatium;
|
||||
double y = e.y / spatium;
|
||||
xml.tag("Element", { { "type", int(e.type) }, { "x", x }, { "y", y } });
|
||||
}
|
||||
xml.endElement();
|
||||
}
|
||||
|
@ -2128,9 +2131,6 @@ void TWrite::write(const Pedal* item, XmlWriter& xml, WriteContext& ctx)
|
|||
|
||||
for (auto i : {
|
||||
Pid::END_HOOK_TYPE,
|
||||
Pid::BEGIN_TEXT,
|
||||
Pid::CONTINUE_TEXT,
|
||||
Pid::END_TEXT,
|
||||
Pid::LINE_VISIBLE,
|
||||
Pid::BEGIN_HOOK_TYPE
|
||||
}) {
|
||||
|
|
|
@ -266,12 +266,19 @@ bool MStyle::readTextStyleValCompat(XmlReader& e)
|
|||
return true;
|
||||
}
|
||||
|
||||
void MStyle::readVersion(String versionTag)
|
||||
{
|
||||
versionTag.remove(u".");
|
||||
m_version = versionTag.toInt();
|
||||
}
|
||||
|
||||
bool MStyle::read(IODevice* device, bool ign)
|
||||
{
|
||||
UNUSED(ign);
|
||||
XmlReader e(device);
|
||||
while (e.readNextStartElement()) {
|
||||
if (e.name() == "museScore") {
|
||||
readVersion(e.attribute("version"));
|
||||
while (e.readNextStartElement()) {
|
||||
if (e.name() == "Style") {
|
||||
read(e, nullptr);
|
||||
|
@ -333,16 +340,16 @@ void MStyle::read(XmlReader& e, compat::ReadChordListHook* readChordListHook)
|
|||
|| tag == "propertyDistanceHead"
|
||||
|| tag == "propertyDistanceStem"
|
||||
|| tag == "propertyDistance")
|
||||
&& defaultStyleVersion() < 400) {
|
||||
&& m_version < 400) {
|
||||
// Ignoring pre-4.0 articulation style settings. Using the new defaults instead
|
||||
e.skipCurrentElement();
|
||||
} else if ((tag == "bracketDistance")
|
||||
&& defaultStyleVersion() < 400) {
|
||||
&& m_version < 400) {
|
||||
// Ignoring pre-4.0 brackets distance settings. Using the new defaults instead.
|
||||
e.skipCurrentElement();
|
||||
} else if (tag == "pedalListStyle") { // pre-3.6.3/4.0 typo
|
||||
set(Sid::pedalLineStyle, TConv::fromXml(e.readAsciiText(), LineType::SOLID));
|
||||
} else if (tag == "chordlineThickness" && defaultStyleVersion() <= 400) {
|
||||
} else if (tag == "chordlineThickness" && m_version < 410) {
|
||||
// Ignoring pre-4.1 value as it was wrong (it wasn't user-editable anyway)
|
||||
e.skipCurrentElement();
|
||||
} else if (!readProperties(e)) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue