# -*- coding: ISO-8859-1 -*-
""" capellaScript -- (c) Paul Villiger
>>> Der Radiergummi

    Mit diesem Skript können Grafikobjekte einer Partitur gezielt gelöscht werden.
    Elemente innerhalb einer Gruppe können nicht gelöscht werden, dazu muss die Gruppe
    zuerst aufgelöst werden (Gruppen auflösen).
    
<<<

Löschobjekte:
    - Artikulationszeichen 
    - Bindebogen
    - Capellazeichen (alle Zeichen mit capella Font)
    - Cresc. und Decresc.
    - Dynamikzeichen (ppp...fff)
    - Einfachtexte
    - Gallerieelemente
    - Geometrische Objekte (Linie, Kreis, ...)
    - Gittarrensymbole
    - Gruppen auflösen
    - Gruppierte Elemente
    - Haltebogen
    - Kreise und Ellipsen
    - Liedtexte
    - Linien
    - Metagrafiken
    - Notenliniensymbol
    - Pageobjekte
    - Polygone
    - Rechtecke
    - Schlangenlinien
    - Textfelder
    - Transponierbare Symbole
    - Triller
    - Triolenklammern
    - Voltenklammern
    - Balkenattribute
    - Bindebogenattribute

    - Partitur


Bereich:
    - Ganze Partitur
    - Aktuelle Notenzeile
    - Aktuelle Notenzeile
    - Aktuelles System
    - Aktuelle Stimme

History: 10.09.2006 - Erste Ausgabe
         16.09.2006 - Absturz wenn drawObjects nicht gelöscht werden
         10.12.2007 - Absturz bei Staff ganze Partitur
         07.08.2010 - Fehler bei tie
         26.11.2010 - pageObjects werden nicht gelöscht (capella Problem)
         22.01.2012 - Balken- und Bindebogenattribute löschen
         04.05.2012 - Fehler bei Systemauswahl


Bemerkungen und Wünsche bitte an: villpaul(a)bluewin.ch

"""

from xml.dom.minidom import parseString, NodeList, Node, Element
from caplib.capDOM import ScoreChange
import tempfile, string, new

def latin1(u):
    return u.encode('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)

def getElementObjects(objList):  # returns a List
    newList = NodeList()
    for n in range(objList.length):
        if objList[n].nodeType == objList[n].ELEMENT_NODE:
            newList.append(objList[n])
    return newList

def getCursor():
    sel = curSelection()
    result = None
    if sel == 0:
        messageBox('Fehler', 'keine aktive Partitur')
        return result
    result = sel
    return result

def deleteScore(score):
    systems = score.gotoChild('systems')
    sy = 0
    for system in systems.getElementsByTagName('system'):
        if sy > 0:
            system.parentNode.removeChild(system)
        else:
            system.setAttribute('justified','false')
        sy += 1
    st = 0
    for staff in systems.getElementsByTagName('staff'):
        if st > 0:
            staff.parentNode.removeChild(staff)
        st += 1
    vo = 0        
    for voice in systems.getElementsByTagName('voice'):
        if vo > 0:
            voice.parentNode.removeChild(voice)
        vo += 1
    for noteObjects in systems.getElementsByTagName('noteObjects'):
        voice = noteObjects.parentNode
        noteObjects.parentNode.removeChild(noteObjects)
        voice.gotoChild('noteObjects')

def deleteGallery(score):
    for gallery in score.getElementsByTagName('gallery'):
        gallery.parentNode.removeChild(gallery)

def deletePageObjects(score):
    for pageObjects in score.getElementsByTagName('pageObjects'):
        # wenn pageObjects gelösacht werden sind sie in capella immer noch vorhanden !!!!
        for drawObj in pageObjects.getElementsByTagName('drawObj'):
            drawObj.parentNode.removeChild(drawObj)

def deleteText(voice):
    for drawObjects in voice.getElementsByTagName('drawObjects'):
        drwObjs = getElementObjects(drawObjects.childNodes)
        for drawObj in drwObjs:
            if drawObj.tagName == 'drawObj':
                for obj in getElementObjects(drawObj.childNodes):
                    if obj.tagName == 'text':
                        font = obj.gotoChild('font')
                        if 'capella' not in font.getAttribute('face'):
                            drawObj.parentNode.removeChild(drawObj)
                            continue

def deleteCapellaText(voice):
    for drawObjects in voice.getElementsByTagName('drawObjects'):
        drwObjs = getElementObjects(drawObjects.childNodes)
        for drawObj in drwObjs:
            if drawObj.tagName == 'drawObj':
                for obj in getElementObjects(drawObj.childNodes):
                    if obj.tagName == 'text':
                        font = obj.gotoChild('font')
                        if 'capella' in font.getAttribute('face'):
                            drawObj.parentNode.removeChild(drawObj)
                            continue

def deleteDynamicSymbols(voice):
    for drawObjects in voice.getElementsByTagName('drawObjects'):
        drwObjs = getElementObjects(drawObjects.childNodes)
        for drawObj in drwObjs:
            if drawObj.tagName == 'drawObj':
                for obj in getElementObjects(drawObj.childNodes):
                    if obj.tagName == 'text':
                        font = obj.gotoChild('font')
                        if 'capella' in font.getAttribute('face'):
                            for content in obj.getElementsByTagName('content'):
                                if latin1(content.firstChild.nodeValue) in ('f','g','h','p','q','r','j','i','s','|','z','{'):
                                    drawObj.parentNode.removeChild(drawObj)

def deleteArticulations(voice):
    for drawObjects in voice.getElementsByTagName('drawObjects'):
        drwObjs = getElementObjects(drawObjects.childNodes)
        for drawObj in drwObjs:
            if drawObj.tagName == 'drawObj':
                for obj in getElementObjects(drawObj.childNodes):
                    if obj.tagName == 'text':
                        font = obj.gotoChild('font')
                        if 'capella' in font.getAttribute('face'):
                            for content in obj.getElementsByTagName('content'):
                                if latin1(content.firstChild.nodeValue) in ( chr(200), chr(201), chr(202), chr(203), chr(204), chr(205), chr(206), chr(207), chr(180) ):
                                    drawObj.parentNode.removeChild(drawObj)
                                    
    for articulation in voice.getElementsByTagName('articulation'):
        articulation.parentNode.removeChild(articulation)

def deleteSimpleObject(voice, simpleObject):
    for drawObjects in voice.getElementsByTagName('drawObjects'):
        drwObjs = getElementObjects(drawObjects.childNodes)
        for drawObj in drwObjs:
            if drawObj.tagName == 'drawObj':
                for obj in getElementObjects(drawObj.childNodes):
                    if obj.tagName == simpleObject:
                        drawObj.parentNode.removeChild(drawObj)
                        continue

def deleteRichText(voice):
    deleteSimpleObject(voice, 'richText')

def deleteWedge(voice):
    deleteSimpleObject(voice, 'wedge')

def deleteGroup(voice):
    deleteSimpleObject(voice, 'group')

def deleteTransposable(voice):
    deleteSimpleObject(voice, 'transposable')

def deleteTrill(voice):
    deleteSimpleObject(voice, 'trill')

def deleteWavyLine(voice):
    deleteSimpleObject(voice, 'wavyLine')

def deleteGuitar(voice):
    deleteSimpleObject(voice, 'guitar')

def deleteLine(voice):
    deleteSimpleObject(voice, 'line')

def deleteRectangle(voice):
    deleteSimpleObject(voice, 'rectangle')
                    
def deleteEllipse(voice):
    deleteSimpleObject(voice, 'ellipse')

def deletePolygon(voice):
    deleteSimpleObject(voice, 'polygon')

def deleteGeometricalObjects(voice):
    deleteLine(voice)
    deleteRectangle(voice)
    deleteEllipse(voice)
    deletePolygon(voice)

def deleteNotelines(voice):
    deleteSimpleObject(voice, 'notelines')

def deleteSlur(voice):
    deleteSimpleObject(voice, 'slur')

def deleteVolta(voice):
    deleteSimpleObject(voice, 'volta')

def deleteBracket(voice):
    deleteSimpleObject(voice, 'bracket')

def deleteMetafile(voice):
    deleteSimpleObject(voice, 'metafile')

def deleteLyric(voice):
    for lyric in voice.getElementsByTagName('lyric'):
        lyric.parentNode.removeChild(lyric)

def deleteTie(voice):
    for tie in voice.getElementsByTagName('tie'):
        tie.parentNode.removeChild(tie)

def removeGroups(voice):
    for drawObjects in voice.getElementsByTagName('drawObjects'):
        # 1. Schritt: alle drawObj an drawObjects hängen
        for drawObj in drawObjects.getElementsByTagName('drawObj'):
            drawObjects.appendChild(drawObj.parentNode.removeChild(drawObj))
        # 2. Schritt: alle drawObj mit group löschen
        for drawObj in drawObjects.getElementsByTagName('drawObj'):
            if drawObj.getElementsByTagName('group'):
                drawObj.parentNode.removeChild(drawObj)

def removeEmptyDrawObjects(voice):
    # drawObjects müssen auch entfernt werden, sonst stürzt capella u.U. ab
    for drawObjects in voice.getElementsByTagName('drawObjects'):
        if not drawObjects.getElementsByTagName('drawObj'):
            drawObjects.parentNode.removeChild(drawObjects)

def deleteBeamSettings(voice):
    for chord in voice.getElementsByTagName('chord'):
        for stem in chord.getElementsByTagName('stem'):
            stem.parentNode.removeChild(stem)
        for beam in chord.getElementsByTagName('beam'):
            beam.parentNode.removeChild(beam)

def deleteSlurSettings(voice):
    for slur in voice.getElementsByTagName('slur'):
        for att in ['x1','y1','x2','y2','x3','y3','x4','y4']:
            if slur.hasAttribute(att):
                slur.removeAttribute(att)

        for basic in slur.parentNode.getElementsByTagName('basic'):
            if basic.hasAttribute('noAdjust'):
                basic.removeAttribute('noAdjust')

procedures ={ 0 : ['keine Aktion', None],
              1 : ['Partitur', deleteScore],
              2 : ['Artikulationszeichen', deleteArticulations],
              3 : ['Bindebogen', deleteSlur],
              4 : ['Capellazeichen', deleteCapellaText],
              5 : ['Cresc. und Decresc.', deleteWedge],
              6 : ['Dynamikzeichen (ppp...fff)', deleteDynamicSymbols],
              7 : ['Einfachtexte', deleteText],
              8 : ['Gallerieelemente', deleteGallery],
              9 : ['Geometrische Objekte', deleteGeometricalObjects],
             10 : ['Gittarrensymbole', deleteGuitar],
             11 : ['Gruppen auflösen', removeGroups],
             12 : ['Gruppierte Elemente', deleteGroup],
             13 : ['Haltebogen', deleteTie],
             14 : ['Kreise und Ellipsen', deleteEllipse],
             15 : ['Liedtexte', deleteLyric],
             16 : ['Linien', deleteLine],
             17 : ['Metagrafiken', deleteMetafile],
             18 : ['Notenliniensymbol', deleteNotelines],
             19 : ['Pageobjekte', deletePageObjects],
             20 : ['Polygone', deletePolygon],
             21 : ['Rechtecke', deleteRectangle],
             22 : ['Schlangenlinien', deleteWavyLine],
             23 : ['Textfelder', deleteRichText],
             24 : ['Transponierbare Symbole', deleteTransposable],
             25 : ['Triller', deleteTrill],
             26 : ['Triolenklammern', deleteBracket],
             27 : ['Voltenklammern', deleteVolta],
             28 : ['Balkenattribute', deleteBeamSettings],
             29 : ['Bindebogenattribute', deleteSlurSettings]
            }

proceduresText = []
for txt, val in procedures.values():
    proceduresText.append(txt)

rangeSel   = { 0 : ['Ganze Partitur', 'score' ],
               1 : ['Aktuelle Notenzeile', 'staff' ],
               2 : ['Aktuelle Notenzeile ganze Partitur', 'scoreStaff' ],
               3 : ['Aktuelles System', 'system'],
               4 : ['Aktuelle Stimme', 'voice']
            }
rangeText = []
for txt, val in rangeSel.values():
    rangeText.append(txt)

def getDialogValues():
    getProcedure = ComboBox(proceduresText, value = 0, width = 33)
    getRange     = Radio(rangeText, value = 0, text = '  Wo soll gelöscht werden?  ')
    dlg = Dialog(' Der Radiergummi  ',
                   VBox([Label('Was soll gelöscht werden ?'),
                         getProcedure,
                         getRange], padding = 8))
    if dlg.run():
        p = procedures[getProcedure.value()][1]
        r = rangeSel[getRange.value()][1]
        return r, p
    else:
        return None, None


def changeDoc(score):
    scoreRange, deleteObject = getDialogValues()
    if deleteObject == None:
        return
    
    if deleteObject in [deleteScore, deleteGallery, deletePageObjects]:
        deleteObject(score)
        return

    sel = getCursor()
    if not sel:
        return
    (sy1, st1,vo1,ob1), (sy2,st2,vo2,ob2) = sel

    if scoreRange == 'score':  # ganze Partitur
        for voice in score.getElementsByTagName('voice'):
            deleteObject(voice)
            removeEmptyDrawObjects(voice)
        return        
    
    staffLayout = None
    if scoreRange == 'scoreStaff':  # Staff ganze Partitur
        sy = 0
        for system in score.getElementsByTagName('system'):
            st = 0
            for staff in system.getElementsByTagName('staff'):
                if sy == sy1 and st == st1:
                    staffLayout = staff.getAttribute('layout')
                st += 1
            sy += 1
        for staff in score.getElementsByTagName('staff'):
            if staffLayout == staff.getAttribute('layout'):
                for voice in staff.getElementsByTagName('voice'):
                    deleteObject(voice)
                    removeEmptyDrawObjects(voice)
                
        return

    sy = 0
    for system in score.getElementsByTagName('system'):
        if scoreRange == 'system': 
            if sy == sy1:
                for voice in system.getElementsByTagName('voice'):
                    deleteObject(voice)
                    removeEmptyDrawObjects(voice)
                return
        else:        
            st = 0
            for staff in system.getElementsByTagName('staff'):
                if scoreRange == 'staff':
                    if sy == sy1 and st == st1:
                        for voice in staff.getElementsByTagName('voice'):
                            deleteObject(voice)
                            removeEmptyDrawObjects(voice)
                        return
                else:
                    vo = 0
                    for voice in staff.getElementsByTagName('voice'):
                        if scoreRange == 'voice':  
                            if sy == sy1 and st == st1 and vo == vo1:
                                deleteObject(voice)
                                removeEmptyDrawObjects(voice)
                                return
                        vo += 1
                st += 1
        sy +=1


class ScoreChange(ScoreChange):

    def changeScore(self, score):
        changeDoc(score)

if activeScore():
    activeScore().registerUndo("Der Radiergummi")
    tempInput = tempfile.mktemp('.capx')
    tempOutput = tempfile.mktemp('.capx')
    activeScore().write(tempInput)

    ScoreChange(tempInput, tempOutput)

    activeScore().read(tempOutput)
    os.remove(tempInput)
    os.remove(tempOutput)



