# -*- coding: ISO-8859-1 -*-
""" capellaScript -- (c) Paul Villiger
>>> Tempobezeichnung

    Mit diesem Skript können Tempobezeichnungen erstellt werden.|
    Die Eingabe erfolgt über einen Zeichenfolge, welche vom Skript interpretiert wird:||

    Notenwerte: 5 1 2 4 8 6 3 --> Brevis bis 1/32|
    Noten innerhalb von '[]' werden verbalkt, Halbe Balken mit '-' vor der Note|
    Punktierung mit '.'|
    Gleichheitszeichen mit '=', Bsp [8.-6]="120"|
    Ziffern müssen in Hochkomma stehen|
    Tuplets mit T+Ziffer vor der Note: Bsp: [8T388]|
    Pausen mit P+Notenwert|
    ' ' --> eine Einheit Abstand|
    '+' --> Das Nachfolgende Element wird um eine halbe Einheit verschoben

<<<

History:  02.06.2008 Erstausgabe
          06.06.2008 Normal-, Expertenmodus
          07.06.2008 - ' ' und '+'
                     - Absturz bei leerer Gruppe
                     - Scriptoptions mit '=' geht nicht
          08.06.2008 - Alle Einstellungen werden gespeichert
          21.05.2012 - latin1_d Anpassung an unicode

"""

# Notenbreite
noteSizeX = [0.0,        # 0
             2.0,        # 1
             1.5,        # 2
             1.5,        # 3
             1.5,        # 4
             2.2,        # 5
             1.5,        # 6
             0.0,        # 7
             1.5]        # 8

noteChars = ['',
            chr(227),      # 1
            chr(174),      # 2
            chr(168),      # 3 
            chr(162),      # 4 
            chr(225),      # 5 
            chr(166),      # 6 
            '',            # 7 
            chr(164)]      # 8
noteCharDot     = chr(46)
noteCharEqual   = chr(61)

restChars = ['',
            chr(73),    # 1
            chr(74),    # 2
            chr(78),    # 3 
            chr(75),    # 4 
            '',         # 5 
            chr(77),    # 6 
            '',         # 7 
            chr(76)]    # 8




import new
from xml.dom.minidom import NodeList, Node, Element
from xml.dom.minidom import parseString, NodeList, Node, Element

def latin1_d(u):
    if isinstance(u, unicode):
        return u
    return u.decode('Latin-1')

doc = parseString('<score/>')

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)


class TempoBezeichnung:
    def __init__(self):
        self.spacingFactor  = 1.2
        self.sizeFactor     = 0.5
        self.barLineStack   = []
        self.slurStack      = []
        self.currentX       = 0.0
        self.currentY       = -4.0
        self.halfBarLine    = False
        self.initDone       = False
        self.dx             = 0.0
        

    def initChord(self, chord):
        self.chord = chord
        
    def initGroup(self):
        if self.initDone:
            return
        else:
            self.initDone = True
        drawObjects = self.chord.gotoChild('drawObjects')
        drawObj = drawObjects.gotoChild('drawObj', True)
        self.group = drawObj.gotoChild('group')

    def writeChar(self,noteChar, dx=0, dy=0):
        self.initGroup()
        newDrawObj = self.group.gotoChild('drawObj', True)

        text = newDrawObj.gotoChild('text')
        text.setAttribute('x', str(self.currentX + dx + self.dx))
        text.setAttribute('y', str(self.currentY + dy))

        font = text.gotoChild('font')
        font.setAttribute('face','capella3')
        font.setAttribute('height', str(18.0 * self.sizeFactor))
        font.setAttribute('charSet','2')
        font.setAttribute('pitchAndFamily','2')
        
        content = text.gotoChild('content')
        textNode = doc.createTextNode( latin1_d(noteChar) )
        content.appendChild(textNode)
        # self.dx = 0.0
                          

    def appendNote(self, duration):
        if self.barLineStack and duration in '8634':
            noteChar = noteChars[4]
        elif duration in '1248635':
            noteChar = noteChars[int(duration)]
        if noteChar == '':
            return

        self.writeChar(noteChar)

        self.currentX += noteSizeX[int(duration)] * self.spacingFactor * self.sizeFactor + self.dx
        self.dx = 0.0

    def appendRest(self, duration):

        if duration in '124863':
            restChar = restChars[int(duration)]
        else:
            return

        self.writeChar(restChar, dx = 0.3 * self.sizeFactor)

        self.currentX += 1.5 * self.spacingFactor * self.sizeFactor + self.dx
        self.dx = 0.0
            
    def appendDot(self):
        self.writeChar(noteCharDot, dx = -1.5 * self.spacingFactor * self.sizeFactor)
        self.currentX += 0.5 * self.spacingFactor * self.sizeFactor + self.dx
        self.dx = 0.0

    def appendEqual(self):
        self.writeChar(noteCharEqual)
        self.currentX += 2.2 * self.spacingFactor *self.sizeFactor + self.dx
        self.dx = 0.0

    def writeDigits(self, digits):
        self.writeChar(digits)
        self.currentX += len(digits) * 1.5 * self.sizeFactor +self.dx
        self.dx = 0.0

    def writeTuple(self, digit):
        self.writeChar(chr( ord(digit) + 198), dx = -0.2 * self.sizeFactor, dy = 1.5 * self.sizeFactor)

    def drawBar(self, half = False):
        if not self.barLineStack:
            return

        pos2 = self.currentX - 0.625 *self.sizeFactor * self.spacingFactor
        pos2 = self.currentX - noteSizeX[4] * self.sizeFactor * self.spacingFactor + 1.1 * self.sizeFactor
        if half:
            pos1 = pos2 - 1.0 * self.sizeFactor
        else:
            pos1 = self.barLineStack.pop() + 1.1 * self.sizeFactor

        count = len(self.barLineStack)

        self.initGroup()
        
        newDrawObj = self.group.gotoChild('drawObj', True)
        rectangle = newDrawObj.gotoChild('rectangle')
        rectangle.setAttribute('lineWidth','0')
        rectangle.setAttribute('filled','true')
        rectangle.setAttribute('x1',str(pos1))
        rectangle.setAttribute('y1',str(self.currentY - (3.5 - count) * self.sizeFactor))
        rectangle.setAttribute('x2',str(pos2))
        rectangle.setAttribute('y2',str(self.currentY - (2.9 - count) * self.sizeFactor))
        
    def drawSlur(self):
        pass
    
    def parseTempo(self, tempoString, chord):
        self.initChord(chord)
        
        ts = tempoString
        while ts <> '':
            ts0 = ts[0]
            if ts0 == '[':
                self.barLineStack.append(self.currentX)
            elif ts0 in '1248635':
                self.appendNote(ts0)
                if self.halfBarLine:
                    self.drawBar(True)
                    self.halfBarLine = False
            elif ts0 == ' ':
                self.currentX += 1.5 * self.spacingFactor * self.sizeFactor + self.dx
                self.dx = 0.0

            elif ts0 == '=':
                self.appendEqual()
            elif ts0 == ']':
                self.drawBar()
            elif ts0 == '-':
                self.halfBarLine = True
            elif ts0 == '+':
                self.dx += 0.75 * self.spacingFactor * self.sizeFactor
            elif ts0 == '.':
                self.appendDot()
            elif ts0 == '(':
                self.slurStack.append(self.currentX)
            elif ts0 == ')':
                self.drawSlur()
            elif ts0 in 'tT':
                ts = ts[1:]
                if ts <> '' and ts[0].isdigit():
                    self.writeTuple(ts[0])
                    self.dx = 0.0
            elif ts0 in 'pP':
                ts = ts[1:]
                if ts <> '' and ts[0].isdigit():
                    self.appendRest(ts[0])
            elif ts0 == "'" or ts0 == '"':
                s = ''
                ts = ts[1:]
                while ts <> '' and ts[0].isdigit():
                    s += ts[0]
                    ts = ts[1:]
                if s <> '':
                    self.writeDigits(s)

            ts = ts[1:]            
            
tempo = TempoBezeichnung()

options = ScriptOptions() 
opt = options.get()
optTempoString = opt.get('tempoString', '4="120"')
optTempoString = optTempoString.replace('#','=')
optSizeFactor = opt.get('sizeFactor', '0.5')
optSpacingFactor = opt.get('spacingFactor', '1.0')
optNoteSelect = eval(opt.get('noteSelect','3'))
optDotSelect = eval(opt.get('dotSelect', 'False'))
optTempoSelect = opt.get('tempoSelect','120')
optExpertSelect = eval(opt.get('expertSelect', 'False'))


##### Dialog ##########################################

dlgTempoString = Edit(value = optTempoString, width = 30)
dlgSizeFactor  = Edit(value = optSizeFactor  , width = 20)
dlgSpacingFactor  = Edit(value = optSpacingFactor  , width = 20)
dlgNoteSelect  = ComboBox(['Brevis', 'Ganze','Halbe','Viertel','Achtel','16tel','32tel'], value = optNoteSelect, width = 11)
dlgDotSelect   = CheckBox('  =', value = optDotSelect, width = 5)
dlgTempoSelect = Edit(value = optTempoSelect, width = 10)
dlgExpertSelect = CheckBox('', value = optExpertSelect)

dlg = Dialog('Tempobezeichnungen',
          VBox([
             VBox(
                  [HBox([Label('Note', width = 13),
                         Label('Punkt', width = 5),
                         Label('Tempo', width = 40)
                         ]),
                   HBox([dlgNoteSelect,
                         Label(' ', width=2),
                         dlgDotSelect,
                         dlgTempoSelect
                         ])
                   ], text= 'Normalmodus:  ',  padding = 5
                  ),

              Label(' '),
             
              VBox([HBox([dlgExpertSelect,
                          Label('   Eingabe im Expertenmodus')
                          ]),
                    HBox([dlgTempoString,
                          Label('   Tempobezeichnung')
                          ]),
                    Label(' ', width = 60),
                    HBox([Label('Noten: 5 1 2 4 8 6 3 (Brevis bis 1/32)', width = 30),
                          Label('Zahlen in Hochkomma: ="120"')
                          ]),
                    HBox([Label('Balken mit [] : [88]=[[66]8]', width = 30),
                          Label('Halbe Balken mit - : [8.-6]')
                          ]),
                    HBox([Label('Pause mit Px: [8P6-6] ', width = 30),
                          Label('Tuplet mit Tx: [8T388]=[88]')
                          ]),
                    
                    ],
                   text = 'Expertenmodus:   ', padding = 5
                   ),
             Label(' '),
             HBox([Label('Grössenfaktor:', width = 15),
                   dlgSizeFactor,
                   Label('  (1.0 = normale Note)')]),
             HBox([Label('Breitenfaktor:', width = 15),
                   dlgSpacingFactor]),
             Label(' ')
             ]))

ok = True
if dlg.run():
    opt['expertSelect'] = str(dlgExpertSelect.value())
    opt['dotSelect']    = str(dlgDotSelect.value())
    opt['noteSelect']   = str(dlgNoteSelect.value())
    opt['tempoSelect']  = dlgTempoString.value()
    opt['tempoString']  = dlgTempoString.value()
    opt['tempoString'] = opt['tempoString'].replace('=','#')
    
    if not dlgExpertSelect.value():
        dlgTempoString = '5124863'[dlgNoteSelect.value()]
        if dlgDotSelect.value():
            dlgTempoString += '.'
        dlgTempoString += '="%s"' % dlgTempoSelect.value()
    else:
        dlgTempoString = dlgTempoString.value()
    try:
        val = dlgSizeFactor.value()
        sf = float(val)
        tempo.sizeFactor = sf
        opt['sizeFactor'] = val
        val = dlgSpacingFactor.value()
        sf = float(val)
        tempo.spacingFactor *= sf
        opt['spacingFactor'] = val
    except:
        messageBox('Fehler','Bitte gültige Zahl eingeben: %s !' % val)
        ok = False

    options.set(opt)
        
else:
    ok =False


def changeDoc(score):
    try:
        sy, st, vo, ob = curSelection()[0]
    except:
        messageBox('Fehler', 'keine gültige Auswahl!')
        return
    system = score.getElementsByTagName('system')[sy]
    staff = system.getElementsByTagName('staff')[st]
    voice = staff.getElementsByTagName('voice')[vo]
    for noteObjects in voice.getElementsByTagName('noteObjects'):
        count = 0
        for obj in noteObjects.childNodes:
            if obj.nodeType == obj.ELEMENT_NODE:
                count += 1
            else:
                continue
            if ob < count and obj.tagName in ['rest','chord']:
                tempo.parseTempo(dlgTempoString, obj)
                return

        messageBox('Achtung','Keine Note hinter Cursor!')
        return
                

# Hauptprogramm:

from caplib.capDOM import ScoreChange
import tempfile

class ScoreChange(ScoreChange):
    def changeScore(self, score):
        changeDoc(score)

if activeScore() and ok:
    activeScore().registerUndo("Tempobezeichnung")
    tempInput = tempfile.mktemp('.capx')
    tempOutput = tempfile.mktemp('.capx')
    activeScore().write(tempInput)
    
    ScoreChange(tempInput, tempOutput)

    activeScore().read(tempOutput)
    os.remove(tempInput)
    os.remove(tempOutput)

