# -*- coding: ISO-8859-1 -*-
""" capellaScript -- (c) Paul Villiger
>>> Notenzeilen transponieren

    Mit diesem Skript können einzelne Notenzeilen einer Partitur oder die ganze Partitur transponiert werden.
    Die aktuelle Tonart und die Notenzeile an der Cursorposition werden in den Dialog übernommen.
    Auch Transposition nach Instrumentenstimmung ist möglich. Neu lässt sich ein ganzer Ordner transponieren.
        
<<<

    History 25.09.2004 - Erstausgabe
            12.10.2004 - Transposition nach Instrumentenstimmung
                       - registerUndo() eingebaut
            06.11.2010 - ganzen Ordner transponieren
            19.01.2011 - Fehler bei unterschiedlicher Zeilenanzahl pro System
    

    Bemerkung:
    Das Transponieren erfolgt 2-stufig. Zuerst wird von der aktuellen Tonart nach C runter
    transponiert und danach wieder zur Zieltonart rauf. Oktavierungen können bei Bedarf in einem
    zweiten Durchgang ohne Transponieren erfolgen.
                    
"""

import tempfile, string

toC = {7:[-1,-1],   # cis
       6:[-4,0],    # fis
       5:[-7,0],    # h
       4:[-3,0],    # e
       3:[-6,0],    # a
       2:[-2,0],    # d
       1:[-5,0],    # g
       0:[0,-1],    # c
       -1:[-3,-1],  # f
       -2:[-6,-1],  # b
       -3:[-2,-1],  # es
       -4:[-5,-1],  # as
       -5:[-1,-1],  # des
       -6:[-4,-1],  # ges
       -7:[-7,0]}   # ces

fromC = {7:[-7,0],  # cis
         6:[-4,0],  # fis
         5:[-1,-1], # h
        4:[3,0],    # e
        3:[6,0],    # a
        2:[2,0],    # d
        1:[5,0],    # g
        0:[0,-1],   # c
       -1:[3,-1],   # f
       -2:[6,-1],   # b
       -3:[2,-1],   # es
       -4:[5,-1],   # as
       -5:[1,-1],   # des
       -6:[4,-1],   # ges
       -7:[7,0]}    # ces


def transposeScore(score, staffSelect, transposeKey, transposeOctave):

    for system in activeScore().systems():
        staffIndex = system.staffIndexFromDescr(staffSelect)
        for staff in system.staves():
            if staffSelect and staff.index() <> staffIndex:
                continue 
            if transposeKey: # Tonart transponieren
                staff.transpose(toC[transposeFrom])
                staff.transpose(fromC[transposeTo])
                if transposeTo > 5:
                    staff.transpose([7,-1])  #  + 1 Oktave
            else: # Stimmung transponieren
                staff.transpose(toC[transposeTo])
                staff.transpose(fromC[transposeFrom])
                # ?? eventuell oktavieren ??

            if transposeOctave > 0:
                for i in range(transposeOctave):
                    staff.transpose([7,-1])  #  - 1 Oktave
            elif transposeOctave < 0:
                for i in range(-transposeOctave):
                    staff.transpose([-7,-1])  #  + 1 Oktave

def getCapFiles(directory):
    fileList = []
    if os.path.isdir(directory):
        files = os.listdir(directory)
        for f in files:
            path, ext = os.path.splitext(f)
            if ext.lower() in ['.cap','.capx']:
                fileList.append(os.path.join(directory,f))

    return fileList

def getDirectory():
    dlg = FileDialog()
    dlg.__init__(bOpen=False)
    dlg.setTitle('Bitte Ordner auswählen')
    dlg.addFilter('capella-Dateien', '*.cap;*.capx')
    
    dlg.setStartFile('$$XX.cap')

    if dlg.run():
        scoreFile=dlg.filePath()
        scoreDir, scoreName = os.path.split(scoreFile)
        return scoreDir
    else:
        return None

def convertClef(score, addOctave):
    for voice in score.getElementsByTagName('voice'):
        for noteObjects in voice.getElementsByTagName('noteObjects'):
            for child in noteObjects.childNodes:
                if child.nodeType == child.ELEMENT_NODE and child.tagName == 'clefSign':
                    if child.getAttribute('clef') == 'bass':
                        child.setAttribute('clef', 'treble')
                elif child.nodeType == child.ELEMENT_NODE and child.tagName == 'chord':
                    if addOctave > 0:
                        for head in child.getElementsByTagName('head'):
                            pitch = head.getAttribute('pitch')
                            pitch = pitch[0] + str(eval(pitch[1]) + addOctave)
                            head.setAttribute('pitch', pitch)

from caplib.capDOM import ScoreChange

class ScoreChange(ScoreChange):
    def changeScore(self, score):
        convertClef(score, changeOctave)
        

def handleXmlScore():

    tempInput = tempfile.mktemp('.capx')
    tempOutput = tempfile.mktemp('.capx')
    activeScore().write(tempInput)

    ScoreChange(tempInput, tempOutput)

    activeScore().read(tempOutput)
    os.remove(tempInput)
    os.remove(tempOutput)
                            
                        
    
    
actualKey = 0
staffList = activeScore().voiceList()
staffIndex = 0
sel = curSelection()

if sel <> 0:
    (sy,st,vo,no) = sel[0]
    system = activeScore().system(sy)
    staff = system.staff(st)
    voice = staff.voice(vo)
    # Aktuelle Tonart am Cursor bestimmen
    if no < voice.nNoteObjs():
        actualKey = voice.noteObj(no).curKey()
        
    # Aktueller Eintrag im Mustersystem Bestimmen
    i = 0
    for descr in staffList:
        if staff.index() == system.staffIndexFromDescr(descr):
            staffIndex = i
        i += 1

keyList = ['7# Cis','6# Fis','5# H','4# E','3# A','2# D','1# G','C','1b F','2b B','3b Es','4b As','5b Des','6b Ges','7b Ces']

keyFrom = ComboBox( keyList, value = -actualKey + 7, width=8)
labFrom = Label('von Tonart/', width=8)
labFrom1= Label('Stimmung:', width=8)

labTo = Label('nach Tonart/', width=8)
labTo1 = Label('Stimmung:', width=8)
keyTo  = ComboBox( keyList, value = 7, width=8)

labOctave = Label('Oktaven:', width=8)
octave = ComboBox( ['+2','+1','0','-1','-2'],value = 2, width=8)

selStaff = ComboBox(staffList, value = staffIndex, width = 20)

selAction = Radio(['Partitur transponieren',
                   'NotenZeile transponieren',
                   'Ordner transponieren (ganze Partitur)'], width=12,value = 0)
selAction1 = Radio(['Tonart transponieren (C -> B-Dur)',
                   'Stimmung transponieren(C -> Es-Instrument)'],width=12, value = 0)
changeClef = CheckBox('Bassschlüssel nach Violinschlüssel ändern?', value = 0)
changeOctave = ComboBox( ['0','+1','+2'], value = 0, width=6)

dlg = Dialog('Transponieren von Tonart oder Stimmung',
             VBox([
                   HBox([labFrom,labTo,labOctave], padding = 8),
                   HBox([labFrom1,labTo1], padding = 8),
                   Label(' '),
                   HBox([keyFrom,keyTo,octave],padding = 8),
                   Label(' '),
                   HBox([selAction1,Label(' ',width=8)], text=' Art der Transposition ',padding = 8),
                   Label(' '),
                   HBox([selAction, VBox([Label(' '),selStaff], padding = 8)], text=' Bereich ',padding = 8),
                   Label(' '),
                   HBox([changeClef, Label(' '), changeOctave, Label(' Bassnoten oktavieren   ')], text = ' Zusatzfunktion (ganze Partitur) ', padding = 0),
                   Label(' ')
                  ]
                 )
             )
            
if dlg.run():
    transposeFrom = - (keyFrom.value() -7)
    transposeTo = - (keyTo.value() -7)
    transposeOctave = - (octave.value() -2)
    action = selAction.value()
    transposeKey = selAction1.value() == 0
    changeClef = changeClef.value() == 1
    changeOctave = changeOctave.value()
    

    if action == 1: # nur selektierte Notenzeile
        activeScore().registerUndo("Notenzeilen transponieren")
        staffSelect = staffList[selStaff.value()]
        transposeScore(activeScore(), staffSelect, transposeKey, transposeOctave)
    elif action == 0: #ganze Partitur
        activeScore().registerUndo("Notenzeilen transponieren")
        transposeScore(activeScore(), '', transposeKey, transposeOctave)
        if changeClef:
            handleXmlScore()

    elif action == 2: #ganzen Ordner transponieren
        selDir = getDirectory()
        if selDir:
            fileList = getCapFiles(selDir)
            for f in fileList:
                path, ext = os.path.splitext(f)
                if ext.lower() == '.cap':
                    xml = 0
                else:
                    xml = 1
                openScore(f)
                transposeScore(activeScore(), '', transposeKey, transposeOctave)
                if changeClef:
                    handleXmlScore()
                activeScore().write(f, xml)
                closeActiveScore()


