# -*- 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.
    
<<<

History:  22.12.03 - Erste Ausgabe
          14.01.04 - Textbreite wird berücksichtigt
          20.02.04 - Text und Ramen werden gruppiert
"""
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('Schriften', width = 18)
    vboxSchriften = VBox([labelSchriften, labelLeerzeile, cboxSchriften])

    cboxSchriftschnitt = ComboBox(['normal',
                                 'kursiv',
                                 'fett',
                                 'fett und kursiv'], value = dlgSet.schnitt, width = 18)
    labelSchriftschnitt = Label('Schriftschnitt', width = 18)
    vboxSchriftschnitt  = VBox([labelSchriftschnitt, labelLeerzeile, cboxSchriftschnitt])

    editSchriftgrad   = Edit(str(dlgSet.fontsize))
    labelSchriftgrad  = Label('Schriftgrad', width = 18)
    vboxSchriftgrad   = VBox([labelSchriftgrad, labelLeerzeile, editSchriftgrad])

    hboxSchrift       = HBox([vboxSchriften, vboxSchriftschnitt, vboxSchriftgrad], text='Schrift')

    # **** Aussehen ****    
    radioUmrandung = Radio(['ohne',
                            'Rechteck',
                            'Kreis / Ellipse'], value = dlgSet.shape)
    labelUmrandung = Label('Umrandung', width = 18)
    vboxUmrandung  = VBox([labelUmrandung, labelLeerzeile, radioUmrandung])

    radioAusrichtung = Radio(['linksbündig',
                              'zentriert',
                              'rechtsbündig'], value = dlgSet.align)
    labelAusrichtung = Label('Ausrichtung', width = 18)
    vboxAusrichtung  = VBox([labelAusrichtung, labelLeerzeile, radioAusrichtung])


    editBreite     = Edit(str(dlgSet.sizeX))
    labelBreite    = Label('    Breite')
    hboxBreite     = HBox([editBreite, labelBreite])
    editHoehe      = Edit(str(dlgSet.sizeY))
    labelHoehe      = Label('    Höhe')
    hboxHoehe     = HBox([editHoehe,labelHoehe])
    
    labelMasse       = Label('Masse')
    vboxMasse        = VBox([labelMasse, labelLeerzeile, hboxBreite, hboxHoehe])

    hboxAussehen    = HBox([vboxUmrandung, vboxAusrichtung, vboxMasse], text='Aussehen')

    # **** Position ****    

    editAbstand    = Edit(str(dlgSet.y))
    labelAbstand   = Label('Abstand von Mittellinie   ')
    hboxAbstand    = HBox([labelAbstand, editAbstand])

    cboxTaktstrich = CheckBox('', value=dlgSet.barline)
    labelTaktstrich= Label('      über Taktstrich   ')
    hboxTaktstrich = HBox([labelTaktstrich, cboxTaktstrich, labelLeerzeile])

    hboxPosition   = HBox([hboxAbstand, hboxTaktstrich], width = 30, text = 'Position')    

    cboxAction     = ComboBox(['Marke setzen', 'Felder zurücksetzen', 'Abbrechen'], value = 0, width = 18)
    labelAction    = Label('                    Aktion', width =14 )

    editText       = Edit(dlgSet.text)
    labelText      = Label('Inhalt', width = 10)
    hboxText       = HBox([labelText, editText, labelAction, cboxAction])

        
    
    
    vbox = VBox([hboxText,
                 hboxSchrift,
                 hboxAussehen,
                 hboxPosition])
    dlg = Dialog('Buchstaben und Zahlen', 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('Fehler', 'keine aktive Partitur')
        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
            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
            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
            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)

