# -*- coding: ISO-8859-1 -*-
""" capellaScript -- 27.12.2007 Hans H. Lampe
>>> Konvertieren von Violine nach Viola und v.v.
    (Version 1.1 - 22.08.2017)||
    Konvertieren der Notierung einer Violine in die einer Viola (Bratsche) und umgekehrt.||

    Noten außerhalb des Tonumfangs des Zielinstruments werden farbig markiert und
    auf Wunsch automatisch oktaviert.||

    Bitte den Cursor für die automatische Zeilenwahl in die entsprechende Notenzeile setzen,
    oder die gewünschte Notenzeile im nachfolgenden Dialog wählen.||

    In dieser Version werden nur einstimmige Notenzeilen unterstützt.|||
      
    Rückmeldungen bitte an Hans H. Lampe:|HansHermann.Lampe@t-online.de        
<<<

27.12.2017 V 1.0 HL Erste Version
22.08.2017 V 1.1 PB Anpassung an Capella 2008
                                 
"""

from caplib.capDOM import ScoreChange
import xml.dom.minidom
DOC = xml.dom.minidom.Document
import tempfile, codecs

def change(score):
 
    # ------ vom (fiktiven) C-Instrument zum Zielinstrument (T = Target) ------ #
    #===========================================================================#
  
    transposeTo = toInstr.value() # Position des "nach-Instruments" in der Tabelle
    Instrparam = fromCinstr[transposeTo] # Instrumentenparameter aus Tabelle lesen
                 
    for system in score.getElementsByTagName('system'):
        for staff in system.getElementsByTagName('staff'): # Aufsuchen der selektierten Notenzeile
            layout = staff.getAttribute('layout')
            if layout != staffList[selStaff.value()]:
                continue
           
    transposeTo = toInstr.value()
    toInstrX = fromCinstr[transposeTo]
    clefType = toInstrX[1]
            
    # Schlüssel setzen
    #=================

    for staffLayout in score.getElementsByTagName('staffLayout'): # Setzen des Schlüssels im Mustersystem #
        description = staffLayout.getAttribute('description')
        if description != staffList[selStaff.value()]:
            continue
        notation = staffLayout.getElementsByTagName('notation')[0]
        notation.setAttribute('defaultClef',['treble','alto'][clefType])

                   
    for staff in score.getElementsByTagName('staff'): # Setzen des Schlüssels in der Partitur #
        layout = staff.getAttribute('layout')
        if layout != staffList[selStaff.value()]:
            continue
        clefSign = staff.getElementsByTagName('clefSign')[0] 
        clefSign.setAttribute('clef',['treble','alto'][clefType])


def Octave(score):
    global indOktaviert

    # ------ Eventuelles Oktavieren der Noten außerhalb des Tonbereichs des Zielinstruments ------ #

    indOktaviert = 0
    if oktavieren: # Noten außerhalb des Tonbereichs des Zielinstruments oktavieren?
        
        if indColor:
            indOktaviert = 1
            for display in score.getElementsByTagName('display'):
                color = display.getAttribute('color')
                
                heads = display.parentNode.getElementsByTagName('head')
                noHeads = len(heads)
                #messageBox('stop',str(noHeads))

                if heads != []: # nur bei 'head' weiter ('display' kommt auch in ganzen und Ganztakt-Pausen vor)
                    headLow = display.parentNode.getElementsByTagName('head')[0] # unterster oder einziger Kopf
                    pitchLow = headLow.getAttribute('pitch')
                    
                    if noHeads > 1: #mehr als ein Kopf (bei 2 = Doppelgriff)
                        headHigh = display.parentNode.getElementsByTagName('head')[noHeads-1] # der höchsteKopf
                        pitchHigh = headHigh.getAttribute('pitch')

                        if color == 'FF0000': # rot (oben außerhalb des Tonbereichs)
                            headHigh.setAttribute('pitch', pitchHigh[0]+str(int(pitchHigh[1])-1))

                        if color == '0000FF': # blau (unten außerhalb des Tonbereichs)
                            headHigh.setAttribute('pitch',pitchHigh[0]+str(int(pitchHigh[1])+1))
                            

                    if color == 'FF0000': # rot (oben außerhalb des Tonbereichs)
                        headLow.setAttribute('pitch', pitchLow[0]+str(int(pitchLow[1])-1))

                    if color == '0000FF': # blau (unten außerhalb des Tonbereichs)
                        headLow.setAttribute('pitch',pitchLow[0]+str(int(pitchLow[1])+1))
        
               
    
class changeClass(ScoreChange):
        def changeScore(self, score):
            global scriptAction, doc
            doc = score.parentNode
            change(score)

class OctaveClass(ScoreChange):
        def changeScore(self, score):
            global scriptAction, doc
            doc = score.parentNode
            Octave(score)


# -------- Unterstützte Instrumente --------- #

instrList = ['Instrument auswählen',  #--00--# Default-Eintrag
             'Violine',               #--01--#
             'Viola (Bratsche)'       #--02--#
            ] 

# -------- 

toCinstr = {0:[9],   #  
            1:[0],   #
            2:[0],   # 
           } 
      

# -------- 

V = 0 # Violinschlüssel
A = 1 # Bratschenschlüssel

fromCinstr = {0:[9,0],    # vom fixtiven C-Instrument 
              1:[0,V],    # 
              2:[0,A],    # 
             }

# --------- Tonumfänge der Instrumente ---------- # 

pRt = {0:(00,00), # PitchRange-Table
       1:(55,96), # Violine: g-c4 (d5, Flageolett)
       2:(48,84), # Viola  : c-c3 (e4, Flageolett)                   
      }



active = 0 # zum Steuern der Schlussanzeige
staffList = activeScore().voiceList()
staffIndex = 0
sel = curSelection()


if sel <> 0:
    (sy,st,vo,no) = sel[0]
    system = activeScore().system(sy)
    staff = system.staff(st)
      
               
    # ------ Aktuellen Eintrag im Mustersystem bestimmen ------ #

    i = 0
    for descr in staffList:
        if staff.index() == system.staffIndexFromDescr(descr):
            staffIndex = i
        i += 1



# -------- Dialogbox --------- #        

labStaff1 = Label(' Notenzeile des Instruments  (durch Positionierung des Cursors wurde diese vorbestimmt)')
selStaff = ComboBox(staffList, value=staffIndex, width=20)
labStaff2 = Label('   "Beschreibung" der Notenzeile im capella-Mustersystem')

fromLab = Label(' von Instrument  -------------------------->')
fromInstr = ComboBox(instrList, value=0, width=18)

toLab = Label('nach Instrument', width=21)
toInstr = ComboBox(instrList, value=0, width=18)

oktavieren = CheckBox('Noten außerhalb des Tonbereichs werden oktaviert', value=0 )

nullLab = Label('')

Hinweis1a = Label('HINWEISE:  -  Falls nur der Tonumfang eines Instruments überprüft und/oder die Noten außerhalb seines Tonbereichs')
Hinweis1b = Label('                        oktaviert werden sollen, bitte das entsprechende Instrument in beiden Feldern auswählen.')
Hinweis2a = Label('                     -  Falls bei Doppelgriffen nur eine der beiden Noten außerhalb des Tonbereiches liegen sollte,')                    
Hinweis2b = Label('                        werden trotzdem beide farbig markiert und ggf. auch beide oktaviert.')
                   
dlg = Dialog('Konvertieren von Violine nach Viola und umgekehrt (Version 1.0)',

             VBox([
                   HBox([labStaff1]),
                   nullLab,
                   HBox([selStaff, labStaff2]),
                   nullLab,
                   HBox([fromLab, toLab], padding = 5), 
                   nullLab,
                   HBox([fromInstr, toInstr, oktavieren],padding = 32),
                   nullLab,
                   HBox([Hinweis1a]),
                   HBox([Hinweis1b]),
                   HBox([Hinweis2a]),
                   HBox([Hinweis2b]),
                   nullLab
                   ]
                 )
             )
      
      
def dlgRun():
    global active, transposeOctave, entfVorz, maxVorz, max6Vorz
    
    if dlg.run():
        active = 1 # nicht ganz klar - siehe unten
        transposeFrom = fromInstr.value()
        transposeTo = toInstr.value()
        
        Instr = toCinstr[transposeFrom]
        Instr = Instr[0]
        
        if Instr == 9:
            messageBox('Bitte das "von Instrument" auswählen', str('Bitte auch die entsprechende Notenzeile auswählen'))
            dlgRun()
            return # darf hier anschließend nicht weiterlaufen

        Instr = fromCinstr[transposeTo]
        Instr = Instr[0]
        
        if Instr == 9:
            messageBox('Bitte das "nach Instrument" auswählen', str('Bitte auch die entsprechende Notenzeile auswählen'))
            dlgRun()
            return # darf hier anschließend nicht weiterlaufen
                   
        activeScore().registerUndo("Instrumente transponieren") # an dieser Stelle???????????
    
                             
dlgRun()

if active == 1: # keine Schlussanzeige bei 'Abbrechen' #### nicht ganz klar !!!!!!!!!!

    oktavieren = oktavieren.value()
    
    if activeScore():  
            activeScore().registerUndo("Instrumente transponieren")
            tempInput = tempfile.mktemp('.capx') # für aktuelle aktive Partitur #
            tempOutput = tempfile.mktemp('.capx')

            activeScore().write(tempInput) # Einlesen der aktiven Partitur
                                    
            changeClass(tempInput, tempOutput) # Transposition durchführen
                       
            activeScore().read(tempOutput) # Ergebnis als aktive Partitur zurück

            
            # ------ Überprüfen des Tonumfangs ---- #

            indColor = 0
            for system in activeScore().systems():
                for staff in system.staves():
                    if staff.index() <> system.staffIndexFromDescr(staffList[selStaff.value()]):
                        continue 

                    # -------- Töne außerhalb des Tonumfangs werden rot dargestellt -------- #

                    # Alle Noten werden zuerst auf schwarz gesetzt
                
                    Bl = (Color.RGB(0,0,0)) # schwarz
                    for obj in staff.noteObjs(): # alle Noten auf schwarz setzen
                        if obj.isChord():
                            obj.setColor(Bl)

                    trTo = toInstr.value()          #transposeTo
                    
                    R = (Color.RGB(255,0,0)) # rot
                    B = (Color.RGB(0,0,255)) # blau

                    for obj in staff.noteObjs():
                        
                        if obj.isChord():
                            noHeads = obj.nHeads() # Anzahl der Köpfe
                            headLow = obj.head(0) # der unterste Kopf
                            chrPlow = headLow.chromaticPitch()
                            chrPhigh = headLow.chromaticPitch()
                            #messageBox('stop',str(chrPlow))
                            pR = pRt[trTo] # PitchRange
                            pLow = pR[0]
                            pHigh = pR[1]
                            if noHeads > 1:
                                headHigh = obj.head(noHeads-1)
                                chrPhigh = headHigh.chromaticPitch()
                                #messageBox('stop',str(chrPhigh))
                            if (chrPlow < pLow):# or (chrP > pHigh): # !!! durch "setColor" z.B 'b' -> # !!!
                                obj.setColor(B)
                                indColor = 1
                            elif (chrPhigh > pHigh):# 
                                obj.setColor(R)
                                indColor = 1
                            else:
                                obj.setColor(Bl) # wenn farbige Noten durch Oktavierung wieder zu schwarzen werden müssen
                                       

            # Setzen der Vorspiel-Transposition und eventuelles Oktavieren der Noten außerhalb des Tonbereichs des Zielinstruments 
            #=====================================================================================================================
           
            activeScore().write(tempInput) # Partitur wieder einlesen

            OctaveClass(tempInput, tempOutput) # Funktions-Aufruf

            activeScore().read(tempOutput) # Ergebnis als aktive Partitur zurück           

            
            # -------- Schluss-Anzeige --------- #

            Instrument = instrList[toInstr.value()]
            staff = staffList[selStaff.value()]
            
            if indColor == 1:
                
                if indOktaviert == 0:
                    messageBox('Achtung: Der Standard-Tonumfang für  >'+str(Instrument)+'<  wurde nicht eingehalten!',
                                       '                     Die  >farbigen Noten<  liegen außerhalb des Standard-Tonbereichs von  >'+str(Instrument)+'<')
                else:
                    messageBox('Achtung: Der Standard-Tonumfang für  >'+str(Instrument)+'<  wurde nicht eingehalten!',
                            'Die  >farbigen Noten<  lagen außerhalb des Standard-Tonbereichs von  >'+str(Instrument)+'<  und wurden auf Wunsch oktaviert')
            else:
                messageBox('             Alle Noten von  >'+Instrument+'<  liegen im Standard-Tonumfang', str(
                                   'geprüft wurde in den Notenzeilen  >'+staff+'<  ("Beschreibung" der Notenzeile im capella-Mustersystem)'))
            os.remove(tempInput)
            os.remove(tempOutput)

