# -*- coding: ISO-8859-1 -*-
""" capellaScript -- (c) Paul Villiger
>>> Voltenklammern ausrichten
    
<<<
Programmtechnischer Hinweis:
    - Dieses Skript arbeitet sowohl mit der internen Schnittstelle sowie mit XML.
    - Mit XML wird die Position einer Voltenklammer innerhalb der Partitur gesucht
    - Mit der internen Schnittstelle werden die Noten- und Taktstrichpositionen ermittelt
      und die Abstaende berechnet
    - Mit XML werden die berechneten Abstaende in das Volta-Objekt eingetragen


History:  18.12.03 - Erste Ausgabe
          19.12.03 - Rechter Rand nach links verschoben, wenn nicht letzte Voltenklammer
                     dadurch ergibt sich ein Abstand zwischen zwei Klammern
          22.12.03 - Undo aktualisiert
          07.02.04 - Fehler bei Voltabeginn mit ganzer Note
                   - Absturz wenn noteRange über Zeilenende ?
          04.03.04 - messageBox Anzeig entfernt
          13.10.05 - Fehler bei Voltenklammern mit Takt oder Tonart
          26.11.05 - Absturz bei falschem Noterange am Zeilenende
          06.11.07 - Voltenklammern bei festen Taktstrichen korrigiert
          10.12.07 - Neue Behandlung Abstand Voltenklammern
          20.04.10 - Attribut prüfen vor getAttribute (capella 7)
          03.09.10 - Fehler bei Voltenklammer über ganzer Pause
                    
"""
from xml.dom.minidom import NodeList

def adjustVolta(sy,st,vo,ob,noteRange, noteObjects):
    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 and voice.noteObj(n-1).subType() in [NoteObj.CHORD, NoteObj.REST]:
            posBLBegin = obj.posX()-0.4
            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 and voice.noteObj(n-1).subType() in [NoteObj.CHORD, NoteObj.REST]:
            posBLBegin = obj.posX()-0.4
            barLineFound = True
        elif obj.subType() == NoteObj.EXPL_BARLINE:
            posBLBegin = obj.posX()
            barLineFound = True
            if noteObjects[n].tagName == 'barline':
                type = noteObjects[n].getAttribute('type')
                if type == 'end':
                    posBLBegin -= 0.4
                elif type == 'repEnd':
                    posBLBegin -= 0.4
                elif type == 'repBegin':
                    posBLBegin += 0.4
                elif type == 'repEndBegin':
                    posBLBegin += 0.0
                    
        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
        
    # Letzte Note unter Voltenklammer suchen
    n = ob
    count = 0
    posEnd = voice.noteObj(n).posX()
    while n < voice.nNoteObjs() and count <= noteRange:
        obj = voice.noteObj(n)
        if obj.subType() in [NoteObj.CHORD, NoteObj.REST]:
            posEnd = voice.noteObj(n).posX()
            count += 1
        n += 1

    n -= 1

    # Barline Ende suchen
    barLineFound = False
    while n < voice.nNoteObjs() and not barLineFound:

        obj = voice.noteObj(n)
        posBLEnd = obj.posX() + 4   # Wenn das Zeilenende keinen Taktstrich hat
                                    # Position ist nicht genau bestimmbar
        if obj.implBarline() != 0:
            posBLEnd = obj.implBarline().posX()
            barLineFound = True
        elif obj.subType() == NoteObj.KEY:
            posBLEnd = obj.posX()-0.4
            barLineFound = True
        elif obj.subType() == NoteObj.METER:
            posBLEnd = obj.posX()-0.4
            barLineFound = True
        elif obj.subType() == NoteObj.EXPL_BARLINE:
            posBLEnd = obj.posX()
            barLineFound = True
            if n < voice.nNoteObjs()-1 and noteObjects[n].tagName == 'barline':
                type = noteObjects[n].getAttribute('type')
                if type == 'end' :
                    posBLEnd -= 0.8
                elif type == 'double' :
                    posBLEnd -= 0.4
                elif type == 'repEnd' :
                    posBLEnd -= 0.8
                elif type == 'repBegin' :
                    posBLEnd += 0.0
                elif type == 'repEndBegin' :
                    posBLEnd -= 0.4

        elif obj.subType() == NoteObj.CLEF and n < 2:
            posBLEnd = obj.posX()
            barLineFound = True
        n = n + 1


    x1 = posBLBegin - posBegin
    x2 = posBLEnd - posEnd
    return (x1, x2, posBLBegin, posBLEnd)

def getNoteObjects(voice):  # returns a List
    noteObject = voice.getElementsByTagName('noteObjects')[0]
    objList = noteObject.childNodes
    newList = NodeList()
    for n in range(objList.length):
        if objList[n].nodeType == objList[n].ELEMENT_NODE:
            newList.append(objList[n])
    return newList

def changeDoc(score):
    systems = score.getElementsByTagName('system')
    for sy in range(systems.length):
        staves = systems[sy].getElementsByTagName('staff')
        for st in range(staves.length):
            vo = 0
            for voice in staves[st].getElementsByTagName('voice'):
                nVoltas = voice.getElementsByTagName('volta').length
                n = 0
                noteObjects = getNoteObjects(voice)
                nNoteObjects = noteObjects.length

                previousVolta = None
                absPosX2 = 0
                for ob in range(nNoteObjects):
                    voltas = noteObjects[ob].getElementsByTagName('volta')
                    for volta in voltas:
                        n = n + 1
                        noteRange = 0  # default
                        for basic in volta.parentNode.getElementsByTagName('basic'):
                            noteRange = 0
                            if basic.hasAttribute('noteRange'):
                                noteRange = eval(basic.getAttribute('noteRange'))
                            if ob + noteRange >= nNoteObjects:
                                # noteRange korrigieren wenn ueber Zeilenende
                                noteRange = nNoteObjects - ob - 1
                                basic.setAttribute('noteRange',str(noteRange))

                        x1,x2, absX1, absX2 = adjustVolta(sy, st, vo, ob, noteRange, noteObjects)

                        volta.setAttribute('x1', str(x1))
                        volta.setAttribute('x2', str(x2))

                        if previousVolta:
                            pVolta, pX1, pX2, pAbsX1, pAbsX2 = previousVolta
                            if abs(absX1 - pAbsX2) < 0.2:
                                pVolta.setAttribute('x2', str(pX2 - 0.4))
                        previousVolta = (volta, x1, x2, absX1, absX2)

                vo = vo + 1
                        

# Hauptprogramm:

from caplib.capDOM import ScoreChange
import tempfile

class ScoreChange(ScoreChange):
    def changeScore(self, score):
        changeDoc(score)

if activeScore():
    activeScore().registerUndo("Voltenklammern ausrichten")
    tempInput = tempfile.mktemp('.capx')
    tempOutput = tempfile.mktemp('.capx')
    activeScore().write(tempInput)
    
    ScoreChange(tempInput, tempOutput)

    activeScore().read(tempOutput)
    os.remove(tempInput)
    os.remove(tempOutput)

