# -*- coding: ISO-8859-1 -*-
""" capellaScript -- 20.02.2004 Paul Villiger
>>> Buchstaben und Ziffern

    Mit diesem Skript werden Buchstaben und Ziffern an der aktuellen Cursor Position gesetzt.
    
<<<
<?xml version="1.0" encoding="ISO-8859-1"?>
<info>
  <lang id="en">
    <title>Letters and figures</title>
    <descr>
      <p>With this script letters and figures will be placed at the actual cursor position.
</p>
    </descr>
  </lang>

  <lang id="de">
    <title>Buchstaben und Ziffern</title>
    <descr>
      <p>Mit diesem Skript werden Buchstaben und Ziffern an der aktuellen Cursor Position gesetzt.
</p>
    </descr>
  </lang>

  <lang id="nl">
    <title>Letters en cijfers</title>
    <descr>
      <p>Met dit script worden lettters en cijfers bij de actuele cursorpositie geplaatst.
</p>
    </descr>
  </lang>
</info>

History:  22.12.03 - Erste Ausgabe
          14.01.04 - Textbreite wird berücksichtigt
          20.02.04 - Text und Ramen werden gruppiert
          11.11.17 - Internationalisierung (en-de-nl)
                   - Zeilenanfang verbessert (cf. Voltenklammern ausrichten) (WW)   
"""

english = {
    'font'          :'Font',
    'standard'      :'Standard',
    'italic'        :'Italic',
    'bold'          :'Bold',
    'boldItalic'    :'Bold Italic',
    'style'         :'Style',
    'size'          :'Size',
    'charForm'      :'Character format',
    'without'       :'without',
    'rectangle'     :'rectangle',
    'ellipse'       :'circle / ellipse',
    'cadre'         :'Cadre',
    'left'          :'left',
    'centred'       :'centred',
    'right'         :'right',
    'align'         :'Alignment',
    'width'         :'    width',
    'height'        :'    height',
    'distText'      :'Extent', 
    'appearance'    :'Apearance',
    'distMidLine'   :'Distance from middle line   ',
    'overBarline'   :'      over barline   ',
    'pos'           :'Position',
    'mark'          :'Set mark',
    'fieldsUndo'    :'Put fields back',
    'cancel'        :'Cancel',
    'action'        :'                    Action',
    'contents'      :'Contents',
    'title'         :'Letters and figures',
    'error'         :'Error',
    'noScore'       :'no active score'
}
german = {
    'font'          :'Schriften',
    'standard'      :'Normal',
    'italic'        :'Kursiv',
    'bold'          :'Fett',
    'boldItalic'    :'Fett und Kursiv',
    'style'         :'Schriftschnitt',
    'size'          :'Schriftgrad',
    'charForm'      :'Schrift',
    'without'       :'ohne',
    'rectangle'     :'Rechteck',
    'ellipse'       :'Kreis / Ellipse',
    'cadre'         :'Umrandung',
    'left'          :'linksbündig',
    'centred'       :'zentriert',
    'right'         :'rechtsbündig',
    'align'         :'Ausrichtung',
    'width'         :'    Breite',
    'height'        :'    Höhe',
    'distText'      :'Masse', 
    'appearance'    :'Aussehen',
    'distMidLine'   :'Abstand von Mittellinie   ',
    'overBarline'   :'      über Taktstrich   ',
    'pos'           :'Position',
    'mark'          :'Marke setzen',
    'fieldsUndo'    :'Felder zurücksetzen',
    'cancel'        :'Abbrechen',
    'action'        :'                    Aktion',
    'contents'      :'Inhalt',
    'title'         :'Buchstaben und Zahlen',
    'error'         :'Fehler',
    'noScore'       :'keine aktive Partitur'
}
dutch = {
    'font'          :'Lettertype',
    'standard'      :'Standaard',
    'italic'        :'Cursief',
    'bold'          :'Vet',
    'boldItalic'    :'Vet Cursief',
    'style'         :'Tekenstijl',
    'size'          :'Grootte',
    'charForm'      :'Tekenopmaak',
    'without'       :'zonder',
    'rectangle'     :'rechthoek',
    'ellipse'       :'cirkel / ellips',
    'cadre'         :'Kader',
    'left'          :'links',
    'centred'       :'gecentreerd',
    'right'         :'rechts',
    'align'         :'Uitlijning',
    'width'         :'    breedte',
    'height'        :'    hoogte',
    'distText'      :'Omvang', 
    'appearance'    :'Uiterlijk',
    'distMidLine'   :'Afstand vanaf middelste lijn   ',
    'overBarline'   :'      boven maatstreep   ',
    'pos'           :'Positie',
    'mark'          :'Kenmerk plaatsen',
    'fieldsUndo'    :'Velden terugzetten',
    'cancel'        :'Annuleren',
    'action'        :'                    Actie',
    'contents'      :'Inhoud',
    'title'         :'Letters en cijfers',
    'error'         :'Fout',
    'noScore'       :'geen actieve partituur'
}

try:
    setStringTable(
        ("en", english),
        ("de", german),
        ("nl", dutch))
except:
    def tr(s):
        return german[s]

#----------------------------------------------------------

from xml.dom.minidom import NodeList

doc = [] # parentNode von score

fonts = (('Arial',34)   # face, pitchAndFamily
         ,('Tahoma',0)
         ,('Times New Roman',None))

class settings:
    def __init__(self):
        self.face      = 0    # fonts[0] 
        self.fontsize  = 10   # 
        self.schnitt   = 2    # bold = 2
        self.y         = 4    # Abstand von der Mittellinie
        self.sizeX, self.sizeY = 2.5, 2.5 
        self.align     = 1    # center=1, left = 0, right = 2
        self.shape     = 1    # Rechteck=1, ohne = 0, Ellipse = 2  
        self.text      = 'A'
        self.barline   = True # Marke über Taktstrich oder Note     
     
defaults = settings()
dlgSet = settings()

options = ScriptOptions() 
opt = options.get()


def getOptions():
    global dlgSet
    dlgSet.face     = eval(opt.get('face',str(dlgSet.face)))
    dlgSet.fontsize = eval(opt.get('fontsize',str(dlgSet.fontsize)))
    dlgSet.schnitt  = eval(opt.get('schnitt',str(dlgSet.schnitt)))
    dlgSet.y        = eval(opt.get('y',str(dlgSet.y)))
    dlgSet.sizeX    = eval(opt.get('sizeX',str(dlgSet.sizeX)))
    dlgSet.sizeY    = eval(opt.get('sizeY',str(dlgSet.sizeY)))
    dlgSet.align    = eval(opt.get('align',str(dlgSet.align)))
    dlgSet.shape    = eval(opt.get('shape',str(dlgSet.shape)))
    dlgSet.text     =      opt.get('text',dlgSet.text)
    dlgSet.barline  = eval(opt.get('barline',str(dlgSet.barline)))

def setOptions():
    global dlgSet
    opt.update(dict(face     = str(dlgSet.face),
                    fontsize = str(dlgSet.fontsize),
                    schnitt  = str(dlgSet.schnitt),
                    y        = str(dlgSet.y),
                    sizeX    = str(dlgSet.sizeX),
                    sizeY    = str(dlgSet.sizeY),
                    align    = str(dlgSet.align),
                    shape    = str(dlgSet.shape),
                    text     =     dlgSet.text,
                    barline  = str(dlgSet.barline)
                    ))
    options.set(opt)

def incrementText():
    global dlgSet
    tmp = dlgSet.text
    if dlgSet.text.isdigit():
        dlgSet.text = str(eval(dlgSet.text)+1)
    else:
        dlgSet.text = chr(ord(dlgSet.text[0])+1)
    setOptions()
    dlgSet.text = tmp

def scriptDialog():
    global dlgSet
    # **** Schrift ***
    labelLeerzeile=Label('', width = 20)
    
    cboxSchriften = ComboBox([fonts[i][0] for i in range(len(fonts))], value=dlgSet.face, width = 18)
    labelSchriften = Label(tr('font'), width = 18)
    vboxSchriften = VBox([labelSchriften, labelLeerzeile, cboxSchriften])

    cboxSchriftschnitt = ComboBox([tr('standard'),
                                 tr('italic'),
                                 tr('bold'),
                                 tr('boldItalic')], value = dlgSet.schnitt, width = 18)
    labelSchriftschnitt = Label(tr('style'), width = 18)
    vboxSchriftschnitt  = VBox([labelSchriftschnitt, labelLeerzeile, cboxSchriftschnitt])

    editSchriftgrad   = Edit(str(dlgSet.fontsize))
    labelSchriftgrad  = Label(tr('size'), width = 18)
    vboxSchriftgrad   = VBox([labelSchriftgrad, labelLeerzeile, editSchriftgrad])

    hboxSchrift       = HBox([vboxSchriften, vboxSchriftschnitt, vboxSchriftgrad], text=tr('charForm'))

    # **** Aussehen ****    
    radioUmrandung = Radio([tr('without'),
                            tr('rectangle'),
                            tr('ellipse')], value = dlgSet.shape)
    labelUmrandung = Label(tr('cadre'), width = 18)
    vboxUmrandung  = VBox([labelUmrandung, labelLeerzeile, radioUmrandung])

    radioAusrichtung = Radio([tr('left'),
                              tr('centred'),
                              tr('right')], value = dlgSet.align)
    labelAusrichtung = Label(tr('align'), width = 18)
    vboxAusrichtung  = VBox([labelAusrichtung, labelLeerzeile, radioAusrichtung])


    editBreite     = Edit(str(dlgSet.sizeX))
    labelBreite    = Label(tr('width'))
    hboxBreite     = HBox([editBreite, labelBreite])
    editHoehe      = Edit(str(dlgSet.sizeY))
    labelHoehe      = Label(tr('height'))
    hboxHoehe     = HBox([editHoehe,labelHoehe])
    
    labelMasse       = Label(tr('distText'))
    vboxMasse        = VBox([labelMasse, labelLeerzeile, hboxBreite, hboxHoehe])

    hboxAussehen    = HBox([vboxUmrandung, vboxAusrichtung, vboxMasse], text=tr('appearance'))

    # **** Position ****    

    editAbstand    = Edit(str(dlgSet.y))
    labelAbstand   = Label(tr('distMidLine'))
    hboxAbstand    = HBox([labelAbstand, editAbstand])

    cboxTaktstrich = CheckBox('', value=dlgSet.barline)
    labelTaktstrich= Label(tr('overBarline'))
    hboxTaktstrich = HBox([labelTaktstrich, cboxTaktstrich, labelLeerzeile])

    hboxPosition   = HBox([hboxAbstand, hboxTaktstrich], width = 30, text = tr('pos'))    

    cboxAction     = ComboBox([tr('mark'), tr('fieldsUndo'), tr('cancel')], value = 0, width = 18)
    labelAction    = Label(tr('action'), width =14 )

    editText       = Edit(dlgSet.text)
    labelText      = Label(tr('contents'), width = 10)
    hboxText       = HBox([labelText, editText, labelAction, cboxAction])

        
    
    
    vbox = VBox([hboxText,
                 hboxSchrift,
                 hboxAussehen,
                 hboxPosition])
    dlg = Dialog(tr('title'), vbox)
    if dlg.run():
        dlgSet.face      = cboxSchriften.value()
        dlgSet.fontsize  = eval(editSchriftgrad.value())
        dlgSet.schnitt   = cboxSchriftschnitt.value()
        dlgSet.y         = eval(editAbstand.value())
        dlgSet.sizeX     = eval(editBreite.value())
        dlgSet.sizeY       = eval(editHoehe.value())
        dlgSet.align     = radioAusrichtung.value()
        dlgSet. shape    = radioUmrandung.value()
        dlgSet.text      = editText.value()
        dlgSet.barline   = cboxTaktstrich.value()
        return cboxAction.value()
    else:
        return 2  # Abbrechen
                



def latin1_e(u):
    return u.encode('Latin-1')
def latin1_d(u):
    return u.decode('Latin-1')

def addElementNode(el,tagName):
    # add new Node to el if Node "tagName" does not exist
    # otherwise return the existing Node
    global doc
    childs = el.childNodes
    for n in range(childs.length):
        if childs[n].nodeType ==childs[n].ELEMENT_NODE and childs[n].tagName == tagName:
            return childs[n]
    newChild = doc.createElement(tagName)
    el.appendChild(newChild)
    return newChild
    
def addNewElementNode(el,tagName):
    # add new Node with tagName "tagName" to el 
    global doc
    newChild = doc.createElement(tagName)
    el.appendChild(newChild)
    return newChild

def getCursor():
    sel = curSelection()
    result = None
    if sel == 0:
        messageBox(tr('error'), tr('noScore'))
        return result
    #if sel[0] != sel[1]:
    #    messageBox('Fehler', 'Markierung ist nicht leer')
    #    return result
    result = sel[0]
    return result

def getElementObjects(objList):  # returns a List
    newList = NodeList()
    for n in range(objList.length):
        if objList[n].nodeType == objList[n].ELEMENT_NODE:
            newList.append(objList[n])
    return newList


def getPositions(sy,st,vo,ob):
    system = activeScore().system(sy)
    staff = system.staff(st)
    voice = staff.voice(vo)
    posBegin = voice.noteObj(ob).posX()

    # Barline Anfang suchen
    n = ob - 1
    barLineFound = False
    while n >= 0 and not barLineFound:
        obj = voice.noteObj(n)
        if obj.implBarline() != 0:
            posBLBegin = obj.implBarline().posX()
            barLineFound = True
        elif obj.subType() == NoteObj.KEY and n < 3:   # gilt fuer Tonart am Zeilenanfang
            # posBLBegin = voice.noteObj(n+1).posX()-2
            key = voice.noteObj(n+1).curKey()
            posBLBegin = voice.noteObj(n).posX() + abs(key) + 0.75 ##(ww)war +1.25
            barLineFound = True
        elif obj.subType() == NoteObj.KEY:
            posBLBegin = obj.posX()-0.5
            barLineFound = True
        elif obj.subType() == NoteObj.METER and n < 3:  # gilt fuer Takt am Zeilenanfang
            # posBLBegin = voice.noteObj(n+1).posX()-2
            posBLBegin = voice.noteObj(n).posX() + 2.5 ##(ww) war +3
            
            barLineFound = True
        elif obj.subType() == NoteObj.METER:
            posBLBegin = obj.posX()-0.5
            barLineFound = True
        elif obj.subType() == NoteObj.EXPL_BARLINE:
            posBLBegin = obj.posX()
            barLineFound = True
        elif obj.subType() == NoteObj.CLEF and n < 3: # gilt fuer Schluessel am Zeilenanfang
            # posBLBegin = voice.noteObj(n+1).posX()-2
            posBLBegin = voice.noteObj(n).posX() + 2 ##(ww) war +2.5
            
            barLineFound = True
        n = n - 1
        
    posNoteObject = posBegin
    posBarline = posBLBegin
    return (posNoteObject, posBarline)

def addRectagle(obj, dx, dy, sx, sy):
    # obj: drawObjects or group
    # sx, sy : width, height
    # dx, dy : center location
    drawObj = addNewElementNode(obj,'drawObj')
    rectangle = addNewElementNode(drawObj,'rectangle')
    rectangle.setAttribute('x1', str(-sx/2.0+dx))
    rectangle.setAttribute('x2', str(+sx/2.0+dx))
    rectangle.setAttribute('y1', str(-sy/2.0+dy))
    rectangle.setAttribute('y2', str(+sy/2.0+dy))
    
def addEllipse(obj, dx, dy, sx, sy):
    # obj: drawObjects or group
    # sx, sy : width, height
    # dx, dy : center location
    drawObj = addNewElementNode(obj,'drawObj')
    ellipse = addNewElementNode(drawObj,'ellipse')
    ellipse.setAttribute('x1', str(-sx/2.0+dx))
    ellipse.setAttribute('x2', str(+sx/2.0+dx))
    ellipse.setAttribute('y1', str(-sy/2.0+dy))
    ellipse.setAttribute('y2', str(+sy/2.0+dy))

def addText(obj, dx, dy, face, size, pitch, text, schnitt ):
    # obj: drawObjects or group
    drawObj = addNewElementNode(obj,'drawObj')

    textOb = addNewElementNode(drawObj,'text')
    textOb.setAttribute('align','center')
    textOb.setAttribute('x',str(dx))
    textOb.setAttribute('y',str(dy+size/12.0))
    
    fontOb = addNewElementNode(textOb,'font')
    fontOb.setAttribute('face',face)
    fontOb.setAttribute('height',str(size))
    if pitch<>None:
        fontOb.setAttribute('pitchAndFamily',str(pitch))
    if schnitt in [1,3]:  # italic
        fontOb.setAttribute('italic','true')
    if schnitt in [2,3]:  # bold
        fontOb.setAttribute('weight','700')

    content = addNewElementNode(textOb,'content')
    textNode = doc.createTextNode(text)
    content.appendChild(textNode)            
    
def getTextSize(cont,textFont):
    text = dict(type = 'text', content = cont, font = textFont)
    res = (10,10)           # Default, wenn score keine Note enthaelt
    for noteObject in activeScore().noteObjs():
        if noteObject.isRest() or noteObject.isChord():
            res = noteObject.textSize(text)
            break
    return res

                

def changeDoc(score):
    global fonts, dlgSet

    action = 1  # mindestens ein Durchlauf
    while action == 1:
        getOptions()
        action = scriptDialog()
        if action == 1:
            dlgSet = defaults            
        setOptions()
    if action == 2:   # Abbruch
        return
    if action == 0:
        incrementText()

    sel = getCursor()
    if sel == None:
        return
    else:
        sy,st,vo,ob = sel
        system = score.getElementsByTagName('system')[sel[0]]
        staff = system.getElementsByTagName('staff')[sel[1]]
        voice = staff.getElementsByTagName('voice')[sel[2]]
        noteObject = voice.getElementsByTagName('noteObjects')[0]
        objList = getElementObjects(noteObject.childNodes)
        if objList.length <= sel[3]:
            return
        obj = objList[sel[3]]
        if obj.tagName in  ['chord', 'rest']:
            posNoteObject, posBarLine = getPositions(sy,st,vo,ob)

            if dlgSet.shape == 1:   # Rechteck
                textFont = dict(face = fonts[dlgSet.face][0],
                                pitchAndFamily = fonts[dlgSet.face][1],
                                height = dlgSet.fontsize,
                                weight = [400,400,700,700][dlgSet.schnitt],
                                italic = dlgSet.schnitt in [1,3])
                tSizeX = getTextSize(dlgSet.text, textFont)[0]
                if tSizeX > dlgSet.sizeX - 0.6:   # Rand von 0.3 Einheiten
                    dlgSet.sizeX = tSizeX + 0.6

            if dlgSet.barline:
                dx = posBarLine-posNoteObject
            else:
                dx = 0.75
            if dlgSet.align == 0:            # align = links
                dx = dx + dlgSet.sizeX / 2.0
            if dlgSet.align == 2:            # align = rechts
                dx = dx - dlgSet.sizeX / 2.0
                
            if dlgSet.shape == 0:
                dy = - (dlgSet.y + dlgSet.fontsize / 12.0)
            else:
                dy = - (dlgSet.y + dlgSet.sizeY / 2.0)
            
            drawObjects = addElementNode(obj,'drawObjects')
            if dlgSet.shape == 0:
                obj = drawObjects
            else:
                drawObj = addNewElementNode(drawObjects,'drawObj')
                group = addNewElementNode(drawObj,'group')
                obj = group

            # drawObj Text einfuegen
            addText(obj, dx, dy,
                    fonts[dlgSet.face][0],
                    dlgSet.fontsize,
                    fonts[dlgSet.face][1],
                    dlgSet.text,
                    dlgSet.schnitt)   # bold

            if dlgSet.shape == 1:            # Rechteck
                # drawObj Rechteck  
                addRectagle(obj, dx, dy,
                            dlgSet.sizeX,
                            dlgSet.sizeY)

            if dlgSet.shape == 2:            # ellipse            
                # drawObj Ellipse
                addEllipse(obj, dx, dy,
                            dlgSet.sizeX,
                            dlgSet.sizeY)
            


# Hauptprogramm:

from caplib.capDOM import ScoreChange
import tempfile

class ScoreChange(ScoreChange):
    def changeScore(self, score):
        global doc
        doc = score.parentNode
        changeDoc(score)

if activeScore():
    activeScore().registerUndo("Buchstaben und Ziffern")
    tempInput = tempfile.mktemp('.capx')
    tempOutput = tempfile.mktemp('.capx')
    activeScore().write(tempInput)
    
    ScoreChange(tempInput, tempOutput)

    activeScore().read(tempOutput)
    os.remove(tempInput)
    os.remove(tempOutput)
