# -*- coding: ISO-8859-1 -*-
""" capellaScript -- (c) Paul Villiger
>>> 
    Akkorde oktavieren

    Dieses Skript fügt zu einem Akkord eine Oktave nach oben oder unten hinzu.
<<<

History:  20.12.07 - Erste Ausgabe
          03.01.08 - Alterierung, Oktavrichtung korrigiert
          17.03.08 - Textkorrektur
          09.06.10 - Auswahl Markierung
          09.04.11 - Vorzeichenattribute werden in Oktave übernommen

Funktionen:

    Akkorde oktavieren ...
    ... in der ganzen Partitur
    ... im aktuellen System
    ... in aktueller Notenzeile
    ... in aktueller Stimme
    ... in aktueller Stimme in der ganzen Partitur
    ... Markierung in aktueller Stimme
"""

german = ("de", {
    'radioWholeScore'    :   '... in der ganzen Partitur',
    'radioCurrentSystem' :   '... im aktuellen System',
    'radioCurrentStaff'  :   '... in aktueller Notenzeile',
    'radioCurrentVoice'  :   '... in aktueller Stimme',
    'radioCurrenStaffWholeScore'  : '... in aktueller Notenzeile, ganze Partitur',
    'radioCurrentSelection'  :  '... Markierung in aktueller Stimme',
    'directionUp'       :   'nach oben oktavieren',
    'dialogHeader'      :   'Akkorde oktavieren',
    'dialogText'        :   'Bereichswahl',
    'error'             :   'Fehler',
    'noActiveScore'     :   'keine aktive Partitur',
    'regUndo'           :   'Akkorde_oktavieren'
    })

try:
    exec('from %s import translations' % ( translationModule() ))
    translations.append(german)
    setLanguages(translations)
except:
    def tr(s):
        return german[1].get(s, "???")

#-------------------------------------------------------------------

import new
from xml.dom.minidom import NodeList, Node, Element
from xml.dom.minidom import parseString, NodeList, Node, Element
from string import find, replace, strip

doc = parseString('<score/>')

def gotoChild(self, name, new=False):
    newEl = None
    if new:
        pass
    else:
        for child in self.childNodes:
            if child.nodeType == child.ELEMENT_NODE and child.tagName == name:
                newEl = child
                break
    if newEl == None:
        newEl = doc.createElement(name)
        self.appendChild(newEl)
    return newEl
Node.gotoChild = new.instancemethod(gotoChild,None,Node)

# Gibt alle Elemente auf der nächsten Ebene zurück
def getChildElements(self, filter = ''):
    childElements = NodeList()
    for child in self.childNodes:
        if child.nodeType == child.ELEMENT_NODE:
            if filter == '':
                childElements.append(child)
            elif child.tagName == filter:
                childElements.append(child)
    return childElements
Node.getChildElements = new.instancemethod(getChildElements,None,Node)

def generateChordOctave(voice, dirUp, begin=0, end=9999):
    noteObjects = voice.gotoChild('noteObjects')
    childs = noteObjects.getChildElements()
    for i in range(len(childs)):
        if begin <= i < end:
            if childs[i].tagName == 'chord':
                chord = childs[i]
            else:
                continue
        else:
            continue

        for head in chord.getElementsByTagName('head'):
            pitch = head.getAttribute('pitch')
            newStep = '0'
            tieBegin = tieEnd = False
            display = ''
            for alter in head.getElementsByTagName('alter'):
                newStep = alter.getAttribute('step')
                display = alter.getAttribute('display')
            for tie in head.getElementsByTagName('tie'):
                tieBegin = tie.getAttribute('begin') == 'true'
                tieEnd = tie.getAttribute('end') == 'true'
            break
        
        if dirUp:
            newPitch = pitch[0] + chr(int(pitch[1]) + 1 + 48)
        else:
            newPitch = pitch[0] + chr(int(pitch[1]) - 1 + 48)
        duplicate = False
        for head in chord.getElementsByTagName('head'):
            pitch = head.getAttribute('pitch')
            if pitch <> newPitch:
                continue
            step = '0'
            for alter in head.getElementsByTagName('alter'):
                step = alter.getAttribute('step')
            if step <> newStep:
                continue
            duplicate = True
            break
        
        if not duplicate:
            heads = chord.gotoChild('heads')
            head = heads.gotoChild('head', True)
            head.setAttribute('pitch', newPitch)
            if newStep <> '0':
                alter = head.gotoChild('alter')
                alter.setAttribute('step', newStep)
            if display:
                alter = head.gotoChild('alter')
                alter.setAttribute('display', display)
            if tieBegin:
                tie = head.gotoChild('tie')
                tie.setAttribute('begin','true')
            if tieEnd:
                tie = head.gotoChild('tie')
                tie.setAttribute('end','true')
        
def getCursor():
    sel = curSelection()
    result = None
    if sel == 0:
        messageBox('Fehler', 'keine aktive Partitur')
        return result
    sy = min(sel[0][0],sel[1][0])
    st = min(sel[0][1],sel[1][1])
    vo = min(sel[0][2],sel[1][2])
    ob1=  min(sel[0][3],sel[1][3])
    ob2=  max(sel[0][3],sel[1][3])
    
    return sy, st, vo, ob1, ob2

def changeDoc(score):
    sy1, st1, vo1, ob1 , ob2 = getCursor()
    directionUp = dialogResult['directionUp'] <> '0' 

    if dialogResult['selection'] == '0':          # in ganzer Partitur
        for system in score.getElementsByTagName('system'):
            for voice in system.getElementsByTagName('voice'):
                generateChordOctave(voice, directionUp)
                                        
    elif dialogResult['selection'] == '1':        # in aktuellem System
        count = 0
        for system in score.getElementsByTagName('system'):
            if count == sy1:
                for voice in system.getElementsByTagName('voice'):
                    generateChordOctave(voice, directionUp)
            count += 1

    elif dialogResult['selection'] == '2':        # in aktueller Notenzeile
        sy = st = 0
        for system in score.getElementsByTagName('system'):
            if sy == sy1:
                for staff in system.getElementsByTagName('staff'):
                    if st == st1:
                        for voice in staff.getElementsByTagName('voice'):
                            generateChordOctave(voice, directionUp)
                    st += 1
            sy += 1
            
    elif dialogResult['selection'] == '3':        # in aktueller Stimme
        sy = st = vo = 0
        for system in score.getElementsByTagName('system'):
            if sy == sy1:
                for staff in system.getElementsByTagName('staff'):
                    if st == st1:
                        for voice in staff.getElementsByTagName('voice'):
                            if vo == vo1:
                                generateChordOctave(voice, directionUp)
                            vo += 1
                    st += 1
            sy += 1
            
    elif dialogResult['selection'] == '4':        # in aktueller Notenzeile, ganze Partitur
        sy = st = 0
        staffLayout = ''
        for system in score.getElementsByTagName('system'):
            if sy == sy1:
                for staff in system.getElementsByTagName('staff'):
                    if st == st1:
                        staffLayout = staff.getAttribute('layout')
                        break
                    st += 1
            sy += 1

        for staff in score.getElementsByTagName('staff'):
            if staffLayout == staff.getAttribute('layout'):
                for voice in staff.getElementsByTagName('voice'):
                    generateChordOctave(voice, directionUp)

    elif dialogResult['selection'] == '5':        # Markierung in aktueller Stimme
        sy = st = vo = 0
        for system in score.getElementsByTagName('system'):
            if sy == sy1:
                for staff in system.getElementsByTagName('staff'):
                    if st == st1:
                        for voice in staff.getElementsByTagName('voice'):
                            if vo == vo1:
                                generateChordOctave(voice, directionUp, ob1, ob2)
                            vo += 1
                    st += 1
            sy += 1

def getDialog():
    options = ScriptOptions() 
    opt = options.get()

    
    selection = Radio([tr('radioWholeScore'),
                       tr('radioCurrentSystem'),
                       tr('radioCurrentStaff'),
                       tr('radioCurrentVoice'),
                       tr('radioCurrenStaffWholeScore'),
                       tr('radioCurrentSelection')
                       ], value = eval( opt.setdefault('selection', '0')))
    
    directionUp = CheckBox( tr('directionUp'), value = eval( opt.setdefault('directionUp', 'True') ))
    dlg = Dialog( tr('dialogHeader'),
                 VBox([selection,
                       Label(' '),
                       directionUp,
                       Label(' ')
                      ], text = tr('dialogText'))
                 )
    if dlg.run():
        result = {'selection':str(selection.value()),
                  'directionUp':str(directionUp.value())
                 }
        options.set(result)
                         
    else:
        result = {'selection':'-1',
                  'directionUp':'False'
                 }
    return result
        

# Hauptprogramm:

from caplib.capDOM import ScoreChange
import tempfile

class ScoreChange(ScoreChange):
    def changeScore(self, score):
        changeDoc(score)


if activeScore():
    dialogResult = getDialog()
    if dialogResult['selection'] <> '-1':
        activeScore().registerUndo( tr('regUndo') )

        tempInput = tempfile.mktemp('.capx')
        tempOutput = tempfile.mktemp('.capx')
        activeScore().write(tempInput)
        
        ScoreChange(tempInput, tempOutput)

        activeScore().read(tempOutput)
        os.remove(tempInput)
        os.remove(tempOutput)

