# -*- coding: ISO-8859-1 -*-
""" capellaScript
>>> Bindebögen und Artikulationszeichen ausrichten
Über ein Dialogfenster kann man die folgenden Aktionen durchführen:
1. Tenutostriche löschen, 
2. Bindebögen von staccato-Punkten absetzen, 
3. sonstige Artikulationszeichen vom Bogen absetzen, 
4. Bindebögen an Vorschlagnoten ausrichten 
5. Bindebögen bei Zeilenumbrüchen ausrichten

Einschränkung: Das Skript funktioniert nur dann korrekt, wenn im Dialog "Stimme"  
(Umschalt+Strg+Eingabetaste) die Halsrichtung auf "je nach Lage" gesetzt  
ist. Das ist in mehrstimmigen Zeilen evt. nicht der Fall.  
<<<
History:
17.11.2004  Erstversion
26.11.2004  Funktion 5. hinzugefügt
"""
# 1.11.2004 Hartmut Lemmel


from caplib.capDOM import ScoreChange
import tempfile

def getElement(parent, *children):
     """ liefert Kind/Enkel/Urenkel..., z.B. parent.child0.child1.child2... """
     p = parent
     for c in children:
         found = 0
         for n in p.childNodes:
             if n.nodeType == parent.ELEMENT_NODE and n.tagName == c:
                 p = n
                 found = 1
                 break
         if not found: return 0
     return p


def latin1_e(u):
    return u.encode('Latin-1')
def latin1_d(u):
    return u.decode('Latin-1')

class ScoreChange(ScoreChange):  
    def changeScore(self, score):
     global fixportamento, fixaccent, fixgrace, tenutokill, fixlinebreak
     
     for el in score.getElementsByTagName('noteObjects'):   # entspricht einer Notenzeile
      chords = el.getElementsByTagName('chord')
      for i in range(chords.length):
       a = getElement(chords[i],'articulation')
       if a and tenutokill and (a.getAttribute('type')=='tenuto'):
           chords[i].removeChild(a)          
       drawObjects=getElement(chords[i],'drawObjects')
       if drawObjects:
        for drawObj in drawObjects.getElementsByTagName('drawObj'):
         slur = getElement(drawObj,'slur')
         if slur:
           # Note mit Bindebogen gefunden
           y1=float(slur.getAttribute('y1'))
           y2=float(slur.getAttribute('y2'))
           y3=float(slur.getAttribute('y3'))
           y4=float(slur.getAttribute('y4'))           
           slurbelow = 0
           if y2>y1:
              slurbelow = 1
           noterange = 0 
           basic = getElement(drawObj,'basic')
           if basic and basic.getAttribute('noteRange')!='':
             noterange = int(basic.getAttribute('noteRange'))

           if fixlinebreak:
             # Bogen an der ersten Note einer Zeile
             if (i==0 and noterange==0) or (i==chords.length-1):
                 if i==0: # Zeilenanfang
                      slur.setAttribute('x1', str((12-0 )/32.0))
                      slur.setAttribute('x2', str((12-10)/32.0))
                      slur.setAttribute('x3', str((12-52)/32.0))
                      slur.setAttribute('x4', str((12-68)/32.0))
                 else:    # Zeilenende
                      slur.setAttribute('x1', str((24+0 )/32.0))
                      slur.setAttribute('x2', str((24+10)/32.0))
                      slur.setAttribute('x3', str((24+52+32)/32.0))
                      slur.setAttribute('x4', str((24+68+32)/32.0))
                 if slurbelow:    
                      slur.setAttribute('y2', str(y1 + 24 /32.0))
                      slur.setAttribute('y3', str(y1 + 32 /32.0))
                      slur.setAttribute('y4', str(y1 + 22 /32.0))
                 else:
                      slur.setAttribute('y2', str(y1 - 24 /32.0))
                      slur.setAttribute('y3', str(y1 - 32 /32.0))
                      slur.setAttribute('y4', str(y1 - 22 /32.0))
             elif i+noterange>=chords.length:
                 x3=float(slur.getAttribute('x3'))
                 x4=float(slur.getAttribute('x4'))
                 x3+=3
                 x4+=3
                 y4=(y3+y4)/2;
                 y3=(y3+y4)/2;
                 slur.setAttribute('x3', str(x3))
                 slur.setAttribute('x4', str(x4))
                 slur.setAttribute('y3', str(y3))
                 slur.setAttribute('y4', str(y4))                

           # Vorschlagnote 
           if fixgrace and (i<chords.length-1):
              displ=getElement(chords[i],'display')
              if displ and (displ.getAttribute('small')=='true'):
               durs=getElement(chords[i],'duration')
               if durs and (durs.getAttribute('noDuration')=='true'):
                durs1=getElement(chords[i+1],'duration')
                if durs1==0 or (durs1.getAttribute('noDuration')=='') or (durs1.getAttribute('noDuration')=='false'):
                  if noterange == 1:
                   stemup=1
                   stem=getElement(chords[i],'stem')
                   if stem and (stem.getAttribute('dir')=='down'):
                      stemup=0
                   if stemup: 
                      slur.setAttribute('x1', '0.59375')  # 19/32
                      slur.setAttribute('y1', '0.46875')  # 15/32
                      slur.setAttribute('x2', '0.84375')  # 27/32
                      slur.setAttribute('y2', '0.84375')  # 27/32
                      slur.setAttribute('x3', '-0.46875') #-15/32
                      slur.setAttribute('y3', '0.84375')  # 27/32
                      slur.setAttribute('x4', '-0.21875') # -7/32
                      slur.setAttribute('y4', '0.46875')  # 15/32
                   else:  
                      slur.setAttribute('x1',  '0.59375') # 19/32
                      slur.setAttribute('y1', '-0.5')     #-16/32
                      slur.setAttribute('x2', '0.84375')  # 27/32
                      slur.setAttribute('y2', '-0.875')   #-28/32
                      slur.setAttribute('x3', '-0.125')   # -4/32
                      slur.setAttribute('y3', '-0.875')   #-28/32
                      slur.setAttribute('x4', '0.125')    #  4/32
                      slur.setAttribute('y4', '-0.5')     #-16/32
                   basic.setAttribute('vertAlign', '1')

           # Note mit Artikulationszeichen?
           a = getElement(chords[i],'articulation')
           if a:
            type=a.getAttribute('type')
            # Note mit Staccatopunkt
            if type == 'staccato':
             if fixportamento:
               if slurbelow:  # Bogen nach unten gewölbt
                 dy1=0.5
                 dy4=0.5
                 if y1<2.5 and (int(round(2*y1)) & 1)==0:
                   dy1=1;
                 if y4<2.5 and (int(round(2*y4)) & 1)==0:
                   dy4=1;
               else:       # Bogen nach oben gewölbt
                 dy1=-0.5
                 dy4=-0.5
                 if y1>-2.5 and (int(round(2*y1)) & 1)==0: # Bogen genau auf einer Linie -> 
                   dy1=-1;  # noch weiter absetzen, weil der Staccatopunkt immer im Linienzwischenraum sitzt
                 if y4>-2.5 and (int(round(2*y4)) & 1)==0:
                   dy4=-1;                    
               y1+=dy1
               y2+=dy1
               y3+=dy4
               y4+=dy4
               if y1==y4: # Beide Endpunkte gleich hoch -> Bogen symmetrisieren
                 y2=y3=(y2+y3)/2
                 x1=float(slur.getAttribute('x1'))
                 x2=float(slur.getAttribute('x2'))
                 x3=float(slur.getAttribute('x3'))
                 x4=float(slur.getAttribute('x4'))
                 dx=(x2-x1+x4-x3)/2;
                 slur.setAttribute('x2',str(x1+dx))
                 slur.setAttribute('x3',str(x4-dx))
               # y-Werte schreiben
               slur.setAttribute('y1',str(y1))
               slur.setAttribute('y2',str(y2))
               slur.setAttribute('y3',str(y3))
               slur.setAttribute('y4',str(y4))

            # Note mit Artikulationszeichen ungleich staccato
            elif fixaccent:
               if type == 'tenuto':
                   sym='\xC8'
                   dx=0.625
               else: 
                   if type == 'staccatissimo': 
                       if slurbelow: sym='\xCE'
                       else:         sym='\xC9'
                   elif type == 'normalAccent': sym='\xCA'
                   elif type == 'strongAccent': sym='\xCB'
                   elif type == 'weakBeat': sym='\xCC'
                   elif type == 'strongBeat': 
                       if slurbelow: sym='\xCF'
                       else:         sym='\xCD'
                   else: continue
                   if slurbelow:
                       if y1<2: continue
                   else:
                       if y1>-2: continue
                   dx=0.7 
               if slurbelow:
                   dy=2
                   if y1<2:
                       if (int(round(2*y1)) & 1)==0: dy=2.5
                   elif y4>y1: dy=2.2
               else:
                   dy=-2
                   if y1>-2:
                       if (int(round(2*y1)) & 1)==0: dy=-2.5
                   elif y4<y1: dy=-2.2
               chords[i].removeChild(a)
               newobj = self.doc.createElement('drawObj')
               drawObjects.appendChild(newobj) 
               txt = self.doc.createElement('text')
               newobj.appendChild(txt)
               txt.setAttribute('x',str(dx))
               txt.setAttribute('y',str(dy))
               txt.setAttribute('align','center')
               cnt = self.doc.createElement('content')
               txt.appendChild(cnt)
               cnt.appendChild(self.doc.createTextNode(latin1_d(sym)))
               font = self.doc.createElement('font')
               txt.appendChild(font)
               font.setAttribute('face','capella3')
               font.setAttribute('height','18')
               font.setAttribute('charSet','2')
               font.setAttribute('pitchAndFamily','2')
               basic = self.doc.createElement('basic')
               newobj.appendChild(basic)
               basic.setAttribute('vertAlign','1')


def scriptDialog():
    global fixportamento, fixaccent, fixgrace, fixlinebreak, tenutokill
    options = ScriptOptions() 
    opt = options.get()  

    chxtenutokill = CheckBox('Tenutostriche löschen', value=int(opt.get('tenutokill','0')))
    chxportamento = CheckBox('Bindebögen von staccato-Punkten absetzen', value=int(opt.get('fixportamento','1')))
    stcportamento = Label('    (Nur einmal nach dem automatischen Bogenausrichten ausführen!)\n ')
    chxaccent     = CheckBox('sonstige Artikulationszeichen vom Bogen absetzen', value=int(opt.get('fixaccent','1')))
    stcaccent     = Label('    (Die Artikulationszeichen werden in Textobjekte umgewandelt.\n    Es werden nur Artik.zeichen am Bogenanfang berücksichtigt.)\n ')
    chxgrace      = CheckBox('Bindebögen an Vorschlagnoten ausrichten', value=int(opt.get('fixgrace','1')))
    chxlinebreak  = CheckBox('Bindebögen am Zeilenanfang/ende ausrichten', value=int(opt.get('fixlinebreak','1')))
    stclinebreak  = Label('    (Nur einmal nach dem automatischen Bogenausrichten ausführen!)\n ')
    box = VBox([chxtenutokill,chxportamento,stcportamento,chxaccent,stcaccent,chxgrace,chxlinebreak,stclinebreak],padding=1)
    dlg = Dialog('Bindebögen und Artikulationszeichen ausrichten', box)

    if dlg.run():
        tenutokill=chxtenutokill.value();
        fixportamento=chxportamento.value();
        fixaccent=chxaccent.value();
        fixgrace=chxgrace.value();
        fixlinebreak=chxlinebreak.value();
        opt.update(dict(tenutokill=tenutokill, fixportamento=fixportamento,
                        fixaccent=fixaccent, fixgrace=fixgrace,
                        fixlinebreak=fixlinebreak))
        options.set(opt)
        return True
    else:
        return False
        
if activeScore():
    if scriptDialog():    
        activeScore().registerUndo("Bindebogen ausrichten")
        tempInput = tempfile.mktemp('.capx')
        tempOutput = tempfile.mktemp('.capx')
        activeScore().write(tempInput)

        ScoreChange(tempInput, tempOutput)

        activeScore().read(tempOutput)
        os.remove(tempInput)
        os.remove(tempOutput)

