# -*- coding: ISO-8859-1 -*-
""" capellaScript -- ©Paul Villiger
>>> Lyric Handler

    Dieses Skript erlaubt Liedstrophen einzufügen, zu löschen, zu kopieren und zu vertauschen.

    Die Aktion wird wahlweise im markierten Bereich oder in der ganzen Partitur durchgeführt.
    
    Bemerkung: Sind mehrere Stimmen markiert, so gilt die Markierung für die ganze Notenzeile

<<<

History:  12.08.2011  - Erste Version

Rückmeldungen bitte an villpaul(at)bluewin.ch                     

"""

import xml.dom
import new, string
from xml.dom.minidom import parseString, NodeList, Node, Element

# START SETTINGS ################

class settings:
    def __init__(self):
        self.versA = 0
        self.versB = 0
        self.rangeSel = 0
        self.actionSelect = 0
     
defaults = settings()
dlgSet = settings()

options = ScriptOptions() 
opt = options.get()


def getOptions():
    global dlgSet
    dlgSet.versA          = eval(opt.get('LyricHandler_versA',           str(dlgSet.versA)))
    dlgSet.versB          = eval(opt.get('LyricHandler_versB',           str(dlgSet.versB)))
    dlgSet.rangeSel       = eval(opt.get('LyricHandler_rangeSel',        str(dlgSet.rangeSel)))
    dlgSet.actionSelect   = eval(opt.get('LyricHandler_actionSelect',    str(dlgSet.actionSelect)))

def setOptions():
    global dlgSet
    opt.update(dict(LyricHandler_versA             = str(dlgSet.versA),
                    LyricHandler_versB             = str(dlgSet.versB),
                    LyricHandler_rangeSel          = str(dlgSet.rangeSel),
                    LyricHandler_actionSelect      = str(dlgSet.actionSelect)
                    ))
    options.set(opt)

# END SETTINGS ################

doc = parseString('<score/>')

def getCursorRange():
    sel = curSelection()
    (sy1,st1,vo1,ob1),(sy2,st2,vo2,ob2) = sel
    sel = (min(sy1,sy1), min(st1,st2),min(vo1,vo2),min(ob1,ob2)),(max(sy1,sy1), max(st1,st2),max(vo1,vo2),max(ob1,ob2))
    (sy1,st1,vo1,ob1),(sy2,st2,vo2,ob2) = sel
    
    if ob2 > ob1: # sonst wird Zeichen hinter rechtem Cursor auch bearbeitet
        ob2 -= 1
        sel = (sy1,st1,vo1,ob1),(sy2,st2,vo2,ob2)
    if sel == 0:
        messageBox('Fehler', 'keine aktive Partitur')
        return (0,0,0,0),(0,0,0,0)
    if sel[0][0:3] <> sel[1][0:3]: # mehrere Stimmen markiert -> alle Noten
        ob1 = 0
        ob2 = 999
    if sel[0][0:2] <> sel[1][0:2]: # mehrere Notenzeilen markiert -> alle Stimmen
        vo1 = 0
        vo2 = 999
    if sel[0][0] <> sel[1][0]: # mehrere Systeme markiert -> alle Notenzeilen
        st1 = 0
        st2 = 999
    sel = (sy1,st1,vo1,ob1),(sy2,st2,vo2,ob2)
    return sel

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 insertLyric(chord, pos):
    for lyric in chord.getElementsByTagName('lyric'):
        for verse in lyric.getElementsByTagName('verse'):
            i = eval(verse.getAttribute('i'))
            if i >= pos:
                verse.setAttribute('i', str(i+1))
                if i == 9:
                    verse.parentNode.removeChild(verse)

def deleteLyric(chord, pos):
    for lyric in chord.getElementsByTagName('lyric'):
        for verse in lyric.getElementsByTagName('verse'):
            i = eval(verse.getAttribute('i'))
            if pos == i:
                verse.parentNode.removeChild(verse)
            if i > pos:
                verse.setAttribute('i', str(i-1))
def deleteLyricRange(chord, pos1, pos2):
    if pos1 > pos2:
        p1 = pos2
        p2 = pos1
    else:
        p1 = pos1
        p2 = pos2
    for lyric in chord.getElementsByTagName('lyric'):
        for verse in lyric.getElementsByTagName('verse'):
            i = eval(verse.getAttribute('i'))
            if p1 <= i <= p2:
                verse.parentNode.removeChild(verse)
            if i > p2:
                verse.setAttribute('i', str(i - p2 + p1 - 1))

def xchangeLyric(chord, pos1, pos2):
    for lyric in chord.getElementsByTagName('lyric'):
        for verse in lyric.getElementsByTagName('verse'):
            i = eval(verse.getAttribute('i'))
            if i == pos1:
                verse.setAttribute('i', str(pos2))
            if i == pos2:
                verse.setAttribute('i', str(pos1))

def copyLyric(chord, pos1, pos2):
    for lyric in chord.getElementsByTagName('lyric'):
        verse_copy = None
        for verse in lyric.getElementsByTagName('verse'):
            i = eval(verse.getAttribute('i'))
            if i == pos1:
                verse_copy = verse.cloneNode(True)
            if i == pos2:
                verse.parentNode.removeChild(verse)
        if verse_copy:
            verse_copy.setAttribute('i', str(pos2))
            lyric.appendChild(verse_copy)


getOptions()

versCount = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
versA = ComboBox(versCount, width = 5, value = dlgSet.versA)
versB = ComboBox(versCount, width = 5, value = dlgSet.versB)
rangeSel = CheckBox('' , value = dlgSet.rangeSel)
actionSelect = Radio(['Einfügen vor A', 'Löschen A', 'Löschen A bis B', 'Tauschen A <-> B', 'Kopieren A -> B'], value = dlgSet.actionSelect)

dlg = Dialog('--- Lyric Handler ---',
             VBox([HBox([Label('Strophe A: ', width = 10), versA]),
                   HBox([Label('Strophe B: ', width = 10), versB]),
                   Label(' '),
                   HBox([Label('Aktion   : ', width = 10), actionSelect]),
                   Label(' '),
                   HBox([Label('Bereich  : ', width = 10), rangeSel, Label('ganze Partitur?')]),
                   Label(''),
                   HBox([Label('Bemerkung:', width = 10), Label('Sind mehrere Stimmen markiert, so gilt ')]),
                   HBox([Label('',           width = 10), Label('die Markierung für die ganze Notenzeile.')])
                 ])
             )

if dlg.run():
    ok = True
    dlgSet.versA = versA.value()
    dlgSet.versB = versB.value()
    dlgSet.rangeSel = rangeSel.value()
    dlgSet.actionSelect = actionSelect.value()

    (sy1, st1, vo1, ob1),(sy2, st2, vo2, ob2) = getCursorRange()
    if dlgSet.rangeSel:
        (sy1, st1, vo1, ob1),(sy2, st2, vo2, ob2) = (0,0,0,0),(999,999,999,999)  # ganze Partitur

    setOptions()
else:
    ok = False

def handleLyricVoice(voice, ob1, ob2, action):
    for noteObjects in voice.getElementsByTagName('noteObjects'):
        ob = 0
        for obj in getElementObjects(noteObjects.childNodes):
            if ob1 <= ob <= ob2 and obj.tagName == 'chord':
                if action == 0:                                 # insert lyric
                    insertLyric(obj, dlgSet.versA)
                elif action == 1:                               # delete lyric
                    deleteLyric(obj, dlgSet.versA)
                elif action == 2:                               # delete lyric range
                    deleteLyricRange(obj, dlgSet.versA, dlgSet.versB)
                elif action == 3:                               # xchange lyric
                    xchangeLyric(obj, dlgSet.versA, dlgSet.versB)
                elif action == 4:                               # copy lyric
                    copyLyric(obj, dlgSet.versA, dlgSet.versB)
            ob += 1

def changeDoc(score):
    sy = 0
    for system in score.getElementsByTagName('system'):
        if sy1 <= sy <= sy2:
            st = 0
            for staff in system.getElementsByTagName('staff'):
                if st1 <= st <= st2:
                    vo = 0
                    for voice in staff.getElementsByTagName('voice'):
                        if vo1 <= vo <= vo2:
                            handleLyricVoice(voice, ob1, ob2, dlgSet.actionSelect)
                        vo += 1
                st += 1
        sy += 1

# Hauptprogramm:

from caplib.capDOM import ScoreChange
import tempfile

class ScoreChange(ScoreChange):
    def changeScore(self, score):
        changeDoc(score)

if activeScore() and ok:
    activeScore().registerUndo("LyricHandler")
    tempInput = tempfile.mktemp('.capx')
    tempOutput = tempfile.mktemp('.capx')
    activeScore().write(tempInput)
    
    ScoreChange(tempInput, tempOutput)

    activeScore().read(tempOutput)
    os.remove(tempInput)
    os.remove(tempOutput)            
                

