# -*- coding: ISO-8859-1 -*-
""" capellaScript -- 03.07.2004 Andreas Herzog
>>> Caecilia

    Mit Hilfe dieses Skripts können Chorpartituren schnell und bequem umgewandelt werden.|
    |
    Sofern durch die Undo-Funktio das Layout der Partitur durch einen internen Fehler von Capella zerstört wird, bitte
    das Skript LayoutFix von Peter Becker verwenden!
    
    Version 1.0b: Vertauschte Stimmen korrigiert    |
    
    
    
    |
    

        |

<<<

# 1.0: Ursprungsversion
# 1.0a: Kleine Fehlerkorrektur
# 1.0b: Vertauschte Stimmen korrigiert
"""
import new
from xml.dom.minidom import Node, Element
import xml.dom
import string
from xml.dom.minidom import NodeList


justStaffs = []
justVoices = []

def latin1_e(u):
    return u.encode('Latin-1')
def latin1_d(u):
    return u.decode('Latin-1')
    
# neu eingefügt
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
    
# neu eingefügt
def addNewElementNode(el,tagName):
    # add new Node with tagName "tagName" to el 
    global doc
    newChild = doc.createElement(tagName)
    el.appendChild(newChild)
    return newChild
    
def getAllLayouts(score):
   
    # Gibt eine Liste zurueck mit allen Eintraegen aus dem Mustersystem,
    # sowie deren Verwendungshaeufigkeit

    staffLayouts = score.getElementsByTagName('staffLayout')
    for i in range(staffLayouts.length):
        staffLayouts[i].count = 0
        for staff in score.getElementsByTagName('staff'):
            if staffLayouts[i].getAttribute('description') == staff.getAttribute('layout'):
                staffLayouts[i].count += 1
                
    return staffLayouts
    
def getDialogValues1():

    global  allLayouts, Auswahl, selectedLayoutRight, selectedLayoutLeft, TonDarstellung, Zielpartiturstimmigkeit, AlteStimmen, StimmenAusrichten, TenorAutomatik, TextAutomatik

    
    rad2 = Radio(['in der Partitur belassen', 'Stimmen löschen','Zeilen löschen'], text='Ausgangsstimmen', padding = 0, value=0)
    rad1 = Radio(['zwei Zeilen (Sopran/Alt & Tenor/Bass)', 'drei Zeilen (Sopran/Alt & Tenor & Bass)','vier Zeilen (Sopran & Alt & Tenor & Bass)'], text='Zielpartitur:', padding = 0, value=0)
    lab1 = Label('Konvertieren in:', width = 1)
    checkBox1 = CheckBox('Stimmen automatisch ausrichten', value=1)
    checkBox2 = CheckBox('Tenor-/Bassschlüssel automatisch zuweisen', value=1)
    checkBox3 = CheckBox('Textautomatik', value=0)
    vbox1  = VBox([checkBox1, checkBox2, checkBox3], text='', padding=4)
    vbox2  = VBox([lab1,rad1,   vbox1], text='', padding=12)
    
    
    dlg = Dialog('Chorpartitur: ', vbox2)

    if dlg.run():
        
        #Auswahl = rad1.value()
        Zielpartiturstimmigkeit = rad1.value() + 2
        AlteStimmen = rad2.value()
        StimmenAusrichten = checkBox1.value()
        TenorAutomatik = checkBox2.value()
        TextAutomatik = checkBox3.value()        
        return True
    else:
        return False

def getDialogValues2(voiceNames):

    global  allLayouts, Auswahl, selectedTenorVoice, selectedBassVoice, selectedTenorStaff, selectedBassStaff, selectedAltVoice, selectedSopranVoice, selectedAltStaff, selectedSopranStaff, Stimmenzahl, Zielpartiturstimmigkeit, StimmenAusrichten, selectedVoices

    
    rad1 = Radio(['zwei Zeilen (Sopran/Alt + Tenor/Bass)', 'drei Zeilen (Sopran/Alt & Tenor & Bass)','vier Zeilen (Sopran & Alt & Tenor & Bass)'], text='Ausgangspartitur:', padding = 0, value=2)
    rad2 = Radio(['zwei Zeilen (Sopran/Alt + Tenor/Bass)', 'drei Zeilen (Sopran/Alt & Tenor & Bass)','vier Zeilen (Sopran & Alt & Tenor & Bass)'], text='Zielpartitur:', padding = 0, value=2)
    
    
    
    layoutList = [ latin1_e(allLayouts[i].getAttribute('description')) for i in range(allLayouts.length ) ]
    
    
    selLayoutCombo1 = ComboBox(voiceNames, value = 0, width=32)
    selLayoutCombo2 = ComboBox(voiceNames, value = 1, width=32)
    selLayoutCombo3 = ComboBox(voiceNames, value = 2, width=32)
    selLayoutCombo4 = ComboBox(voiceNames, value = 3, width=32)
    

    lab1 = Label('Sopran:', width = 5) 
    lab2 = Label('Alt:   ', width = 5) 
    lab3 = Label('Tenor: ', width = 5) 
    lab4 = Label('Bass:  ', width = 5) 
    lableer = Label(' ', width = 1) 


    hBox1 = HBox([lab1, selLayoutCombo1], text='', padding=12)
    hBox2 = HBox([lab2, selLayoutCombo2], text='', padding=12)
    hBox3 = HBox([lab3, selLayoutCombo3], text='', padding=12)
    hBox4 = HBox([lab4, selLayoutCombo4], text='', padding=12)
    
    vbox1  = VBox([hBox1,hBox2,hBox3,hBox4], text='', padding=12)
    vbox2  = VBox([vbox1,lableer], text='', padding=12)
    
    dlg = Dialog('Ausgangsstimmen wählen: ', vbox2)

    if dlg.run() and Stimmenzahl>=4:
        selectedSopranVoice = latin1_d(justVoices[selLayoutCombo1.value()])
        selectedSopranStaff = latin1_d(justStaffs[selLayoutCombo1.value()])        
        selectedAltVoice = latin1_d(justVoices[selLayoutCombo2.value()])
        selectedAltStaff = latin1_d(justStaffs[selLayoutCombo2.value()])
        selectedTenorVoice = latin1_d(justVoices[selLayoutCombo3.value()])
        selectedTenorStaff = latin1_d(justStaffs[selLayoutCombo3.value()])
        selectedBassVoice = latin1_d(justVoices[selLayoutCombo4.value()])
        selectedBassStaff = latin1_d(justStaffs[selLayoutCombo4.value()])        
        selectedVoices = [voiceNames[selLayoutCombo1.value()],voiceNames[selLayoutCombo2.value()],voiceNames[selLayoutCombo3.value()],voiceNames[selLayoutCombo4.value()]]
        return True
    else:
        return False

def getDialogValues3():

    global  selectedVoices, TextVoice

    selTextCombo = ComboBox(selectedVoices, value = 0, width=32)

    lab1 = Label('Textzeile', width = 5) 
    lab2 = Label('Achtung: die bisherigen Liedtexte der anderen', width = 5) 
    lab3 = Label('               Stimmen werden ggf. überschrieben!', width = 5) 
    
    lableer = Label('', width = 5) 
    hBox1 = HBox([lab1, selTextCombo], text='', padding=12)
    
    vbox1  = VBox([hBox1], text='', padding=12)
    vbox2  = VBox([vbox1, lab2, lab3], text='', padding=4)
    
    dlg = Dialog('Ausgangsstimmen wählen: ', vbox2)

    if dlg.run() and Stimmenzahl>=4:
        TextVoice = selTextCombo.value()
        return True
    else:
        return False

def getCursor():
    sel = curSelection()
    result = None
    if sel == 0:
        messageBox('Fehler', 'keine aktive Partitur')
        return result
    result = sel[0]
    return result
                        
def getVoiceNames(score):
    global Stimmenzahl
    
    layouts = []
    
    Stimmenzahl = 0
    for staff in score.getElementsByTagName('staff'):
        layout = staff.getAttribute('layout')
        for n in range(staff.getElementsByTagName('voice').length):
            nplus = n+1
            justStaff = latin1_e(layout)
            justVoice = latin1_e(' %02d' % n)
            voiceName = latin1_e(layout+(' %02d' % nplus)+'.Stimme')
            if voiceName not in layouts:
                layouts.append(voiceName)
                justStaffs.append(justStaff)
                justVoices.append(justVoice)
                Stimmenzahl = Stimmenzahl + 1
    #layouts.sort()
    return layouts
    
    
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)    
    
def changeDoc(score):
	global  allLayouts, selectedTenorVoice, selectedBassVoice, selectedTenorStaff, selectedBassStaff, selectedSopranVoice, selectedSopranStaff,  selectedAltVoice, selectedAltStaff, stimme, Stimmenzahl, TenorAutomatik, TextAutomatik, TextVoice
	 
	voiceNames = getVoiceNames(score)
	allLayouts = getAllLayouts(score)
	staffLayouts = score.getElementsByTagName('staffLayout')
	systems = score.getElementsByTagName('system')
	staffs = score.getElementsByTagName('staff')
	
	
	if getDialogValues1():
		if getDialogValues2(voiceNames):
		 TextTrue = True
		 if TextAutomatik == 1:								# Dritter Dialog, sofern Textautomatik gewünscht wird
		 	TextTrue = getDialogValues3()
		 if TextTrue == True:

			BracketCounter = 0

			if Zielpartiturstimmigkeit == 2:				# Benennung der neuen Stimmen in Abhängigkeit von der Zielstimmigkeit
				ZielStimme1 = 'Sopran und Alt neu'
				ZielStimme2 = 'Tenor und Bass neu'
				ZielStimme3 = 'Tenor temporaer'
				ZielStimme4 = 'Alt temporaer' 
			if Zielpartiturstimmigkeit == 3:
				ZielStimme1 = 'Sopran und Alt neu'
				ZielStimme2 = 'Tenor neu'
				ZielStimme3 = 'Bass neu'
				ZielStimme4 = 'Alt temporaer'
			if Zielpartiturstimmigkeit == 4: 
				ZielStimme1 = 'Sopran neu'
				ZielStimme2 = 'Bass neu'
				ZielStimme3 = 'Tenor neu'
				ZielStimme4 = 'Alt neu'
			
			if TextAutomatik == 1:							# Zuweisung 
				if TextVoice == 0:
					ZielText = ZielStimme1
				if TextVoice == 1:
					ZielText = ZielStimme4
				if TextVoice == 2:
					ZielText = ZielStimme3
				if TextVoice == 3:
					ZielText = ZielStimme2														
					
			for layout in staffLayouts:
				descr = layout.getAttribute('description')
				if descr == selectedBassStaff:
					BassDistances = layout.getElementsByTagName('distances')[0]	# Speicherung des Abstandes für Korrektur der Altabstände
					BassTop = BassDistances.getAttribute('top') 
				if descr == selectedSopranStaff:
					SopranDistances = layout.getElementsByTagName('distances')[0]	# Speicherung des Abstandes für Korrektur der Tenorabstände
					SopranBottom = SopranDistances.getAttribute('bottom') 
										
			for layout in staffLayouts:							# Bearbeitung des Mustersystems
				descr = layout.getAttribute('description')
				
				if descr == selectedSopranStaff:				# Clonen und Einfügen der Stimmen im Mustersystem
					BracketStart = BracketCounter				# Klammerstart 
					clone1 = layout.cloneNode(1)
					clone1.setAttribute('description', ZielStimme1)
					layout.parentNode.appendChild(clone1)
				if descr == selectedAltStaff and Zielpartiturstimmigkeit == 4:			
					clone3 = layout.cloneNode(1)
					clone3.setAttribute('description', ZielStimme4)
					AltDistances = clone3.getElementsByTagName('distances')[0]
					AltDistances.setAttribute('top',BassTop)						# Korrektur der AltAbstände
					clone3.setAttribute('description', ZielStimme4)
					layout.parentNode.appendChild(clone3)
				if descr == selectedTenorStaff and Zielpartiturstimmigkeit >= 3:			
					clone4 = layout.cloneNode(1)
					clone4.setAttribute('description', ZielStimme3)
					TenorDistances = clone4.getElementsByTagName('distances')[0]	# Korrektur der TenorAbstände
					TenorDistances.setAttribute('bottom',SopranBottom)					
					clone4.setAttribute('description', ZielStimme3)
					if TenorAutomatik == 1:
						notation4 = clone4.getElementsByTagName('notation')[0]
						notation4.setAttribute('defaultClef','G2-')
					layout.parentNode.appendChild(clone4)	
				if descr == selectedBassStaff:			
					clone2 = layout.cloneNode(1)
					clone2.setAttribute('description', ZielStimme2)
					layout.parentNode.appendChild(clone2)
			
			for system in systems:
			 staves = system.getElementsByTagName('staves')
			 for staff in system.getElementsByTagName('staff'): 									# Bearbeitung der Partitursysteme
				descr = staff.getAttribute('layout')			# Clonen und Einfügen der Zeilen im Partitursystem
				

				if descr == selectedSopranStaff:									# Bearbeitung der Sopran (bzw. Alt)-Stimme
					found1 = 1
					clone1 = staff.cloneNode(1)
					voiceCounter = 0
					for voice in clone1.getElementsByTagName('voice'):				# Ausrichten der Stimme
							if StimmenAusrichten == 1:
								if Zielpartiturstimmigkeit == 2:
									voice.setAttribute('stemDir' , 'up')
								if Zielpartiturstimmigkeit == 3:
									voice.setAttribute('stemDir' , 'up')							
								if Zielpartiturstimmigkeit == 4:
									voice.setAttribute('stemDir' , '')
								if voiceCounter <> string.atoi(selectedSopranVoice):
									voice.parentNode.removeChild(voice)
							voiceCounter += 1
					clone1.setAttribute('layout', ZielStimme1)
						
				if descr == selectedBassStaff:										# Bearbeitung der Bass (bzw. Tenor)-Stimme
					found2 = 1
					clone2 = staff.cloneNode(1)
					voiceCounter = 0
					for voice in clone2.getElementsByTagName('voice'):				# Ausrichten der Stimme			
							if StimmenAusrichten == 1:
								if Zielpartiturstimmigkeit == 2:
									voice.setAttribute('stemDir' , 'down')
								if Zielpartiturstimmigkeit == 3:
									voice.setAttribute('stemDir' , '')							
								if Zielpartiturstimmigkeit == 4:
									voice.setAttribute('stemDir' , '')					
							if voiceCounter <> string.atoi(selectedBassVoice):		# Weglöschen der anderen Stimmen
								voice.parentNode.removeChild(voice)
							voiceCounter += 1
					clone2.setAttribute('layout', ZielStimme2)

				if  descr == selectedTenorStaff:									# Bearbeitung der Tenor-Stimme
					found3 = 1
					clone3 = staff.cloneNode(1)
					voiceCounter = 0
					for voice in clone3.getElementsByTagName('voice'):				
							if StimmenAusrichten == 1:								# Ausrichten der Stimme
								if Zielpartiturstimmigkeit == 2:
									voice.setAttribute('stemDir' , 'up')
								if Zielpartiturstimmigkeit == 3:
									voice.setAttribute('stemDir' , '')							
								if Zielpartiturstimmigkeit == 4:
									voice.setAttribute('stemDir' , '')					
							if voiceCounter <> string.atoi(selectedTenorVoice):		# Weglöschen der anderen Stimmen
								voice.parentNode.removeChild(voice)	
							voiceCounter += 1
							if TenorAutomatik == 1 and Zielpartiturstimmigkeit == 2:
								clefSigns = voice.getElementsByTagName('clefSign')
								if clefSigns.length <> 0:
									clefSigns[0].setAttribute('clef','bass')
							if TenorAutomatik == 1 and Zielpartiturstimmigkeit >= 3:
								clefSigns = voice.getElementsByTagName('clefSign')
								if clefSigns.length <> 0:
									clefSigns[0].setAttribute('clef','G2-')
					clone3.setAttribute('layout', ZielStimme3)
											
				if descr == selectedAltStaff:										# Bearbeitung der Alt-Stimme
					found4 = 1
					clone4 = staff.cloneNode(1)
					voiceCounter = 0
					for voice in clone4.getElementsByTagName('voice'):				
							if StimmenAusrichten == 1:								# Ausrichten der Stimme
								if Zielpartiturstimmigkeit == 2:
									voice.setAttribute('stemDir' , 'down')
								if Zielpartiturstimmigkeit == 3:
									voice.setAttribute('stemDir' , 'down')							
								if Zielpartiturstimmigkeit == 4:
									voice.setAttribute('stemDir' , '')								
							if voiceCounter <> string.atoi(selectedAltVoice):		# Weglöschen der anderen Stimmen
								voice.parentNode.removeChild(voice)
							voiceCounter += 1
					clone4.setAttribute('layout', ZielStimme4)
					
				BracketCounter += 1		
			 
			 CountChords1 = 0														# Übertragung des Textes
			 CountChords2 = 0
			 CountChords3 = 0
			 CountChords4 = 0
			 
			 for chord in clone1.getElementsByTagName('chord'):
			 	CountChords1 += 1 
			 for chord in clone2.getElementsByTagName('chord'):
			 	CountChords2 += 1 
			 for chord in clone3.getElementsByTagName('chord'):
			 	CountChords3 += 1 
			 for chord in clone4.getElementsByTagName('chord'):
			 	CountChords4 += 1 
			 				 				 				 
			 CloneMass = [clone1, clone2, clone3, clone4]
			 
			 
			 if TextAutomatik == 1:
			  for cloneX in CloneMass:
			   if cloneX.getElementsByTagName('voice').length <> 0 and cloneX.getAttribute('layout') == ZielText:
			    for voice in cloneX.getElementsByTagName('voice'):
			 			ChordCounter = 0
			 			for chord in cloneX.getElementsByTagName('chord'):
			 			 if ChordCounter < CountChords1:
			 				if chord.getElementsByTagName('lyric').length <> 0:
			 					LyricClone = chord.getElementsByTagName('lyric')[0].cloneNode(1)
			 					clone1.getElementsByTagName('chord')[ChordCounter].appendChild(LyricClone)
			 			 if ChordCounter < CountChords2 and Zielpartiturstimmigkeit >= 3:
			 				if chord.getElementsByTagName('lyric').length <> 0:
			 					LyricClone = chord.getElementsByTagName('lyric')[0].cloneNode(1)
			 					clone2.getElementsByTagName('chord')[ChordCounter].appendChild(LyricClone)
			 			 if ChordCounter < CountChords3 and Zielpartiturstimmigkeit >= 3:
			 				if chord.getElementsByTagName('lyric').length <> 0:
			 					LyricClone = chord.getElementsByTagName('lyric')[0].cloneNode(1)
			 					clone3.getElementsByTagName('chord')[ChordCounter].appendChild(LyricClone)
			 			 if ChordCounter < CountChords4 and Zielpartiturstimmigkeit == 4:
			 				if chord.getElementsByTagName('lyric').length <> 0:
			 					LyricClone = chord.getElementsByTagName('lyric')[0].cloneNode(1)
			 					clone4.getElementsByTagName('chord')[ChordCounter].appendChild(LyricClone)
			 			 ChordCounter += 1



			 
			 if Zielpartiturstimmigkeit == 4: 										# Anhängen der Stimmen in der Partitur
				 if clone1.getElementsByTagName('voice').length <> 0:				# bei 4 Stimmen	
				 	staff.parentNode.appendChild(clone1)			
				 if clone2.getElementsByTagName('voice').length <> 0:
				 	staff.parentNode.appendChild(clone2)				
				 if clone3.getElementsByTagName('voice').length <> 0:
				 	staff.parentNode.appendChild(clone3)				
				 if clone4.getElementsByTagName('voice').length <> 0:
				 	staff.parentNode.appendChild(clone4)				

			 if Zielpartiturstimmigkeit == 3: 										# Anhängen der Stimmen in der Partitur
				 if clone1.getElementsByTagName('voice').length <> 0:				# bei 3 Stimmen	
				 	if clone4.getElementsByTagName('voice').length <> 0:
				 		for voice in clone4.getElementsByTagName('voice'):
				 			VoiceClone1 = voice.cloneNode(1)
				 	 	for voice in clone1.getElementsByTagName('voice'):
				 			voice.parentNode.appendChild(VoiceClone1)
				 	staff.parentNode.appendChild(clone1)			
				 if clone2.getElementsByTagName('voice').length <> 0:
				 	staff.parentNode.appendChild(clone2)				
				 if clone3.getElementsByTagName('voice').length <> 0:
				 	staff.parentNode.appendChild(clone3)				
				 if clone4.getElementsByTagName('voice').length <> 0 and clone1.getElementsByTagName('voice').length == 0:
				 	clone4.setAttribute('description', ZielStimme1)
				 	staff.parentNode.appendChild(clone4)				 				 				
				 	
			 if Zielpartiturstimmigkeit == 2: 										# Anhängen der Stimmen in der Partitur
				 if clone1.getElementsByTagName('voice').length <> 0:				# bei 2 Stimmen	
				 	if clone4.getElementsByTagName('voice').length <> 0:
				 		for voice in clone4.getElementsByTagName('voice'):
				 			VoiceClone1 = voice.cloneNode(1)
				 	 	for voice in clone1.getElementsByTagName('voice'):
				 			voice.parentNode.appendChild(VoiceClone1)
				 	staff.parentNode.appendChild(clone1)			
				 if clone2.getElementsByTagName('voice').length <> 0:
				 	if clone3.getElementsByTagName('voice').length <> 0:
				 		for voice in clone3.getElementsByTagName('voice'):
				 			VoiceClone2 = voice.cloneNode(1)
				 	 	for voice in clone2.getElementsByTagName('voice'):
				 			voice.parentNode.appendChild(VoiceClone2)
				 			voice.parentNode.insertBefore(VoiceClone2, voice)
				 	staff.parentNode.appendChild(clone2)				
				 if clone3.getElementsByTagName('voice').length <> 0 and clone2.getElementsByTagName('voice').length == 0:
				 	clone3.setAttribute('layout', ZielStimme2)
				 	staff.parentNode.appendChild(clone3)
				 if clone4.getElementsByTagName('voice').length <> 0 and clone1.getElementsByTagName('voice').length == 0:
				 	clone4.setAttribute('description', ZielStimme1)
				 	staff.parentNode.appendChild(clone4)
					

			for system in systems:										# Korrektur der Stimmenausrichtung bei einstimmigen
			 for staff in system.getElementsByTagName('staff'): 		# Notenzeilen
				VoiceCounter = 0
				if StimmenAusrichten == 1:
					layout = staff.getAttribute('layout')
					if layout == ZielStimme1 or layout == ZielStimme2 or layout == ZielStimme3 or layout == ZielStimme4:
						for voice in staff.getElementsByTagName('voice'):
							VoiceCounter += 1
						if VoiceCounter == 1:
							voice.setAttribute('stemDir','')			 				 					
			
																									

			if Stimmenzahl < 4:
				messageBox('Fehler:', 'Zu wenig Stimmen' + str(Stimmenzahl))
	    	
	    		
	    	
# 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("Caecilia")
	tempInput = tempfile.mktemp('.capx')
	tempOutput = tempfile.mktemp('.capx')
	activeScore().write(tempInput)
	ScoreChange(tempInput, tempOutput)
	activeScore().read(tempOutput)
	os.remove(tempInput)
	os.remove(tempOutput)

