# -*- coding: ISO-8859-1 -*-
""" capellaScript -- Bernd Jungmann
>>> Balken über Pausen hinweg fortsetzen

In einer Stimme markierte Noten werden |
über Pausen hinweg mit Balken verbunden |
nach der Methode, die in der Capella-Hilfe |
im Kapitel "12.2 Grafikobjekte an (unsichtbaren)|
Noten verankern" beschrieben ist.|
|
Voraussetzung:|
- Alle markierten Noten und Pausen sind |
  gleichlang|
- Anfang und Ende der Markierung ist |
  jeweils eine Note (bzw. Akkord)|
|
Version 3: Bernd Jungmann 2.2.05|
<<<
Version 3: Bernd Jungmann 2.2.05|
- unsichtbar gemachte Noten jetzt auch unhörbar|
Version 2: Bernd Jungmann 22.1.05
- jetzt auch Mehrfachpausen überbrückt
- letzte Note im Umwandelbereich nicht mehr mit beam-force Attribut
Version 1: Bernd Jungmann 10.1.05|
"""
from caplib.capDOM import ScoreChange
import tempfile
def latin1(u):
    return u.encode('Latin-1')

class MarkedScoreChange (ScoreChange):
    def __init__(self, inputFile, outputFile, cursor1, cursor2):
        self.sysI1, self.staffI1, self.voiceI1, self.objI1 = cursor1
        self.sysI2,self.staffI2,self.voiceI2,self.objI2 = cursor2
        self.sysCurrent = 0
        self.staffCurrent = 0
        self.voiceCurrent = 0
        self.objCurrent = 0
        self.lastchord = 0
#        messageBox("__init__: objI1, objI2", str(self.objI1)+', '+str(self.objI2))
        ScoreChange.__init__(self, inputFile, outputFile)
    def copyElement(self, el):
        ScoreChange.copyElement(self, el)
        # Positionsmerker aktualisieren
        if el.tagName == 'system':
            self.sysCurrent = self.sysCurrent + 1
            self.staffCurrent = 0
            self.voiceCurrent = 0
            self.objCurrent = 0
        if el.tagName == 'staff':
            self.staffCurrent = self.staffCurrent + 1
            self.voiceCurrent = 0
            self.objCurrent = 0
        if el.tagName == 'voice':
            self.voiceCurrent = self.voiceCurrent + 1
            self.objCurrent = 0
        if el.tagName in ['clefSign','keySign','timeSign','chord','rest','ExplicitBarline']:
            self.objCurrent = self.objCurrent + 1
    def isInMarkedArea(self):
        if self.sysI1 <= self.sysCurrent and self.sysCurrent < self.sysI2:
            return True
        if self.sysI1 == self.sysCurrent and self.sysCurrent == self.sysI2 \
           and self.staffI1 == self.staffCurrent \
           and self.voiceI1 == self.voiceCurrent \
           and self.objI1 <= self.objCurrent and self.objCurrent < self.objI2:
            return True
        return False

# capella3.ttf-Code für die Grafik-Pausen
def getRestCode(rat):
    if str(rat) == "1/8":
        return 'L'
    if str(rat) == "1/16":
        return 'M'
    if str(rat) == "1/32":
        return 'N'
    return 'O'

class BalkenScoreChange (MarkedScoreChange):
    def changeElement(self, el):
        if self.isInMarkedArea():
            if el.tagName == 'chord' or el.tagName == 'rest':
                # jetzt sind wir in der Stimme, in der die Markierung ist.
                # Wir wissen bereits: Wir können hier alle Pausen ersetzen
                # durch Akkorde gleicher Spieldauer
#                messageBox("Jetzt ändern",el.tagName)
                if el.tagName == 'chord':
                    # wir merken uns den Akkord, um ihn unsichtbar an die Stelle der nächsten Pause zu setzen
                    if self.lastchord:
                        self.lastchord.unlink()
                    self.lastchord = el.cloneNode(True)
                    if self.objCurrent < self.objI2-1:
                        # Außer beim letzten: Sicherstellen, dass der Balken durchgezogen ist
                        oldbeams = el.getElementsByTagName('beam')
                        if oldbeams:
                            beam = oldbeams[0]
                        else:
                            beam = self.doc.createElement('beam')
                            el.appendChild(beam)
                        beam.setAttribute('group','force')
                elif el.tagName == 'rest':
                    # wir setzen anstelle der Pause den letzen Akkord
                    duration = el.getElementsByTagName('duration')[0]
                    rDur = duration.getAttribute('base')
                    # die Pause könnte eine verticalPos haben. Ggfls. wegnehmen
                    verticalPos = el.getElementsByTagName('verticalPos')
                    if verticalPos:
                        el.removeChild(verticalPos[0])
                        verticalPos[0].unlink()
                    # Pause zu Akkord machen (da können ggfls. drangehängte drawObjects dranbleiben)
                    el.tagName = 'chord'
                    heads = self.lastchord.getElementsByTagName('heads')[0]
                    newheads = el.appendChild(heads.cloneNode(True))
                    headlist = newheads.getElementsByTagName('head')
                    for head in headlist:
                        head.setAttribute('silent','true')
                    # Drangehängten Akkord unsichtbar machen
                    olddisplays = el.getElementsByTagName('display')
                    if olddisplays:
                        display = olddisplays[0]
                    else:
                        display = self.doc.createElement('display')
                        el.appendChild(display)
                    display.setAttribute('invisible','true')
                    # Sicherstellen, dass der Balken durchgezogen ist
                    beam = self.doc.createElement('beam')
                    beam.setAttribute('group','force')
                    el.appendChild(beam)
                    # Pause als DrawObject erzeugen
                    font = self.doc.createElement('font')
                    font.setAttribute('face','capella3')
                    font.setAttribute('height','18')
                    font.setAttribute('charSet','2')
                    font.setAttribute('pitchAndFamily','2')
                    text = self.doc.createElement('text')
                    text.setAttribute('x','0')
                    text.setAttribute('y','0')
                    text.appendChild(font)
                    string = self.doc.createTextNode(getRestCode(rDur))
                    content = self.doc.createElement('content')
                    content.appendChild(string)
                    text.appendChild(content)
                    drawObj = self.doc.createElement('drawObj')
                    drawObj.appendChild(text)
                    # Vorm Anhängen feststellen, ob's schon drawObjects gibt
                    olddrawObjects = el.getElementsByTagName('drawObjects')
                    if(olddrawObjects):
                        drawObjects = olddrawObjects[0]
                    else:
                        drawObjects = self.doc.createElement('drawObjects')
                        el.appendChild(drawObjects)
                    drawObjects.appendChild(drawObj)
 
def BalkenVerlaengern(score,(sysind, staffind, voiceind, objind),(sysind2, staffind2, voiceind2, objind2)):
    if sysind == sysind2 and staffind == staffind2 and voiceind == voiceind2:
        sys = score.system(sysind)
        staff = sys.staff(staffind)
        voice = staff.voice(voiceind)
        no = voice.nNoteObjs()
        if objind <= no and objind2 <= no:
            # man kann auch rückwärts markieren. Wozu eigentlich??
            if objind > objind2:
                obji = objind
                objind = objind2
                objind2 = obji
            # wir untersuchen, ob gleichlange Balkennoten mit Pausen
            # dazwischen vorliegen
            firstobj = voice.noteObj(objind)
            duration = firstobj.duration()
            maxduration = Rational("1/8")
            if firstobj.isChord() and duration <= maxduration:
                ind = objind + 1
                nPausen = 0
                while ind < objind2:
                    obj = voice.noteObj(ind)
                    if duration == obj.duration():
                        if obj.isChord():pass
                        elif obj.isRest():
                            nPausen = nPausen + 1
                        else:
                            break
                        ind = ind + 1
                    else:
                        break
                if ind == objind2 and nPausen > 0 and obj.isChord():
                    # wir sind sicher, dass erstes und letztes Objekt Akkorde sind
                    # und dass dazwischen höchstens Pausen gleicher Länge liegen.
                    if 2 == messageBox("Balken verlängern", str(nPausen) + " " + str(duration) + "-Pausen mit Balken überbrücken?", 1,2):
                        score.registerUndo("Balken über Pausen")
                        tempFile1 = tempfile.mktemp('.capx')
                        tempFile2 = tempfile.mktemp('.capx')
#                        messageBox("BalkenVerlaengern - jetzt ScoreChange rufen",str(tempFile1))
                        score.write(tempFile1)
                        BalkenScoreChange(tempFile1,tempFile2,(sysind, staffind, voiceind, objind),\
                                          (sysind, staffind, voiceind, objind2))
#                        messageBox("BalkenVerlaengern - jetzt modifizierte Partitur laden",str(tempFile2))
                        score.read(tempFile2)
                        os.remove(tempFile1)
                        os.remove(tempFile2)
                else:
                    messageBox("BalkenVerlaengern - keine Aktion!","Der markierte Bereich darf nur Noten und Pausen gleicher Länge enthalten \n\
und muß mit einer Note aufhören und mindestens eine Pause enthalten")
            else:
                messageBox("BalkenVerlaengern - keine Aktion!","Der markierte Bereich muss mit einer Note <= 1/8 anfangen")
        else:
            messageBox("Klappt nicht:","objind "+str(objind)+"objind2 "+str(objind2)+" no "+str(no))
    else:
        messageBox("BalkenVerlaengern - keine Aktion!","Markierung muss in einer einzigen Zeile und Stimme sein")
            

# Hauptprogramm
as = activeScore()
if as:
    sel = curSelection()
    start = sel[0]
    stop = sel[1]
    BalkenVerlaengern(as, start, stop)

