# -*- coding: UTF-8 -*-
""" capellaScript -- (C) 2026 Ken Haiker, acaMusic (www.acamusic.de)
			 Implementation: Brian Schueler
>>> aC.Tremolor
    Rev 0.1.14 (26.07.2026) --- Plugin allgemein für Musiker
	||
	Das aC.Tremolor-Plugin erzeugt aus einer
	|
	1. einzelnen Note (nur Basis-Note), vor die der Cursor bei Plugin-Aufruf gesetzt ist,
	einen monotonen Triller/Tremolo mit einstellbarer Wiederholungsrate (1/8, 1/16, 1/32). Die grafische Darstellung 
	des Tremolos erfolgt in diesem Fall ausschließlich als Abbreviaturnote (Tremolobalken im Notenhals),
	|
	2. Basis-Note, vor die der Cursor bei Plugin-Aufruf gesetzt ist, in Verbindung mit einer auf 
	"ohne Wert" gesetzten rechts daneben liegenden Wechselnote einen aus zwei Tönen 
	bestehenden Tremolo. Es kann eingestellt werden, ob die Darstellung als Abbreviaturnote oder mit Grafikbalken 
	zwischen den beiden beteiligten Noten erfolgen soll.
	||
	Das Plugin ändert (ganz bewusst) keine existierende reguläre Note in eine Wechselnote "ohne Wert". Diese 
	Maßnahme muss manuell erfolgen.
	||
	Ein Tremolo kann neu erzeugt werden, ein bestehender überschrieben, das Tremolo-Attribut an der Basis-Note
	aufgelöst, oder der existierende Tremolo expandiert (die sich hinter ihm befindenden Noten werden sichtbar
	gemacht) werden.
	||
	Ein Handbuch gibt es für das aC.Tremolor-Plugin nicht.
	||
    -------------   www.acaMusic.de   -------------
    ||
    
<<<

History:  Jun 2026 - Erste Version

"""

import pprint
import re
import math
import tempfile
from caplib.capDOM import ScoreChange, childElements
from xml.dom.minidom import NodeList, Node, Element

sys.stdout = open(tempfile.gettempdir()+'capella-ac-tremolor.log', 'w')
tremoloBarTag = '13113-20'

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 gotoChild(self, name, new=False):
	newEl = None
	for child in self.childNodes:
		if child.nodeType == child.ELEMENT_NODE and child.tagName == name:
			newEl = child
			break

	if new == False:
		return newEl

	if newEl == None:
		newEl = doc.createElement(name)
		self.appendChild(newEl)
	return newEl
import new
Node.gotoChild = new.instancemethod(gotoChild,None,Node)

def addNewElementNode(el,tagName):
	# add new Node with tagName "tagName" to el
	global doc
	newChild = doc.createElement(tagName)
	el.appendChild(newChild)
	return newChild

# Get the base child object and the child list of an object
def getChildren(el, name, forcedPlural = None):
	if name == 'staff':
		namePlural = 'staves'
	elif name == 'drawObj':
		namePlural = 'drawObjects'
	else:
		namePlural = name+'s'
	if forcedPlural is not None:
		namePlural = forcedPlural
	plElems = childElements(el, namePlural)
	if len(plElems) > 0:
		plFirstElem = plElems[0]
		retval = childElements(plFirstElem, name)
		return (plFirstElem, retval)
	else:	   
		xElem = el.getElementsByTagName(name)
		if xElem.length > 0:
			x = xElem[0]
			childNodes = getElementObjects(x.childNodes)
			return (x, childNodes)
		else:
			return (None, [])

# Replace a named child tree
def setChildren(el, children, name):
	oldchildren = childElements(el, name)
	for oldchild in oldchildren:
		el.removeChild(oldchild)
	for child in children:
		el.appendChild(child)

# Get the decimal duration of a note/chord/rest
def getObjDuration(noteObj):
	duration = noteObj.getElementsByTagName('duration')
	# Only NoteObjects that have a duration
	if len(duration) > 0:
		noDuration = duration[0].getAttribute('noDuration')
		if noDuration == 'true':
			return 0.0

		baseT = duration[0].getAttribute('base')
		if baseT is None or baseT == "":
			return 0.0
		base = baseT.split('/')
		if len(base) == 2:
			value = float(base[0])/float(base[1])
		else:
			value = float(base[0])

		# Tuplet Note
		tuplet = duration[0].gotoChild('tuplet', False)
		if tuplet is not None:
			count = int(tuplet.getAttribute('count'))
			if count == 3:
				value = value * 2.0 / 3.0

		# Dotted Note
		dots = duration[0].getAttribute('dots')
		if len(dots) > 0:
			for i in range(0,int(dots)):
				value = value + value/2.0
		return value
	return None

# Get the total duration of a voice
def getVoiceTotalTime(voice):
	totalTime = 0.0
	onoteObjects, noteObjects = getChildren(voice, 'noteObjects')
	for obj in noteObjects:
		duration = getObjDuration(obj)
		if duration is not None:
			totalTime = totalTime + duration
	return totalTime

# Get the total duration of a staff
def getStaffTotalTime(staff):
	totalTime = 0.0
	ovoices, voices = getChildren(staff, 'voice')
	for voice in voices:
		tt = getVoiceTotalTime(voice)
		totalTime = max(totalTime, tt)
	return totalTime

# Determine FretIndex color and font size - keyword "Fret"
def getFretColorAndSize(score):
	fretInfoStaves = []
	selected_staff = curSelection()[0][1]

	# Search for Fret Keyword in Note Objects
	osystens, systems = getChildren(score, 'system')
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		staffID = 0
		for staff in staves:	
			ovoices, voices = getChildren(staff, 'voice')
			staffName = staff.getAttribute('layout')
			for voice in voices:
				oObjs, objs = getChildren(voice, 'noteObjects')
				for i in range(0,len(objs)):
					obj = objs[i]
					texts = obj.getElementsByTagName('text')
					if texts.length > 0:
						for text in texts:
							content = ''
							content_t = text.getElementsByTagName('content')[0].firstChild
							if content_t is not None:
								content = content_t.data
							fonts = text.getElementsByTagName('font')
							print('content=',content)
							if fonts.length > 0 and content.lower().replace("!","") == u'fret':
								is_aln = False
								if content.lower() == (u'fret!'):
									is_aln = True
								color = fonts[0].getAttribute('color')
								height = int(fonts[0].getAttribute('height'))
								fretInfoStaves.append((color, height, staffName, staffID, is_aln))
			staffID = staffID + 1
	if len(fretInfoStaves) > 1:
		for fretInfoStaff in fretInfoStaves:
			color, height, staffName, staffID, is_aln = fretInfoStaff
			if (selected_staff == staffID):
				return (color, height, staffName, is_aln)

	elif len(fretInfoStaves) == 1:
		color, height, staffName, staffID, is_aln = fretInfoStaves[0]
		return (color, height, staffName, is_aln)

	return (u'', 0, u'-', False)

def getObjTextObjsByCriteria(noteObj, criteria):
	texts = []
	if noteObj.localName in [ 'chord', 'rest' ]:
		odrawObjs, drawObjs = getChildren(noteObj, 'drawObjects')
		if drawObjs is not None and len(drawObjs) > 0:
			for drawObj in drawObjs:
				if isTextCriteriaDrawObj(drawObj, criteria):
					texts.append(drawObj)
	return texts

def isTextCriteriaDrawObj(drawObj, criteria):
	text = drawObj.gotoChild('text', False)
	richText = drawObj.gotoChild('richText', False)
	basic = drawObj.gotoChild('basic', False)
	transposable = drawObj.gotoChild('transposable', False)
	group = drawObj.gotoChild('group', False)
	if transposable is not None:
		if criteria == 'chord':
			return True
		else:
			return False
	if group is not None: 
		drOb = group.gotoChild('drawObj', False)
		if drOb is not None:
			if criteria == 'chord':
				return True
			else:
				return False
	if text is not None:
		font = text.gotoChild('font', False)
		if font is not None:
			face = font.getAttribute('face')
			# Music Symbol Criteria (capella3 font face)
			if face == 'capella3':
				if criteria == 'musicSymbol':
					return True
				else:
					return False
			height = font.getAttribute('height')
			try:
				height = int(height)
			except:
				height = 0
			color = font.getAttribute('color')
			# Global Fret Index Criteria (font color and size)
			if color == estFretIndexFontColor and height == estFretIndexFontSize:
				if criteria == 'globalFretIndex':
					return True
				else:
					return False
			# Local Fret Index Criteria (font color and size)
			if color == estFretIndexFontColor and height < estFretIndexFontSize:
				if criteria == 'localFretIndex':
					return True
				else:
					return False
		# Fingering criteria (placementHint)
		if basic is not None and basic.getAttribute('placementHint') == 'fingering':
			if criteria == 'fingering':
				return True
			else:
				return False 
		# Simple Text (without placementHint) or Performance Instruction (placementHint)
		if (basic is None) or (basic is not None and basic.getAttribute('placementHint') in ['', 'performanceInstruction']) :
			if criteria == 'simple':
				return True
			else:
				return False
	# Rich Text (richText Child is present)
	if richText is not None:
		if criteria == 'richText':
			return True
		else:
			return False
	return False

def cap2acaTone(head, baseOct4):
	pitch = head.getAttribute('pitch')
	alter = head.gotoChild('alter', False)
	step = 0
	if alter is not None:
		step = int(alter.getAttribute('step'))
	tone = pitch
	octave = int(tone[1])
	if baseOct4:
		octave = octave - 1
	if step == 1:
		tone = tone[0] + '#' + str(octave)
	elif step == -1:
		tone = tone[0] + 'b' + str(octave)
	return tone

def aca2capTone(tone, baseOct4):
	step = 0
	if len(tone) < 2:
		return (None, None)
	base = tone[0]
	tone = tone[1:]
	while tone[0] in [u'b', u'#']:
		if tone[0] == 'b':
			step = step - 1
		else:
			step = step + 1
		tone = tone[1:]
	if len(tone) < 1:
		return (None, None)
	octave = tone[0]
	if baseOct4:
		octave = str(int(octave) + 1)
	return (base+octave, step)

def getTextContent(drawObj):
	text = drawObj.gotoChild('text', False)
	if text is not None:
		content = ''
		content_t = text.getElementsByTagName('content')[0].firstChild
		if content_t is not None:
			content = content_t.data
		return content
	return None

def getGlobalFretIndex(noteObj):
	textObjs = getObjTextObjsByCriteria(noteObj, 'globalFretIndex')
	gfis = []
	for textObj in textObjs:
		content = getTextContent(textObj)
		try:
			dummy = int(content)
			gfis.append(textObj)
		except:
			pass
	if len(gfis) == 1:
		content = getTextContent(gfis[0])
		return content
	return ''

def getKey(noteObj):
	if noteObj.localName == 'keySign':
		fifths = noteObj.getAttribute('fifths')
		if fifths is not None:
			return int(fifths)
	return None

def getKeys(noteObj):
	key = getKey(noteObj)
	if key is not None:
		return str(key)
	return ''

def addPolyPoint(points, x, y):
	newPoint = doc.createElement('point')
	newPoint.setAttribute('x', str(x))	
	newPoint.setAttribute('y', str(y))	
	points.appendChild(newPoint)

def addTremoloBarXY(drawObjects, x1, y1, x2, y2):
	newDrawObj = doc.createElement('drawObj')
	basic = newDrawObj.gotoChild('basic', True)
	basic.setAttribute('behindNotes', 'true')
	basic.setAttribute('horizAlign','0')
	basic.setAttribute('vertAlign','1')

	polygon = newDrawObj.gotoChild('polygon', True)
	polygon.setAttribute('filled', 'true')
	polygon.setAttribute('lineWidth', '0')
	points = polygon.gotoChild('points', True)
	yd = -0.4
	addPolyPoint(points, x1, y1)
	addPolyPoint(points, x2, y2)
	addPolyPoint(points, x2, y2+yd)
	addPolyPoint(points, x1, y1+yd)
	drawObjects.appendChild(newDrawObj)


def addTremoloBar(drawObjects, x, y, stemDirUp = True):
	newDrawObj = doc.createElement('drawObj')
	basic = newDrawObj.gotoChild('basic', True)
	basic.setAttribute('behindNotes', 'true')
	basic.setAttribute('horizAlign','1')
	basic.setAttribute('vertAlign','3')

	polygon = newDrawObj.gotoChild('polygon', True)
	polygon.setAttribute('filled', 'true')
	polygon.setAttribute('lineWidth', '0')
	points = polygon.gotoChild('points', True)
	if stemDirUp:
		stemDirSign = 1.0
	else:
		stemDirSign = -1.0
	x1 = -0.7 + x 
	x2 = 0.7 + x
	y1 = stemDirSign * 1.3 + y
	y2 = stemDirSign * 1.7 + y
	yd = -0.4
	addPolyPoint(points, x1, y1)
	addPolyPoint(points, x1, y2)
	addPolyPoint(points, x2, y2+yd)
	addPolyPoint(points, x2, y1+yd)
	drawObjects.appendChild(newDrawObj)

def midiPitch(tone):
	step = 0
	key_notes = "CDEFGAB"
	i = key_notes.find(tone[0])
	tone = tone[1:]
	if len(tone) < 1:
		return None
	while tone[0] in [u'b', u'#']:
		if tone[0] == 'b':
			step = step - 1
		else:
			step = step + 1
		tone = tone[1:]
	n = (0,2,4,5,7,9,11)[i] + 12 * int(tone[0]) + step
	return n

def findAlienTags(obj):
	odrawObjs, drawObjs = getChildren(obj, 'drawObjects')
	for drawObj in drawObjs:
		basic = drawObj.gotoChild('basic', False)
		if basic is not None:
			tag = basic.getAttribute('tag')
			if tag != '' and tag != tremoloBarTag:
				return True
	return False

def deleteTremoloBars(obj):
	stem = obj.gotoChild('stem', False)
	if stem is not None:
		tremoloBars = stem.getAttribute('tremoloBars')
		if tremoloBars != '':
			stem.removeAttribute('tremoloBars')

	odrawObjs, drawObjs = getChildren(obj, 'drawObjects')
	for drawObj in drawObjs:
		basic = drawObj.gotoChild('basic', False)
		if basic is not None:
			tag = basic.getAttribute('tag')
			if tag == tremoloBarTag:
				odrawObjs.removeChild(drawObj)

def getCapNoteObj2(selection):
	[startSystemID, startStaffID, startVoiceID, startObjID], [endSystemID, endStaffID, endVoiceID, endObjID] = selection
	system = activeScore().system(startSystemID)
	staff = system.staff(startStaffID)
	voice = staff.voice(startVoiceID)
	obj1 = None
	if startObjID + 1 < voice.nNoteObjs():
		obj1 = voice.noteObj(startObjID)
		obj2 = voice.noteObj(startObjID + 1)
		return (obj1, obj2)
	return (obj1, None)

def getNoteObj2(score, selection):
	osystens, systems = getChildren(score, 'system')
	systemID = 0
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		staffID = 0
		for staff in staves:
			ovoices, voices = getChildren(staff, 'voice')
			voiceID = 0
			for voice in voices:
				oobjs, objs = getChildren(voice, 'noteObjects')
				numObjs = len(objs)
				objID = 0
				for i in range(0, numObjs):
					obj = objs[i]
					obj2 = None
					if i+1 < numObjs:
						obj2 = objs[i+1]
					if (systemID, staffID, voiceID, objID) == selection[0]:
						return (obj, obj2)
					objID = objID + 1
				voiceID = voiceID + 1
			staffID = staffID + 1
		systemID = systemID + 1
	return (None, None)

def addTremolo(score, selection, dualTremolo, refreshOnly = False):
	stemDirUp = False
	isAbbrev = False
	obj, nextObj = getNoteObj2(score, selection)
	objCap, nextObjCap = getCapNoteObj2(selection)
	if not dualTremolo:
		nextObj = None
	stem = obj.gotoChild('stem', False)
	dura = getObjDuration(obj)
	if stem is not None:
		dir = stem.getAttribute('dir')
		tremBars = stem.getAttribute('tremoloBars')
		if tremBars != '' and int(tremBars) > 0:
			isAbbrev = True
		if dir == 'up':
			stemDirUp = True
	else:
		stem = obj.gotoChild('stem', True)
		stemDirs = ['up', 'down']	
		if not refreshOnly:
			stem.setAttribute('dir', stemDirs[setTremStemPrefDirIndex])
			stemDirUp = setTremStemPrefDirIndex == 0

	reps = setTremRepsIndex + 1
	if refreshOnly:
		duraInfo, _a, _b = getTremoloNotesAndDura(obj, nextObj)
		reps = duraInfo[2]
		if reps is None:
			return

	if (refreshOnly and not isAbbrev) or nextObj is not None and (setTremPolygonBars and not refreshOnly) and ((dura is not None and dura >= 0.249) or not setTremFlagNoteAbbrev):
		# Dual Tremolo
		deleteTremoloBars(obj)
		objDrawObjects = obj.gotoChild('drawObjects', True)
		newDrawObj = doc.createElement('drawObj')

		newBasic = doc.createElement('basic')
		newBasic.setAttribute('behindNotes', 'true')
		newBasic.setAttribute('horizAlign', '1')
		if stemDirUp:
			newBasic.setAttribute('vertAlign', '1')
		else:
			newBasic.setAttribute('vertAlign', '2')
		newBasic.setAttribute('tag', tremoloBarTag)
		newDrawObj.appendChild(newBasic)

		newGroup = doc.createElement('group')
		pitches = getPitches(obj)
		nextPitches = getPitches(nextObj)
		midiPitches = []
		nextMidiPitches = []
		for pitch in pitches:
			midiPitches.append(midiPitch(pitch))
		for pitch in nextPitches:
			nextMidiPitches.append(midiPitch(pitch))
		yd= 0.0
		if len(midiPitches) > 0 and len(nextMidiPitches) > 0:
			yd = (nextMidiPitches[-1] - midiPitches[0]) * -0.3
		for rep in range(0, reps):
			absX1 = 0
			absX2 = 0
			if objCap is not None:
				absX1 = objCap.posX(True)
			if nextObjCap is not None:
				absX2 = nextObjCap.posX(True)
			yOffs = rep * -0.7
			xd = absX2-absX1 + 1.0
			symmBalance = 0.0
			rising = math.degrees(math.atan2(yd+symmBalance, xd))
			if rising < -setTremBarMaxAngle or rising > setTremBarMaxAngle:
				if rising < 0:
					target = -setTremBarMaxAngle
				else:
					target = setTremBarMaxAngle
				symmBalance = - yd + xd * math.tan(math.radians(target))
				rising = math.degrees(math.atan2(yd+symmBalance, xd))
			x1 = 0.0
			x2 = 0.0 + xd
			y1 = 0 + yOffs
			y2 = 0 + yOffs + yd + symmBalance
			if setTremBarSymm:
				y1 = y1 - symmBalance / 2
				y2 = y2 - symmBalance / 2
			# Cut Line but keeping rising
			cut = 1.8
			y1 = y1 + cut * math.sin(math.radians(rising))
			y2 = y2 - cut * math.sin(math.radians(rising))
			x1 = x1 + cut
			x2 = x2 - cut
			addTremoloBarXY(newGroup, x1, y1, x2, y2)
		newDrawObj.appendChild(newGroup)
		objDrawObjects.appendChild(newDrawObj)
	else:
		# Mono Tremolo
		if stem is not None and refreshOnly == False:
			deleteTremoloBars(obj)
			stem.setAttribute('tremoloBars', str(reps))
	
def getPosAtCursor(score):
	pos = 0.0
	sys1 = curSelection()[0][0]
	sta1 = curSelection()[0][1]
	voi1 = curSelection()[0][2]
	nObj1 = curSelection()[0][3]

	osystens, systems = getChildren(score, 'system')
	systemID = 0
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		staffID = 0
		savedPosSystem = pos
		for staff in staves:
			pos = savedPosSystem
			ovoices, voices = getChildren(staff, 'voice')
			voiceID = 0
			savedPosStaff = pos
			for voice in voices:
				pos = savedPosStaff
				oobjs, objs = getChildren(voice, 'noteObjects')
				numObjs = len(objs)
				objID = 0
				for i in range(0, numObjs):
					obj = objs[i]
					if (systemID, staffID, voiceID, objID) == (sys1, sta1, voi1, nObj1):
						return pos
					dura = getObjDuration(obj)
					if dura is not None:
						pos = pos + dura
					objID = objID + 1
				voiceID = voiceID + 1
			staffID = staffID + 1
		systemID = systemID + 1
	return None

def getPitches(noteObj):
	retval = []
	oheads, heads = getChildren(noteObj, 'head')
	for head in heads:
		pitch = head.getAttribute('pitch')
		alter = head.gotoChild('alter')
		step = 0
		if alter is not None:
			tstep = alter.getAttribute('step')
			if tstep is not None and tstep != '':
				step = int(tstep)
		stepSigns = [ 'bb', 'b', '', '#', '##' ]
		if pitch is not None:
			retval.append(pitch[0]+stepSigns[step+2]+pitch[1])
	return retval

def getTremoloNotesAndDura(obj, nextObj=None):
	baseNotePitches = None
	if obj is not None:
		baseNotePitches = getPitches(obj)
	nextNotePitches = None
	if nextObj is not None:
		nextNotePitches = getPitches(nextObj)

	baseNoteBase = None
	baseNoteDots = None
	baseNoteNoDura = False
	tremoloBars = None
	if obj is not None:
		if obj.localName == 'chord':
			duration = obj.gotoChild('duration')
			if duration is not None:
				baseNoteBase = duration.getAttribute('base')
				baseNoteDots = duration.getAttribute('dots')
				noDuration = duration.getAttribute('noDuration')
				if noDuration == 'true':
					baseNoteNoDura = True
			stem = obj.gotoChild('stem', False)
			if stem is not None:
				tremBars = stem.getAttribute('tremoloBars')
				if tremBars != '':
					tremoloBars = int(tremBars)

	nextNoteBase = None
	nextNoteDots = None
	nextNoteNoDura = False
	if nextObj is not None:
		if nextObj.localName == 'chord':
			duration = nextObj.gotoChild('duration', False)
			if duration is not None:
				nextNoteBase = duration.getAttribute('base')
				nextNoteDots = duration.getAttribute('dots')
				noDuration = duration.getAttribute('noDuration')
				if noDuration == 'true':
					nextNoteNoDura = True

	odrawObjs, drawObjs = getChildren(obj, 'drawObjects')
	for drawObj in drawObjs:
		basic = drawObj.gotoChild('basic', False)
		if basic is not None:
			tag = basic.getAttribute('tag')
			if tag == tremoloBarTag:
				group = drawObj.gotoChild('group', False)
				if group is not None:
					gDrawObjs = childElements(group, 'drawObj')
					tremoloBars = len(gDrawObjs)

	if baseNoteNoDura == False and nextNoteNoDura == True:
		if baseNoteBase == nextNoteBase and baseNoteDots == nextNoteDots:
			# Dual Tremolo
			return ((baseNoteBase, baseNoteDots, tremoloBars), baseNotePitches, nextNotePitches)
		else:
			# Mono Tremolo
			return ((baseNoteBase, baseNoteDots, tremoloBars), baseNotePitches, None)
	else:
		if baseNoteNoDura == False:
			# Mono Tremolo
			return ((baseNoteBase, baseNoteDots, tremoloBars), baseNotePitches, None)

	# Not a valid Tremolo Base Note
	return ((None, None, None), None, None)

def objChord(tones = [], dura = '1/4', dots = None, tie_begin = False, tie_end = False):
	if tones == []:
		newEl = doc.createElement('rest')
	else:
		newEl = doc.createElement('chord')

	duration = newEl.gotoChild('duration', True)
	duration.setAttribute('base', dura)
	if dots is not None:
		duration.setAttribute('dots', str(dots))
	if tones != []:
		heads = newEl.gotoChild('heads', True)
		for tone in tones:
			head = doc.createElement('head')
			pitch, step = aca2capTone(tone, False)
			if pitch is not None:
				head.setAttribute('pitch', pitch)
				if step is not None and step != 0:
					alter = doc.createElement('alter')
					alter.setAttribute('step', str(step))
					head.appendChild(alter)
				if tie_begin or tie_end:
					tienode = doc.createElement('tie')
					if tie_begin:
						tienode.setAttribute('begin', 'true')
					if tie_end:
						tienode.setAttribute('end', 'true')
					head.appendChild(tienode)
				heads.appendChild(head)
	return newEl

def expandTremolo(oObjs, objs, idx):
	baseNote = objs[idx]
	if idx+1 > len(objs):
		nextNote = None
	else:
		nextNote = objs[idx + 1]
	baseNoteDuration, baseNotePitches, toggleNotePitches = getTremoloNotesAndDura(baseNote, nextNote)
	baseDurationBase = baseNoteDuration[0]	
	baseDurationDots = baseNoteDuration[1]	
	baseDurationTremBars = baseNoteDuration[2]	

	if baseDurationBase is None:
		return
	if baseDurationTremBars is None:
		return

	dura = 0.0
	oDuration = baseNote.gotoChild('duration', False)
	stem = baseNote.gotoChild('stem', False)
	isAbbrev = False
	if stem is not None:
		tremoloBars = stem.getAttribute('tremoloBars')
		if tremoloBars != '':
			isAbbrev = True
	base = '0'
	if oDuration is not None:
		baseNoteBase = oDuration.getAttribute('base')
		baseNoteDots = oDuration.getAttribute('dots')
		base = baseNoteBase.split('/')
		if len(base) > 1:
			dura = float(base[0])/float(base[1])
	repDura = 1.0/2.0**(2.0+baseDurationTremBars)
	repDuraBase = '1/'+str(2.0**(2.0+baseDurationTremBars))
	
	nextNoteBase = ''
	nextNoteDots = ''
	if nextNote is not None:
		oDuration = nextNote.gotoChild('duration', False)	
		if oDuration is not None:
			nextNoteBase = oDuration.getAttribute('base')
			nextNoteDots = oDuration.getAttribute('dots')
	
	# Dual-Tremolo base/toggle note duration check
	if baseDurationTremBars and not isAbbrev and nextNote is not None:
		if baseNoteBase != nextNoteBase or baseNoteDots != nextNoteDots:
			msgNoValidDualTremDuras(baseNoteBase, baseNoteDots, nextNoteBase, nextNoteDots)
			return

	if repDura >= dura:
		msgNoValidTremDura(baseNoteBase, '1/'+str(2**(3+setTremRepsIndex)))
		return

	destObjs = []

	# Dual-Tremolo
	if toggleNotePitches is not None and len(toggleNotePitches) > 0:
		toggle = True
		for i in range(len(objs)):
			# Previous Notes
			if i < idx:
				destObjs.append(objs[i].cloneNode(deep=True))
			# The Base Note
			if i == idx:
				newBaseNote = objs[i].cloneNode(deep=True)
				nbnDuration = newBaseNote.gotoChild('duration', True)
				nbnDuration.setAttribute('base', repDuraBase)
				nbnDots = nbnDuration.getAttribute('dots')
				deleteTremoloBars(newBaseNote)
				if nbnDots != '':
					nbnDuration.removeAttribute('dots')
				# Place the Base Note
				destObjs.append(newBaseNote)
				# Generate Inter-Tremolo notes
				baseTotalDura = dura
				intBaseNoteDots = 0
				try:
					intBaseNoteDots = int(baseNoteDots)
				except:
					pass

				for dotI in range(0,intBaseNoteDots):
					baseTotalDura = baseTotalDura + dura / (2**(dotI + 1))
				for r in range(2, int(baseTotalDura/repDura)):
					if toggle:
						newNote = objChord(toggleNotePitches, repDuraBase)
						destObjs.append(newNote)
					else:
						newNote = objChord(baseNotePitches, repDuraBase)
						destObjs.append(newNote)
					toggle = not toggle
			# The Next Note (but obly when number of Inter-Tremolo notes are even)
			if i == idx + 1:
				rest = baseTotalDura/repDura - float(int(baseTotalDura/repDura))
				newDots = None
				if rest < 0.999:
					if abs(rest-0.5) < 0.001:
						newDots = 1
					if abs(rest-0.75) < 0.001 or abs(rest-0.25) < 0.001:
						newDots = 2
				newNextNote = objs[i].cloneNode(deep=True)
				nnnDuration = newNextNote.gotoChild('duration', True)
				nnnDuration.setAttribute('base', repDuraBase)
				nnnDots = nnnDuration.getAttribute('dots')
				if newDots == None:
					if nnnDots != '':
						nnnDuration.removeAttribute('dots')
				else:
					nnnDuration.setAttribute('dots', str(newDots))
				nnnNoDuration = nnnDuration.getAttribute('noDuration')
				if nnnNoDuration != '':
					nnnDuration.removeAttribute('noDuration')
				if toggle == False:
					# Take Base Note Pitches
					baseNoteHeads = baseNote.gotoChild('heads', False)
					if baseNoteHeads is not None:
						newBaseNoteHeads = baseNoteHeads.cloneNode(deep=True)
						newNextNoteOldHeads = newNextNote.gotoChild('heads', False)
						if newNextNoteOldHeads is not None:
							newNextNote.removeChild(newNextNoteOldHeads)
							newNextNote.appendChild(newBaseNoteHeads)
				destObjs.append(newNextNote)
			# The Rest
			if i > idx + 1:
				destObjs.append(objs[i].cloneNode(deep=True))
			oObjs.removeChild(objs[i])
	else:
	# Mono-Tremolo
		for i in range(len(objs)):
			# Previous Notes
			if i < idx:
				destObjs.append(objs[i].cloneNode(deep=True))
			# The Base Note
			if i == idx:
				newBaseNote = objs[i].cloneNode(deep=True)
				nbnDuration = newBaseNote.gotoChild('duration', True)
				nbnDuration.setAttribute('base', repDuraBase)
				nbnDots = nbnDuration.getAttribute('dots')
				deleteTremoloBars(newBaseNote)
				if nbnDots != '':
					nbnDuration.removeAttribute('dots')
				# Place the Base Note
				destObjs.append(newBaseNote)
				# Generate Inter-Tremolo notes
				baseTotalDura = dura
				intBaseNoteDots = 0
				try:
					intBaseNoteDots = int(baseNoteDots)
				except:
					pass

				for dotI in range(0,intBaseNoteDots):
					baseTotalDura = baseTotalDura + dura / (2**(dotI + 1))
				for r in range(1, int(baseTotalDura/repDura)):
					rest = baseTotalDura/repDura - r - 1
					newDots = None
					if rest < 0.999:
						if abs(rest-0.5) < 0.001:
							newDots = 1
						if abs(rest-0.75) < 0.001 or abs(rest-0.25) < 0.001:
							newDots = 2
					newNote = objChord(baseNotePitches, repDuraBase, newDots)
					destObjs.append(newNote)
			# The Rest
			if i > idx:
				destObjs.append(objs[i].cloneNode(deep=True))
			oObjs.removeChild(objs[i])
	setChildren(oObjs, destObjs, 'noteObj')
	

def aC_TremolorMain(score):
	cursorMarkStart = curSelection()[0]
	cursorMarkEnd = curSelection()[1]
	print('cursorMarkStart', cursorMarkStart)
	print('cursorMarkEnd', cursorMarkEnd)

	osystens, systems = getChildren(score, 'system')
	pos = 0
	systemID = 0
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		savedPos = pos
		staffID = 0
		for staff in staves:
			pos = savedPos
			ovoices, voices = getChildren(staff, 'voice')
			savedPosStaff = pos
			voiceID = 0
			for voice in voices:
				pos = savedPosStaff
				oObjs, objs = getChildren(voice, 'noteObjects')
				objID = 0
				for i in range(0,len(objs)):
					obj = objs[i]
					duration = getObjDuration(obj)
					if cursorMarkStart == (systemID, staffID, voiceID, objID):
						if findAlienTags(obj):
							msgWarningAlienTag()
							return
						# Add Tremolo
						if setTremActionIndex == 0:
							if i+1 < len(objs):
								dura = 0.0
								oDuration = obj.gotoChild('duration', False)
								base = '0'
								if oDuration is not None:
									oBase = oDuration.getAttribute('base')
									base = oBase.split('/')
									if len(base) > 1:
										dura = float(base[0])/float(base[1])
								repDura = 1.0/2.0**(3.0+setTremRepsIndex)
								if repDura >= dura:
									msgNoValidTremDura(oBase, '1/'+str(2**(3+setTremRepsIndex)))
								else:
									if estTremToggleNotePitches is not None and len(estTremToggleNotePitches) > 0:
										addTremolo(score, [cursorMarkStart, cursorMarkEnd], True, False)
									elif estTremBaseNotePitches is not None and len(estTremBaseNotePitches) > 0:
										addTremolo(score, [cursorMarkStart, cursorMarkEnd], False, False)
						# Refresh Tremolo
						if setTremActionIndex == 1:
							if i+1 < len(objs):
								if estTremToggleNotePitches is not None and len(estTremToggleNotePitches) > 0:
									addTremolo(score, [cursorMarkStart, cursorMarkEnd], True, True)
							# Expand Tremolo
						elif setTremActionIndex == 2:
							expandTremolo(oObjs, objs, i)
						# Delete Tremolo
						elif setTremActionIndex == 3:
							deleteTremoloBars(obj)
					if duration is not None:
						pos = pos + duration
					objID = objID + 1
				voiceID = voiceID + 1
			staffID = staffID + 1
		systemID = systemID + 1

class Config:
	def __init__(self):
		self.file = ScriptOptions()
		self.dic = {}

	def get(self, varName, defValue = None):
		try:
			value = self.dic.get(varName)
			if defValue is not None:
				if value is None:
					value = defValue
					if type(defValue) is bool:
						defValue = str(value).lower
					elif type(defValue) is int:
						defValue = int(value)
					self.set(varName, defValue)
				else:
					if type(defValue) is bool:
						if value == 'true':
							value = True
						elif value == 'false':
							value = False
						elif value == '1':
							value = True
						elif value == '0':
							value = False
					if type(defValue) is int:
						value = int(value)
			return value
		except:
			pass
		return None
	
	def set(self, varName, value):
		if type(value) is bool:
			value = str(value).lower()
		if type(value) is int:
			value = str(value)
		self.dic.update({varName: value})

	def load(self):
		try:
			self.dic = self.file.get()
			for key in self.dic.keys():
				value = self.dic.get(key)
				if value == 'false':
					value = False
				elif value == 'true':
					value = True
				self.dic.update({key: value})
			print("loaded", self.dic)
		except:
			print("Could not open configuration file",self.file)

	def save(self):
		try:
			for key in self.dic.keys():
				value = self.dic.get(key)
				if type(value) is bool:
					value = str(value).lower()
				self.dic.update({key: value})
			self.file.set(self.dic)
		except:
			print("Could not save configuration file",self.file)

# Get color and font size for a given keyword
def getColorAndSizeForKeyword(score, keyword, case_sens=False):
	parsed = False
	if case_sens == False:
		keyword = keyword.lower()
	osystens, systems = getChildren(score, 'system')
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:
			staffName = staff.getAttribute('layout')
			ovoices, voices = getChildren(staff, 'voice')
			for voice in voices:
				oObjs, objs = getChildren(voice, 'noteObjects')
				for i in range(0,len(objs)):
					obj = objs[i]
					texts = obj.getElementsByTagName('text')
					if texts.length > 0:
						for text in texts:
							content = text.getElementsByTagName('content')
							data = ''
							if content is not None and len(content) > 0:
								fs = content[0].firstChild
								if fs is not None:
									data = fs.data
	
							fonts = text.getElementsByTagName('font')
							if case_sens == False:
								data = data.lower()
							if fonts.length > 0 and data.replace("!","").startswith(keyword):
								if data == (keyword+'!'):
									parsed = True
								color = fonts[0].getAttribute('color')
								height = int(fonts[0].getAttribute('height'))
								return (color, height, staffName, parsed)
	return (u'', 0, u'<?>', False)

def getPitchIndex(noteObj):
	texts = noteObj.getElementsByTagName('text')
	if texts.length > 0:
		for text in texts:
			try:
				content = text.getElementsByTagName('content')[0].firstChild.data
				fonts = text.getElementsByTagName('font')
				if fonts.length > 0:
					color = fonts[0].getAttribute('color')
					height = int(fonts[0].getAttribute('height'))
					if height == estPitchIndexFontSize and color == estPitchIndexFontColor and content.strip().lower() != 'pitch':
						return content.lower()
			except:
				pass
	return u''

def getGlobalFretIndex(noteObj):
	texts = noteObj.getElementsByTagName('text')
	if texts.length > 0:
		for text in texts:
			try:
				content = text.getElementsByTagName('content')[0].firstChild.data
				fonts = text.getElementsByTagName('font')
				if fonts.length > 0:
					color = fonts[0].getAttribute('color')
					height = int(fonts[0].getAttribute('height'))
					if height == estFretIndexFontSize and color == estFretIndexFontColor and content.strip().lower().replace('!', '') != 'fret':
						return content.lower().replace('pitch ', '')
			except:
				pass
	return u''

def getPitchIndexCount(score, staff_name):
	osystens, systems = getChildren(score, 'system')
	count = 0
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:
			ovoices, voices = getChildren(staff, 'voice')
			staffName = staff.getAttribute('layout')
			for voice in voices:
				oObjs, objs = getChildren(voice, 'noteObjects')
				for i in range(0,len(objs)):
					obj = objs[i]
					pitchIndex = getPitchIndex(obj)
					if len(pitchIndex) > 0:
						count = count + 1
	return count

def getGlobalFretIndexCount(score, staff_name):
	osystens, systems = getChildren(score, 'system')
	count = 0
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:
			ovoices, voices = getChildren(staff, 'voice')
			staffName = staff.getAttribute('layout')
			for voice in voices:
				oObjs, objs = getChildren(voice, 'noteObjects')
				for i in range(0,len(objs)):
					obj = objs[i]
					pitchIndex = getGlobalFretIndex(obj)
					if len(pitchIndex) > 0:
						count = count + 1
	return count

config = Config()


def msgNoValidTremNote():
	messageBox('Keine gültige Tremolo Note', 'Cursor steht vor Pause oder Note ohne Wert.')

def msgNoValidTremDura(duration, repRate):
	messageBox('Kein gültiger Tremolo', 'Die Wiederholungsrate ('+repRate+') muss kleiner sein als die Dauer der unpunktierten Basisnote ('+duration+').')

def msgWarningAlienTag():
	messageBox('Ausführung verweigert', 'Diese Note wurde von einem fremden Plugin mit einer Funktion belegt.')

def msgNoValidDualTremDuras(baseNoteBase, baseNoteDots, nextNoteBase, nextNoteDots):
	printedBaseNoteDots = ''
	printedNextNoteDots = ''
	if baseNoteDots is not None:
		try:
			printedBaseNoteDots = 'p'*int(baseNoteDots)
		except:
			pass
	if nextNoteDots is not None:
		try:
			printedNextNoteDots = 'p'*int(nextNoteDots)
		except:
			pass
	messageBox('Kein gültiger Dual-Tremolo', 'Die Dauer der Wechsel-Note ('+nextNoteBase+printedNextNoteDots+') entspricht nicht der Dauer der Basisnote ('+baseNoteBase+printedBaseNoteDots+').')

isAln = False
estTremBaseNoteDuration = (None, None, None)
estTremBaseNotePitches = None
estTremToggleNotePitches = None

def analyzeScore(score):
	global cursorKeys
	global setGermanH
	global setStep7Flat
	global estNotation
	global estStyle
	global estFretIndexFontSize	
	global estFretIndexFontColor
	global estFretIndexStaffName
	global estGlobalFretIndexCount
	global estPitchIndexFontColor
	global estPitchIndexFontSize	     
	global estPitchIndexStaffName
	global estPitchIndexCount
	global estTremBaseNoteDuration
	global estTremBaseNotePitches
	global estTremToggleNotePitches
	global isAln

	cursorKeys = ['-', '-']
	estNotation = '-'
	estStyle = '-'
	setGermanH = False
	setStep7Flat = True

	cursorMarkStart = curSelection()[0]
	cursorMarkEnd = curSelection()[1]
	print('cursorMarkStart', cursorMarkStart)
	print('cursorMarkEnd', cursorMarkEnd)

	estFretIndexFontColor, estFretIndexFontSize, estFretIndexStaffName, isAln = getFretColorAndSize(score)
	estPitchIndexFontColor, estPitchIndexFontSize, estPitchIndexStaffName, estPitchIndexParsed = getColorAndSizeForKeyword(score, u'Pitch')
	estPitchIndexCount = getPitchIndexCount(score, estPitchIndexStaffName)
	estGlobalFretIndexCount = getGlobalFretIndexCount(score, estFretIndexStaffName)

	osystens, systems = getChildren(score, 'system')
	systemID = 0
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		staffID = 0
		for staff in staves:
			ovoices, voices = getChildren(staff, 'voice')
			voiceID = 0
			for voice in voices:
				oObjs, objs = getChildren(voice, 'noteObjects')
				objID = 0
				for i in range(0,len(objs)):
					obj = objs[i]
					if cursorMarkStart == (systemID, staffID, voiceID, objID):
						if i+1 < len(objs):
							nextObj = objs[i+1]
						else:
							nextObj = None
						estTremBaseNoteDuration, estTremBaseNotePitches, estTremToggleNotePitches =	getTremoloNotesAndDura(obj, nextObj)
					objID = objID + 1
				voiceID = voiceID + 1
			staffID = staffID + 1
		systemID = systemID + 1
	if estTremBaseNoteDuration[0] is None:
		msgNoValidTremNote()

# Analyze Phase
class ScoreAnalyze(ScoreChange):
	def changeScore(self, score):
		global doc
		doc = score.parentNode
		analyzeScore(score)

# Plugin Processing Phase
class ScoreChange(ScoreChange):
	def changeScore(self, score):
		global doc
		doc = score.parentNode
		aC_TremolorMain(score)
		
def beautify(text):
	return text.replace('#', '♯').replace('b', '♭')

# Plugin Dialog
def dialog():
	global config
	global setTremActionIndex
	global setTremStemPrefDirIndex
	global setTremPolygonBars
	global setTremBarSymm
	global setTremBarMaxAngle
	global setTremFlagNoteAbbrev

	global comboTremReps
	global comboTremTonevar
	global comboTremStemPrefDir
	global comboTremAction
	global checkTremPolygonBars
	global checkTremBarHead
	global editTremBarMaxAngle
	global checkTremFlagNoteAbbrev
	global checkReset

	global estFretIndexFontColor
	global estFretIndexFontSize
	global estFretIndexStaffName
	global estGlobalFretIndexCount
	global estPitchIndexFontColor
	global estPitchIndexFontSize
	global estPitchIndexStaffName
	global estPitchIndexCount
	global isAln

	config.load()
	setTremRepsIndex = config.get('setTremRepsIndex', 0)
	setTremStemPrefDirIndex = config.get('setTremStemPrefDirIndex', 0)
	setTremPolygonBars = config.get('setTremPolygonBars', True)
	setTremBarSymm = config.get('setTremBarSymm', True)
	setTremBarMaxAngle = config.get('setTremBarMaxAngle', 30)
	setTremFlagNoteAbbrev = config.get('setTremFlagNoteAbbrev', True)
	setTremActionIndex = 0

	wdt = 45
	spacer = Label('')

	labelFretIndexColor = Label('Fret-Index Font-Farbe', width=wdt / 2 + 2)
	labelFretIndexHeight = Label('Fret-Index Font-Größe', width=wdt / 2 + 2)
	labelFretIndexNum = Label('Anzahl globale Fret-Indexe', width=wdt / 2 + 2)
	labelFretIndexActive = Label('Fret-Index aktuell aktiv', width=wdt / 2 + 2)
	vBoxFretIndexLabels = VBox([labelFretIndexColor, labelFretIndexHeight, labelFretIndexNum, labelFretIndexActive])

	fColor = '-'
	fSize = '-'
	isConv = '-'
	gfiCount = '-'
	if estFretIndexFontColor != '':
		fColor = '#'+estFretIndexFontColor
		fSize = str(estFretIndexFontSize)+' px'
		gfiCount = str(estGlobalFretIndexCount)
		if isAln:
			isConv = "Ja"
		else:
			isConv = "Nein (Tremolo-Einbau möglich)"
	labelFretIndexColorValue = Label(': '+fColor, width=wdt/2+1)
	labelFretIndexHeightValue = Label(': '+fSize, fg = estFretIndexFontColor, width=wdt/2+1)
	labelFretIndexNumValue = Label(': '+gfiCount, width=wdt/2+1)
	labelFretIndexActiveValue = Label(': '+isConv, width=wdt/2+1)
	vBoxFretIndexValues = VBox([labelFretIndexColorValue, labelFretIndexHeightValue, labelFretIndexNumValue, labelFretIndexActiveValue])

	labelFretIndexInfo1 = Label('Achtung: Es existiert ein AKTIVER(!) Fret-Index.  Nach Veränderung', width=wdt)
	labelFretIndexInfo2 = Label('der Notenzeile (z.B.  durch Einbau eines Tremolos) muss dieser', width=wdt)
	labelFretIndexInfo3 = Label('durch das acaLead-Plugin aktualisiert werden!', width=wdt)
	estPropertiesElems = [HBox([vBoxFretIndexLabels, vBoxFretIndexValues])]
	if isAln:
		estPropertiesElems.extend([spacer, labelFretIndexInfo1, labelFretIndexInfo2, labelFretIndexInfo3])
	vBoxEstProperties = VBox(estPropertiesElems, width = wdt) 
	hBoxEstProperties = HBox([vBoxEstProperties], text = 'Ermittelte Fret-Eigenschaften')

	printedTremNoteActive = '-'
	printedTremNoteDuration = '-'
	printedTremBaseNotes = '-'
	printedTremToggleNotes = '-'
	if estTremBaseNoteDuration[0] is not None:
		printedTremNoteDuration = estTremBaseNoteDuration[0]
		if estTremBaseNoteDuration[1] is not None:
			dots = 0
			try:
				dots = int(estTremBaseNoteDuration[1])
			except:
				pass
			printedTremNoteDuration = printedTremNoteDuration + 'p' * dots
		printedTremNoteActive = 'nein'
		if estTremBaseNoteDuration[2] is not None:
			tremBars = 0
			try:
				tremBars = int(estTremBaseNoteDuration[2])
			except:
				pass
			if tremBars > 0:
				printedTremNoteActive = 'ja (' + '/' * tremBars + ')'
	if estTremBaseNotePitches is not None and len(estTremBaseNotePitches) > 0:
		pitchesAca = []
		for pitch in estTremBaseNotePitches:
			tone = re.sub('(.*)[-0-9]+','\\1', pitch)
			oct = re.sub('.*([-0-9]+)','\\1', pitch)
			oct = int(oct) - 1
			oct = str(oct)
			pitchesAca.append(tone+oct)
		printedTremBaseNotes = ',  '.join(pitchesAca) + '  (+1 Oct.  in Capella/MIDI)'
	if estTremToggleNotePitches is not None and len(estTremToggleNotePitches) > 0:
		pitchesAca = []
		for pitch in estTremToggleNotePitches:
			tone = re.sub('(.*)[-0-9]+','\\1', pitch)
			oct = re.sub('.*([-0-9]+)','\\1', pitch)
			oct = int(oct) - 1
			oct = str(oct)
			pitchesAca.append(tone+oct)
		printedTremToggleNotes = ',  '.join(pitchesAca)

	labelTremNoteActive = Label('Tremolo-Markierung vorhanden', width = wdt/2)
	labelTremNoteDuration = Label('Dauer', width = wdt/2)
	labelTremNaseNoteName = Label('Name', width = wdt/2)
	vBoxTremNoteLabels = VBox([labelTremNoteActive, labelTremNoteDuration, labelTremNaseNoteName])

	labelTremNoteActiveValue = Label(': '+printedTremNoteActive, width = wdt/2)
	labelTremNoteDurationValue = Label(': '+printedTremNoteDuration, width = wdt/2)
	labelTremNaseNoteNameValue = Label(': '+printedTremBaseNotes, width = wdt/2)
	vBoxTremBaseNote = VBox([labelTremNoteActive, labelTremNoteDuration, labelTremNaseNoteName])
	vBoxTremNoteValues = VBox([labelTremNoteActiveValue, labelTremNoteDurationValue, labelTremNaseNoteNameValue])
	hBoxTremBaseNote = HBox([vBoxTremBaseNote, vBoxTremNoteValues], text = 'Basisnote (an Cursorposition)')


	labelTremToggleNoteName = Label('Name', width = wdt/2)
	labelTremToggleNoteNameValue = Label(': '+printedTremToggleNotes, width = wdt/2)
	hBoxTremToggleNote = HBox([labelTremToggleNoteName, labelTremToggleNoteNameValue])

	labelTremNoteInfo1 = Label('Eine Wechselnote muss DIESELBE Dauer wie die Basisnote haben', width = wdt)
	labelTremNoteInfo2 = Label('UND auf "ohne Wert" gesetzt sein,  sonst gibt es einen monotonen', width = wdt)
	labelTremNoteInfo3 = Label('Triller.  (Die Wechselnote wird NICHT vom Plugin generiert!)', width = wdt)
	vBoxTremToggleNote = VBox([hBoxTremToggleNote, spacer, labelTremNoteInfo1, labelTremNoteInfo2, labelTremNoteInfo3])
	hBoxTremToggleNote = HBox([vBoxTremToggleNote], text = 'Wechselnote (rechts neben Basisnote)')

	tremRepsOpts = ['8tel', '16tel', '32tel']
	labelTremReps = Label('Wiederholungsrate', width = wdt / 2 + 10)
	comboTremReps = ComboBox(tremRepsOpts, value = setTremRepsIndex, width = wdt/4 + 1)
	hBoxTremReps = HBox([labelTremReps, comboTremReps], width = wdt)

	tremStemPrefDirOpts = ['Oben', 'Unten']
	labelTremStemPrefDir = Label('Ausrichtung Notenhals (Basisnote) bei "auto"', width = wdt/2 + 10)
	comboTremStemPrefDir = ComboBox(tremStemPrefDirOpts, value = setTremStemPrefDirIndex, width = wdt/4 + 1)
	hBoxTremStemPrefDir = HBox([labelTremStemPrefDir, comboTremStemPrefDir], width = wdt)

	checkTremPolygonBars = CheckBox('Dual-Noten-Tremolo mit Grafikbalken (oder Abbreviatur-Note)', value = setTremPolygonBars, width = wdt)
	labelTremBarMaxAngle = Label('Max.  rel.  Steigung des Grafikbalkens (0..45°)', width = wdt/2 + 10)
	editTremBarMaxAngle = Edit(value = str(setTremBarMaxAngle), width = wdt/4)
	hBoxTremBarMaxAngle = HBox([labelTremBarMaxAngle, editTremBarMaxAngle], width = wdt)
	checkTremBarHead = CheckBox('Tremolo-Balken symmetrisch (oder an Basis-Notenkopf fixiert)', value = setTremBarSymm, width = wdt)
	checkTremFlagNoteAbbrev = CheckBox('Fähnchen-Tremolo-Noten immer als Abbreviatur-Noten', value = setTremFlagNoteAbbrev, width = wdt)
	labelTremActionInfo1 = Label('Achtung: Nach einer manuellen Notenveränderung muss ggf.  das', width = wdt)
	labelTremActionInfo2 = Label('Plugin für eine optische Balken-Korrektur "aufgefrischt" werden.', width = wdt)

	vBoxTremoloSettings = VBox([hBoxTremBaseNote, hBoxTremToggleNote, spacer, hBoxTremReps, hBoxTremStemPrefDir, checkTremPolygonBars, hBoxTremBarMaxAngle, checkTremBarHead, checkTremFlagNoteAbbrev, spacer, labelTremActionInfo1, labelTremActionInfo2], height = 1, width = wdt)
	hBoxTremoloSettings = HBox([vBoxTremoloSettings], text = 'Tremolo-/Triller-Einstellungen')

	tremActionOpts = ['Erzeugen/Überschreiben', 'Auffrischen', 'Expandieren', 'Auflösen']
	labelTremAction = Label('Handhabung', width = wdt / 2)
	comboTremAction = ComboBox(tremActionOpts, value = setTremActionIndex, width = wdt/2)
	hBoxTremAction = HBox([labelTremAction, comboTremAction], )
	
	checkReset = CheckBox('EINSTELLUNGEN ZURÜCKSETZEN', value=False, width=wdt + 3)
	pluginBoxV = VBox([checkReset, hBoxTremAction])
	pluginBox = HBox([pluginBoxV], text = 'Plugin-Anwendung' , width = wdt)

	vBox = VBox([hBoxEstProperties, spacer, hBoxTremoloSettings, spacer, pluginBox], width = wdt)
	dlg = Dialog('aC.Tremolor', vBox)
	return dlg


if activeScore():
	tempInput = tempfile.mktemp('.capx')
	tempOutput = tempfile.mktemp('.capx')
	activeScore().write(tempInput)
	ScoreAnalyze(tempInput, tempOutput)
	os.remove(tempInput)
	os.remove(tempOutput)

	dialogPass = False
	while not dialogPass:
		dialogOkClicked = dialog().run()
		setReset = checkReset.value()
		if setReset and dialogOkClicked:
			config.dic = {}
			dialogStatus = False
			dialogPass = False
			config.save()
		else:
			dialogPass = True

	if dialogOkClicked:
		setTremRepsIndex = comboTremReps.value()
		setTremStemPrefDirIndex = comboTremStemPrefDir.value()
		setTremActionIndex = comboTremAction.value()
		setTremPolygonBars = checkTremPolygonBars.value()
		setTremBarSymm = checkTremBarHead.value()
		setTremFlagNoteAbbrev = checkTremFlagNoteAbbrev.value()
		setTremBarMaxAngle = 30
		try:
			setTremBarMaxAngle = int(editTremBarMaxAngle.value())
			if setTremBarMaxAngle > 45:
				setTremBarMaxAngle = 45
			elif setTremBarMaxAngle < 0:
				setTremBarMaxAngle = 0
		except:
			pass
	
		config.set('setTremRepsIndex', setTremRepsIndex)
		config.set('setTremStemPrefDirIndex', setTremStemPrefDirIndex)
		config.set('setTremPolygonBars', setTremPolygonBars)
		config.set('setTremBarSymm', setTremBarSymm)
		config.set('setTremBarMaxAngle', setTremBarMaxAngle)
		config.set('setTremFlagNoteAbbrev', setTremFlagNoteAbbrev)

		print(config.dic)
		config.save()

		activeScore().registerUndo("aC.Tremolor")
		tempInput = tempfile.mktemp('.capx')
		tempOutput = tempfile.mktemp('.capx')
		activeScore().write(tempInput)
		ScoreChange(tempInput, tempOutput)
		activeScore().read(tempOutput)
		os.remove(tempInput)
		os.remove(tempOutput)

