
    rh^                    \   S SK Jr  S SKrS SKJr  S SKJr  S SKJr  S SKJ	r	J
r
  \R                  r " S S\R                  5      r " S	 S
\5      r " S S\R                  5      r " S S\R                   5      r " S S\R$                  5      r\S:X  a  S SKr\R*                  " \5        gg)    )annotationsN)exceptions21)prebase)lookup)numberToBraille	yieldDotsc                      \ rS rSrSrSS jrSS jr\S 5       r\R                  S 5       rS r
S	 rS
 rSS jrS rS rSS jrS rS rS rS rSrg)BrailleText   u  
Object that handles all the formatting associated with braille music notation on multiple lines.

>>> bt = braille.text.BrailleText(lineLength=10, showHand='right')
>>> bt
<music21.braille.text.BrailleText 1 line, 0 headings, 10 cols>
>>> bt.lineLength
10
>>> bt.allLines
[<music21.braille.text.BrailleTextLine '⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'>]
>>> bt.rightHandSymbol
True
>>> bt.leftHandSymbol
False
>>> bt.allHeadings
[]
Nc                    Xl         S U l        / U l        U R                  5         S U l        SU l        SU l        / U l        X l        g )NF)	
lineLengthcurrentLineallLinesmakeNewLine	_showHandrightHandSymbolleftHandSymbolallHeadingsshowHand)selfr   r   s      N/home/james-whalen/.local/lib/python3.13/site-packages/music21/braille/text.py__init__BrailleText.__init__)   sF    $$#     c                    [        U R                  5      nU S3US:w  a  SOS-   n[        U R                  5      nU S3US:w  a  SOS-   nU SU SU R                   S3$ )Nz line   s z headingz, z cols)lenr   r   r   )r   line_lenline_strheading_lenheading_strs        r   _reprInternalBrailleText._reprInternal6   sq    t}}%Zu%A2F$**+$X.9I#rR2k]"T__,=UCCr   c                    U R                   $ N)r   r   s    r   r   BrailleText.showHand>   s    ~~r   c                X    US:X  a  SU l         g US:X  a  SU l        g Ub  [        S5      eg )NrightTleftzIllegal hand sign request.)r   r   BrailleTextException)r   newHands     r   r   r)   B   s9    g#'D "&D &'CDD !r   c                b   U R                   R                  S:w  a  U R                  5         [        U R                  5      S-
  nUnU R                   nUR                  5        H/  nSUl        UR                  USS9  U R                  5         US-  nM1     U R                  R                  X#45        g)u  
adds a heading to the BrailleText.  Heading can be a single or multiple line
Unicode string representing a heading.

These headings are not stored in allHeadings, but instead in .allLines,
what .allHeadings stores is the index of the start of a heading section
and the index of the end of a heading section.

(since each BrailleTextLine knows whether it is a heading or not, storing
the index of headings might be overkill)

>>> bt = braille.text.BrailleText(lineLength=10)
>>> headingText = braille.basic.timeSigToBraille(meter.TimeSignature('4/8'))
>>> bt.addHeading(headingText)
>>> len(bt.allLines)
2
>>> bt.allLines[0].isHeading
True
>>> print(str(bt.allLines[0]))
⠼⠙⠦
>>> bt.allHeadings
[(0, 1)]
>>> bt.addMeasureNumber(7)
>>> headingText = braille.basic.timeSigToBraille(meter.TimeSignature('3/4'))
>>> bt.addHeading(headingText)
>>> len(bt.allLines)
4
>>> bt.allHeadings
[(0, 1), (2, 3)]
r   r   TFaddSpaceN)	r   textLocationr   r   r   
splitlines	isHeadingappendr   )r   heading
indexStart
indexFinalbrailleCurrentLineheadingLines         r   
addHeadingBrailleText.addHeadingK   s    > ((A-'!+

!--"--/K+/(%%kE%B!OJ	 0
 	 89r   c                d    UR                  [        S   5       H  nU R                  U5        M     g)zk
Adds an expression long enough that it is split at
each space symbol such that line wrapping could occur.
spaceN)splitsymbolsappendOrInsertCurrent)r   longExprbrailleExprs      r   addLongExpressionBrailleText.addLongExpressionw   s*    
 $>>''*:;K&&{3 <r   c                    U R                  5         U R                  (       d  U R                  (       a,  U R                  U5        U R                  R                  USS9  gU R                  R                  SU5        g)uc  
Adds a NoteGrouping to a new line, prefacing that new line
with the appropriate spaces or keyboard symbols and dots.

>>> bt = braille.text.BrailleText(10)
>>> bt.currentLine.append('hi', addSpace=False)
>>> print(str(bt))
hi
>>> c = braille.lookup.pitchNameToNotes['C']['quarter']  # dots 1456
>>> bt.addToNewLine(c + c + c)
>>> print(str(bt))
hi
⠀⠀⠹⠹⠹

It is done differently if there are hand symbols involved:

>>> bt = braille.text.BrailleText(10)
>>> bt.showHand = 'right'
>>> bt.currentLine.append('hi', addSpace=False)
>>> bt.addToNewLine(c + c + c)
>>> print(str(bt))
hi
⠨⠜⠄⠹⠹⠹

Fr0      N)r   r   r   !optionalAddKeyboardSymbolsAndDotsr   r5   insert)r   brailleNoteGroupings     r   addToNewLineBrailleText.addToNewLine   sa    4 	4#6#6223FG##$7%#H##A':;r   c                    U R                   R                  XS9(       a  U R                   R                  XS9  gU R                  5         U R                   R	                  SU5        g)u  
append expression to the current line if it is possible,
or make a new line and insert it there:

>>> bt = braille.text.BrailleText(lineLength=10)
>>> bt.appendOrInsertCurrent('hello', addSpace=False)
>>> print(str(bt))
hello
>>> bt.appendOrInsertCurrent(braille.lookup.symbols['space'] + 'hi')
>>> print(str(bt))
hello⠀⠀hi
>>> bt.appendOrInsertCurrent(braille.lookup.symbols['space'] + 'there')
>>> print(str(bt))
hello⠀⠀hi
⠀⠀⠀there
r0   rG   N)r   	canAppendr5   r   rI   )r   rC   r1   s      r   rA   !BrailleText.appendOrInsertCurrent   sT    " %%k%E##K#C##A{3r   c                   U R                  U5      n U R                  R                  XS9  SU R                  l
        g ! [         Ga    U R	                  5         U R
                  (       d  U R                  (       a  U R
                  (       a$  U R                  R                  S[        S   5        O4U R                  (       a#  U R                  R                  S[        S   5        [        US   5       H  nU R                  R                  USS9  M     U R                  R                  USS9   GNU R                  R                  SU5         GN#f = f)Nr0   rG   rh_keyboardlh_keyboardr   FT)rH   r   r5   r-   r   r   r   rI   r@   r   containsNoteGrouping)r   inaccordr1   dots       r   addInaccordBrailleText.addInaccord   s   99(C	5##H#@ 15- $ 	5##t':':''$$++Aw}/EF(($$++Aw}/EF$Xa[1C$$++C%+@ 2  ''5'A  ''84	5s   > C/E0EEc                    [        U[        5      (       a  [        U5      nU R                  R                  S:w  a  U R                  5         U R                  R                  USS9  g)u  
Add a measure number (either a braille number or an int).

>>> bt = braille.text.BrailleText(lineLength=10)
>>> bt
<music21.braille.text.BrailleText 1 line, 0 headings, 10 cols>
>>> bt.allLines
[<music21.braille.text.BrailleTextLine '⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'>]
>>> bt.addMeasureNumber(4)
>>> print(str(bt.allLines[0]))
⠼⠙
>>> bt.currentLine.textLocation
2

If there are already lines, then add a new one:

>>> bt.addMeasureNumber(5)
>>> bt.allLines
[<music21.braille.text.BrailleTextLine '⠼⠙⠀⠀⠀⠀⠀⠀⠀⠀'>,
 <music21.braille.text.BrailleTextLine '⠼⠑⠀⠀⠀⠀⠀⠀⠀⠀'>]
>>> print(str(bt.allLines[-1]))
⠼⠑
r   Fr0   N)
isinstanceintr   r   r2   r   r5   )r   measureNumbers     r   addMeasureNumberBrailleText.addMeasureNumber   sT    0 mS))+M:M((A->r   c                .   SnU R                   R                  (       d  U R                  (       d  U R                  (       a  U R                   R                  S:X  a  SnU R                  (       a"  U R                   R                  [        S   US9  O2U R                  (       a!  U R                   R                  [        S   US9  U(       a/  [        US   5       H  nU R                   R                  USS9  M     SnU R                   R                  S:X  a  SnU$ )z
Adds symbols for rh_keyboard or lh_keyboard depending on what
is appropriate

returns a boolean indicating whether a space needs to be added
before the next symbol is needed.
Tr   FrQ   r0   rR   )r   rS   r   r   r2   r5   r@   r   )r   noteGroupingr1   rU   s       r   rH   -BrailleText.optionalAddKeyboardSymbolsAndDots   s       55))T-@-@,,1 ##  ''(>'R$$  ''(>'R$\!_5C$$++C%+@ 6H((A-Hr   c                ^    SnU R                   R                  S:X  a  SnU R                  XS9  g)u  
Appends signatures to the current location if there is space, otherwise appends to
a new line:

>>> bt = braille.text.BrailleText(lineLength=5)
>>> bt.addSignatures(braille.basic.timeSigToBraille(meter.TimeSignature('4/8')))
>>> print(str(bt.currentLine))
⠼⠙⠦
>>> bt.addSignatures(braille.basic.timeSigToBraille(meter.TimeSignature('3/4')))
>>> print(str(bt.currentLine))
⠀⠀⠼⠉⠲
>>> len(bt.allLines)
2
Tr   Fr0   N)r   r2   rA   )r   
signaturesr1   s      r   addSignaturesBrailleText.addSignatures  s3     ((A-H"":"Ar   c                    [        U R                  5      U l        U R                  R	                  U R                  5        g)z
Add a newline to the BrailleText

>>> bt = braille.text.BrailleText(lineLength=10)
>>> len(bt.allLines)
1
>>> bt.makeNewLine()
>>> len(bt.allLines)
2
>>> bt.makeNewLine()
>>> len(bt.allLines)
3
N)BrailleTextLiner   r   r   r5   r(   s    r   r   BrailleText.makeNewLine  s-     +4??;T--.r   c                   U R                    H  u  pSn[        U[        U R                  5      5       HG  nU R                  U   R                  (       a    O(U R                  U   R
                  n[        X55      nMI     [        X5       Hw  nU R                  U   n[        U5      nUR                  [        S   5      nU[        U5      :  d  MF  UR                  U[        S   5      nUR                  SU5        X7l        My     M     g)u7  
Recenter each of the headings so that they exactly align
with the text beneath them.

Demonstration with non braille text:

>>> heading1 = 'hello'
>>> body1 = 'anyoneHome?' + braille.lookup.symbols['space'] + 'yup!'
>>> bt = braille.text.BrailleText(lineLength=12)
>>> bt.addHeading(heading1)
>>> bt.addLongExpression(body1)
>>> bt.allHeadings
[(0, 1)]
>>> bt.recenterHeadings()
>>> print(str(bt))
⠀⠀⠀hello⠀⠀⠀⠀
⠀anyoneHome?
⠀⠀yup!

Each heading is aligned with its own text

>>> heading2 = 'buh'
>>> body2 = 'short' + braille.lookup.symbols['space'] + 'court'
>>> bt.addHeading(heading2)
>>> bt.addLongExpression(body2)
>>> bt.allHeadings
[(0, 1), (3, 4)]
>>> bt.recenterHeadings()
>>> print(str(bt))
⠀⠀⠀hello⠀⠀⠀⠀
⠀anyoneHome?
⠀⠀yup!
⠀⠀⠀⠀buh⠀⠀⠀⠀⠀
⠀short⠀court
r   r>   N)r   ranger   r   r4   r2   maxstrstripr@   centerrI   )	r   r7   r8   maxLineLengthir   jbrailleTextLinelineStrToCenters	            r   recenterHeadingsBrailleText.recenterHeadings)  s    H )-(8(8$ZM:s4=='9:==#--!]]1-::
 #M >	 ;
 :2"&--"2"%o"6"1"7"78H"I 3#77&5&<&<]GT[L\&]O#**1o>3@0 3 )9r   c                    U R                  5         SR                  U R                   Vs/ s H  n[        U5      PM     sn5      $ s  snf )N
)rs   joinr   rk   )r   lines     r   __str__BrailleText.__str__]  s7    yy>#d)>??>s   A)r   r   r   r   r   r   r   r   )(   Nreturnrk   Tr'   )__name__
__module____qualname____firstlineno____doc__r   r$   propertyr   setterr;   rD   rK   rA   rV   r\   rH   rc   r   rs   ry   __static_attributes__ r   r   r
   r
      s}    "!D   __E E*:X4<B4.5$?>6B*/"2Ah@r   r
   c                  <   ^  \ rS rSrSrSU 4S jjrS rS rSrU =r	$ )BrailleKeyboardib  z<
A subclass of BrailleText that handles both hands at once.
c                J   > [         TU ]  US9  S U l        S U l        SU l        g )N)r   r   )superr   rightHandLineleftHandLinehighestMeasureNumberLength)r   r   	__class__s     r   r   BrailleKeyboard.__init__g  s+    J/! *+'r   c                X   U R                   R                  S:X  a  U R                   U l        O?[        U R                  5      U l        U R
                  R                  U R                  5        [        U R                  5      U l        U R
                  R                  U R                  5        g )Nr   )r   r2   r   rf   r   r   r5   r   r(   s    r   makeNewLinesBrailleKeyboard.makeNewLinest  sv    ((A-!%!1!1D!0!ADMM  !3!34+DOO<T../r   c                   U R                   c  U R                  c  U R                  5         U R                   R                  S:X  aW  U R                   R	                  U R
                  [        U5      -
  U5        U R                   R                  U R                  l        SnU R                   R                  SL a  SnU R                   R                  [        S   SS9  U R                  R                  [        S   SS9  U(       a/  [        US   5       H  nU R                   R                  USS9  M     U(       a/  [        US   5       H  nU R                  R                  USS9  M     U R                   R                  X$S9(       a  U R                  R                  X4S9(       a  U(       a  U R                  R                  X4S9  U(       a  U R                   R                  X$S9  U R                   R                  U R                  R                  :  a'  U R                   R                  U R                  l        GOU R                  R                  U R                   l        GOU R                  5         U R                   R	                  U R
                  [        U5      -
  U5        U R                   R                  U R                  l        U R                   R                  [        S   SS9  U R                  R                  [        S   SS9  U(       aI  [        US   5       H  nU R                   R                  USS9  M     U R                   R                  USS9  U(       aI  [        US   5       H  nU R                  R                  USS9  M     U R                  R                  USS9  U R                   R                  U R                  R                  :  a&  U R                   R                  U R                  l        O%U R                  R                  U R                   l        SU R                   l        SU R                  l        g )Nr   TFrQ   r0   rR   )r   r   r   r2   rI   r   r   rS   r5   r@   r   rN   )r   r[   noteGroupingRnoteGroupingLr1   rU   s         r   addNoteGroupings BrailleKeyboard.addNoteGroupings~  sy   %$*;*;*C**a/%%d&E&EMHZ&Z&35-1-?-?-L-LD*22e;H%%gm&<t%L$$W]%;d$K$]1%56C&&--cE-B 7$]1%56C%%,,S5,A 7(((J%%///Q!!(((J""))-)K!!..1B1B1O1OO151C1C1P1P!!.262C2C2P2P""/%%d&E&EMHZ&Z&35-1-?-?-L-LD*%%gm&<t%L$$W]%;d$K$]1%56C&&--cE-B 7""))-%)H$]1%56C%%,,S5,A 7!!(((G!!..1B1B1O1OO151C1C1P1P!!.262C2C2P2P""/26/15.r   )r   r   r   r{   )
r   r   r   r   r   r   r   r   r   __classcell__)r   s   @r   r   r   b  s    ,016 16r   r   c                  \    \ rS rSrSrSSS jjrSS jrSS jrS rSS jr	S r
S	 rS
 rSrg)rf   i  u  
An object representing a single line of braille text:

The initial value is the length of the line:

>>> btl = braille.text.BrailleTextLine(40)
>>> btl
<music21.braille.text.BrailleTextLine '⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'>
>>> btl.isHeading
False
>>> btl.containsNoteGrouping
False
>>> btl.lineLength
40
>>> btl.textLocation
0
>>> btl.highestUsedLocation
0
>>> btl.allChars == 40 * [braille.lookup.symbols['space']]
True

>>> btl.append(braille.lookup.symbols['tie'])
>>> btl
<music21.braille.text.BrailleTextLine '⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'>
>>> print(str(btl))
⠀⠈⠉
c                    SU l         SU l        Xl        U R                  [        S   /-  U l        SU l        SU l        g )NFr>   r   )r4   rS   r   r@   allCharsr2   highestUsedLocation)r   r   s     r   r   BrailleTextLine.__init__  s>    $)!$77+;*<<#$ r   c                J    [        SR                  U R                  5      5      $ )Nr   )reprrw   r   r(   s    r   r$   BrailleTextLine._reprInternal  s    BHHT]]+,,r   c                ^   U R                  X5      (       d  [        S5      eU(       a5  [        S   U R                  U R                  '   U =R                  S-  sl        [        U5       H0  nX0R                  U R                  '   U =R                  S-  sl        M2     U R                  U l        g)uE  
Appends text (with optional space at the beginning) or raises an
exception if it cannot be appended.

>>> btl = braille.text.BrailleTextLine(6)
>>> btl.append(braille.lookup.symbols['tie'], addSpace=False)
>>> print(str(btl))
⠈⠉
>>> btl.textLocation
2
>>> btl.highestUsedLocation
2

Default is to add a space:

>>> btl.append(braille.lookup.symbols['tie'])
>>> print(str(btl))
⠈⠉⠀⠈⠉

Out of room:

>>> btl.append(braille.lookup.symbols['tie'])
Traceback (most recent call last):
music21.braille.text.BrailleTextException: Text does not fit at end of braille text line.

Text is appended at `textLocation`, overwriting other text that might be there.

>>> btl.textLocation = btl.highestUsedLocation = 0
>>> btl.append('hi', addSpace=False)
>>> btl.textLocation = btl.highestUsedLocation = 5
>>> print(str(btl))
hi⠀⠈⠉
z.Text does not fit at end of braille text line.r>   r   N)rN   r-   r@   r   r2   listr   )r   textr1   chars       r   r5   BrailleTextLine.append  s    D ~~d--&'WXX/6w/?DMM$++,"JD/3MM$++,"  $(#4#4 r   c                    U R                  X5      (       d  [        S5      e[        X!S9 H  u  p4X@R                  U'   M     U[	        U5      -   U l        [        U R                  U R
                  5      U l        g)u  
Inserts text at a certain location, updating textLocation and possibly
highestUsedLocation:

>>> btl = braille.text.BrailleTextLine(6)
>>> btl.insert(2, braille.lookup.symbols['tie'])
>>> print(str(btl))
⠀⠀⠈⠉
>>> btl.textLocation
4
>>> btl.highestUsedLocation
4

>>> btl.insert(0, braille.lookup.symbols['tie'])

It looks like we have deleted the previous tie:

>>> print(str(btl))
⠈⠉

But that's because only characters up to .textLocation are printed
(this may change later)

>>> btl.textLocation
2
>>> btl.highestUsedLocation
4

Let's change textLocation and now see:

>>> btl.textLocation = btl.highestUsedLocation
>>> print(str(btl))
⠈⠉⠈⠉

Inserting beyond the end creates an error:

>>> btl.insert(5, braille.lookup.symbols['tie'])
Traceback (most recent call last):
music21.braille.text.BrailleTextException: Text cannot be inserted at specified location.

Unlike list inserts, this insert overwrites the previous text:

>>> btl.insert(0, 'hi')
>>> btl.textLocation = btl.highestUsedLocation
>>> print(str(btl))
hi⠈⠉
z.Text cannot be inserted at specified location.)startN)	canInsertr-   	enumerater   r   r2   rj   r   )r   r2   r   ro   r   s        r   rI   BrailleTextLine.insert  si    ` ~~l11&'WXX :GA#MM! ;(3t94#&t'?'?ARAR#S r   c                    [        U R                  U R                  5      nU(       a  SOSnU[        U5      -   U-   U R                  :  a  gg)a  
Returns True if there is enough space in this line to append the text, or False
if not:

>>> btl = braille.text.BrailleTextLine(10)
>>> btl.canAppend('1234567890', addSpace=False)
True
>>> btl.canAppend('12345678901', addSpace=False)
False
>>> btl.canAppend('1234567890', addSpace=True)
False
>>> btl.textLocation
0
>>> btl.textLocation = 5
>>> btl.canAppend('12345', addSpace=False)
True
>>> btl.canAppend('123456', addSpace=False)
False

If highestUsedLocation > textLocation, highestUsedLocation is used instead:

>>> btl.highestUsedLocation = 7
>>> btl.canAppend('123', addSpace=False)
True
>>> btl.canAppend('1234', addSpace=False)
False
r   r   FT)rj   r   r2   r   r   )r   r   r1   searchLocationaddSpaceAmounts        r   rN   BrailleTextLine.canAppend<  sE    8 T55t7H7HI&ASY&74??Jr   c                >    U[        U5      -   U R                  :  a  gg)z
Returns True if there is enough space starting at textLocation to append
the text. False otherwise:

>>> btl = braille.text.BrailleTextLine(10)
>>> btl.canInsert(4, '123456')
True
>>> btl.canInsert(5, '123456')
False
FT)r   r   )r   r2   r   s      r   r   BrailleTextLine.canInsert_  s     #d)#doo5r   c                    U R                   S-
  nUS:  a  gU R                  U   nU[        S   :X  a,  [        S   U R                  U'   U =R                   S-  sl         gg)u  
Occasionally a line ends with a hyphen because
the last appender thought it would be helpful, such as
to put more characters into a line.  But in case it
is not, then this method will change that last character
to a space and set textLocation back one character,
so it is not printed.

>>> bt = braille.text.BrailleTextLine(10)
>>> bt.append('hi', addSpace=False)
>>> bt.append(braille.lookup.symbols['music_hyphen'], addSpace=False)
>>> print(str(bt))
hi⠐
>>> bt.textLocation
3
>>> print(bt.allChars[2])
⠐
>>> bt.lastHyphenToSpace()
>>> print(str(bt))
hi
>>> bt.allChars[2] == braille.lookup.symbols['space']
True
>>> bt.textLocation
2
r   r   Nmusic_hyphenr>   )r2   r   r@   )r   prevLocprevChars      r   lastHyphenToSpace!BrailleTextLine.lastHyphenToSpaceo  sb    4 ##a'Q;==)w~..%,W%5DMM'"" /r   c                R    SR                  U R                  SU R                   5      $ )Nr   r   )rw   r   r2   r(   s    r   ry   BrailleTextLine.__str__  s"    wwt}}Qt'8'89::r   )r   rS   r   r4   r   r2   Nr   )r   rZ   r|   r~   )r   r   r   r   r   r   r$   r5   rI   rN   r   r   ry   r   r   r   r   rf   rf     s4    6%-*5X5Tn!F  #D;r   rf   c                      \ rS rSrSrg)r-   i  r   Nr   r   r   r   r   r   r   r   r-   r-         r   r-   c                      \ rS rSrSrg)Testi  r   Nr   r   r   r   r   r     r   r   r   __main__)
__future__r   unittestmusic21r   r   music21.brailler   music21.braille.basicr   r   r@   ProtoM21Objectr
   r   rf   Music21Exceptionr-   TestCaser   r   mainTestr   r   r   <module>r      s    #     " <
..H@'(( H@V
M6k M6``;g,, `;J	<88 		8 	 zT r   