
    3j^                    Z   d dl mZ d dlZd dlmZ d dlmZ d dlmZ d dlm	Z	m
Z
 ej                  Z G d dej                        Z G d	 d
e      Z G d dej                        Z G d dej                         Z G d dej$                        Zedk(  rd dlZ ej*                  e       yy)    )annotationsN)exceptions21)prebase)lookup)numberToBraille	yieldDotsc                      e Zd ZdZddZddZed        Zej                  d        Zd Z	d Z
d	 Zdd
Zd Zd ZddZd Zd Zd Zd Zy)BrailleTextu  
    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                    || _         d | _        g | _        | j                          d | _        d| _        d| _        g | _        || _        y )NF)	
lineLengthcurrentLineallLinesmakeNewLine	_showHandrightHandSymbolleftHandSymbolallHeadingsshowHand)selfr   r   s      A/DATA/.local/lib/python3.12/site-packages/music21/braille/text.py__init__zBrailleText.__init__(   sJ    $$#     c                    t        | j                        }| d|dk7  rdndz   }t        | j                        }| d|dk7  rdndz   }| d| d| j                   dS )Nz line   s z headingz, z cols)lenr   r   r   )r   line_lenline_strheading_lenheading_strs        r   _reprInternalzBrailleText._reprInternal5   sq    t}}%Zu%A2F$**+$X.9I#rR2k]"T__,=UCCr   c                    | j                   S N)r   r   s    r   r   zBrailleText.showHand=   s    ~~r   c                R    |dk(  rd| _         y |dk(  rd| _        y |t        d      y )NrightTleftzIllegal hand sign request.)r   r   BrailleTextException)r   newHands     r   r   zBrailleText.showHandA   s9    g#'D "&D &'CDD !r   c                d   | j                   j                  dk7  r| j                          t        | j                        dz
  }|}| j                   }|j                         D ]1  }d|_        |j                  |d       | j                          |dz  }3 | j                  j                  ||f       y)uW  
        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   
addHeadingzBrailleText.addHeadingJ   s    > ((A-'!+

!--"--/K+/(%%kE%B!OJ	 0
 	Z 89r   c                `    |j                  t        d         D ]  }| j                  |        y)z
        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   addLongExpressionzBrailleText.addLongExpressionv   s*    
 $>>''*:;K&&{3 <r   c                    | j                          | j                  s| j                  r/| j                  |       | j                  j                  |d       y| j                  j                  d|       y)u  
        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
        ⠨⠜⠄⠹⠹⠹

        Fr,      N)r   r   r   !optionalAddKeyboardSymbolsAndDotsr   r1   insert)r   brailleNoteGroupings     r   addToNewLinezBrailleText.addToNewLine~   sa    4 	4#6#6223FG##$7%#H##A':;r   c                    | j                   j                  ||      r| j                   j                  ||       y| j                          | j                   j	                  d|       y)u1  
        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
        r,   rA   N)r   	canAppendr1   r   rC   )r   r>   r-   s      r   r<   z!BrailleText.appendOrInsertCurrent   sX    " %%kH%E##K(#C##A{3r   c                   | j                  |      }	 | j                  j                  ||       d| j                  _
        y # t        $ r | j	                          | j
                  s| j                  r| j
                  r$| j                  j                  dt        d          n/| j                  r#| j                  j                  dt        d          t        |d         D ]  }| j                  j                  |d       ! | j                  j                  |d       n| j                  j                  d|       Y w xY w)Nr,   rA   rh_keyboardlh_keyboardr   FT)rB   r   r1   r)   r   r   r   rC   r;   r   containsNoteGrouping)r   inaccordr-   dots       r   addInaccordzBrailleText.addInaccord   s   99(C	5##Hx#@ 15- $ 	5##t':':''$$++Aw}/EF(($$++Aw}/EF$Xa[1C$$++C%+@ 2  ''5'A  ''84	5s   A C:E ?E c                    t        |t              rt        |      }| j                  j                  dk7  r| j                          | j                  j                  |d       y)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   Fr,   N)
isinstanceintr   r   r.   r   r1   )r   measureNumbers     r   addMeasureNumberzBrailleText.addMeasureNumber   sQ    0 mS)+M:M((A->r   c                   d}| j                   j                  s| j                  s| j                  r| j                   j                  dk(  rd}| j                  r%| j                   j                  t        d   |       n0| j                  r$| j                   j                  t        d   |       |r0t        |d         D ]  }| j                   j                  |d       ! d}| j                   j                  dk(  rd}|S )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   FrI   r,   rJ   )r   rK   r   r   r.   r1   r;   r   )r   noteGroupingr-   rM   s       r   rB   z-BrailleText.optionalAddKeyboardSymbolsAndDots   s       55))T-@-@,,1 ##  ''(>'R$$  ''(>'R$\!_5C$$++C%+@ 6H((A-Hr   c                d    d}| j                   j                  dk(  rd}| j                  ||       y)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   Fr,   N)r   r.   r<   )r   
signaturesr-   s      r   addSignatureszBrailleText.addSignatures  s5     ((A-H"":"Ar   c                    t        | j                        | _        | j                  j	                  | j                         y)a  
        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   r1   r%   s    r   r   zBrailleText.makeNewLine  s-     +4??;T--.r   c                    | j                   D ]  \  }}d}t        |t        | j                              D ]B  }| j                  |   j                  r n'| j                  |   j
                  }t        ||      }D t        ||      D ]u  }| j                  |   }t        |      }|j                  t        d         }|t        |      kD  sD|j                  |t        d         }|j                  d|       ||_        w  y)u'  
        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   r9   N)r   ranger   r   r0   r.   maxstrstripr;   centerrC   )	r   r3   r4   maxLineLengthir   jbrailleTextLinelineStrToCenters	            r   recenterHeadingszBrailleText.recenterHeadings(  s    H )-(8(8$ZM:s4=='9:==#--!]]1-::
 #M: >	 ;
 :z2"&--"2"%o"6"1"7"78H"I 3#77&5&<&<]GT[L\&]O#**1o>3@O0 3 )9r   c                    | j                          dj                  | j                  D cg c]  }t        |       c}      S c c}w )N
)rf   joinr   r^   )r   lines     r   __str__zBrailleText.__str__\  s7    yy>#d)>??>s   A)(   Nreturnr^   Tr$   )__name__
__module____qualname____doc__r   r"   propertyr   setterr7   r?   rE   r<   rN   rS   rB   rX   r   rf   rk    r   r   r
   r
      s}    "!D   __E E*:X4<B4.5$?>6B*/"2Ah@r   r
   c                  0     e Zd ZdZd fd	Zd Zd Z xZS )BrailleKeyboardzD
    A subclass of BrailleText that handles both hands at once.
    c                P    t         |   |       d | _        d | _        d| _        y )N)r   r   )superr   rightHandLineleftHandLinehighestMeasureNumberLength)r   r   	__class__s     r   r   zBrailleKeyboard.__init__f  s+    J/! *+'r   c                V   | j                   j                  dk(  r| j                   | _        n?t        | j                        | _        | j
                  j                  | j                         t        | j                        | _        | j
                  j                  | j                         y )Nr   )r   r.   r{   rZ   r   r   r1   r|   r%   s    r   makeNewLineszBrailleKeyboard.makeNewLiness  sv    ((A-!%!1!1D!0!ADMM  !3!34+DOO<T../r   c                x   | j                   | j                  | j                          | j                   j                  dk(  rW| j                   j	                  | j
                  t        |      z
  |       | j                   j                  | j                  _        d}| j                   j                  du rd}| j                   j                  t        d   d       | j                  j                  t        d   d       |r0t        |d         D ]  }| j                   j                  |d       ! |r0t        |d         D ]  }| j                  j                  |d       ! | j                   j                  ||      r| j                  j                  ||      r|r| j                  j                  ||       |r| j                   j                  ||       | j                   j                  | j                  j                  kD  r'| j                   j                  | j                  _        n| j                  j                  | j                   _        n| j                          | j                   j	                  | j
                  t        |      z
  |       | j                   j                  | j                  _        | j                   j                  t        d   d       | j                  j                  t        d   d       |rMt        |d         D ]  }| j                   j                  |d       ! | j                   j                  |d       |rMt        |d         D ]  }| j                  j                  |d       ! | j                  j                  |d       | j                   j                  | j                  j                  kD  r&| j                   j                  | j                  _        n%| j                  j                  | j                   _        d| j                   _        d| j                  _        y )Nr   TFrI   r,   rJ   )r{   r|   r   r.   rC   r}   r   rK   r1   r;   r   rG   )r   rR   noteGroupingRnoteGroupingLr-   rM   s         r   addNoteGroupingsz BrailleKeyboard.addNoteGroupings}  s   %$*;*;*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   rl   )rp   rq   rr   rs   r   r   r   __classcell__)r~   s   @r   rx   rx   a  s    ,016r   rx   c                  J    e Zd ZdZdddZddZddZd ZddZd Z	d Z
d	 Zy
)rZ   uT  
    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                    d| _         d| _        || _        | j                  t        d   gz  | _        d| _        d| _        y )NFr9   r   )r0   rK   r   r;   allCharsr.   highestUsedLocation)r   r   s     r   r   zBrailleTextLine.__init__  s@    $)!$77+;*<<#$ r   c                J    t        dj                  | j                              S )Nr   )reprri   r   r%   s    r   r"   zBrailleTextLine._reprInternal  s    BHHT]]+,,r   c                J   | j                  ||      st        d      |r5t        d   | j                  | j                  <   | xj                  dz  c_        t        |      D ]0  }|| j                  | j                  <   | xj                  dz  c_        2 | j                  | _        y)u  
        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.r9   r   N)rG   r)   r;   r   r.   listr   )r   textr-   chars       r   r1   zBrailleTextLine.append  s    D ~~dH-&'WXX/6w/?DMM$++,"JD/3DMM$++,"  $(#4#4 r   c                    | j                  ||      st        d      t        ||      D ]  \  }}|| j                  |<    |t	        |      z   | _        t        | j                  | j
                        | _        y)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   r.   r]   r   )r   r.   r   rb   r   s        r   rC   zBrailleTextLine.insert  sn    ` ~~lD1&'WXX \:GAt#DMM! ;(3t94#&t'?'?ARAR#S r   c                    t        | j                  | j                        }|rdnd}|t        |      z   |z   | j                  kD  ryy)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)r]   r   r.   r   r   )r   r   r-   searchLocationaddSpaceAmounts        r   rG   zBrailleTextLine.canAppend;  sE    8 T55t7H7HI&ASY&74??Jr   c                <    |t        |      z   | j                  kD  ryy)a  
        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   r.   r   s      r   r   zBrailleTextLine.canInsert^  s     #d)#doo5r   c                    | j                   dz
  }|dk  ry| j                  |   }|t        d   k(  r,t        d   | j                  |<   | xj                   dz  c_         yy)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_hyphenr9   )r.   r   r;   )r   prevLocprevChars      r   lastHyphenToSpacez!BrailleTextLine.lastHyphenToSpacen  sb    4 ##a'Q;==)w~..%,W%5DMM'"" /r   c                R    dj                  | j                  d| j                         S )Nr   r   )ri   r   r.   r%   s    r   rk   zBrailleTextLine.__str__  s"    wwt}}Qt'8'89::r   Nr   )r   rQ   rm   ro   )rp   rq   rr   rs   r   r"   r1   rC   rG   r   r   rk   rv   r   r   rZ   rZ     s4    6%-*5X5Tn!F  #D;r   rZ   c                      e Zd Zy)r)   Nrp   rq   rr   rv   r   r   r)   r)         r   r)   c                      e Zd Zy)TestNr   rv   r   r   r   r     r   r   r   __main__)
__future__r   unittestmusic21r   r   music21.brailler   music21.braille.basicr   r   r;   ProtoM21Objectr
   rx   rZ   Music21Exceptionr)   TestCaser   rp   mainTestrv   r   r   <module>r      s    #     " <
..H@'(( H@V
M6k M6``;g,, `;J	<88 		8 	 zGT r   