# -*- coding: UTF-8 -*-
""" capellaScript -- (C) 2026 Ken Haiker, acaMusic (www.acamusic.de)
			 Implementation: Brian Schueler
>>> aCaL.Utiller
    Rev 0.1.11 (23.07.2026) --- Plugin für Gitarristen (und andere Saiteninstrumente wie Bass, Ukulele usw.)
	||
	Das aCaL.Utiller-Plugin enthält eine kleine Sammlung nützlicher "Schönheitsfunktionen" aus  
	den Bereichen der beiden acaMusic-Paradigmen acaChords (aC) und acaLead (aL). Die in dieses Plugin 
	aufgenommenen Funktionen haben in den eigentlich relevanten Tool-Plugins keinen passenden Platz gefunden
	und wurden deshalb hierhin ausgelagert.
	||
	Der aCaL.Utiller ist derzeit mit folgenden drei Funktionen ausgestattet:
	||
	1. "Horizontales" Positionieren aller Akkord- und Stufensymbole (acaChords), die durch manuelle Verschiebungen
	wieder zentral an den Notenköpfen ausgerichtet werden sollen.
	|
	Eine zusätzliche Sonderbehandlung kommt dabei dem generischen Stufensymbol "I" bzw. "i" zu, das durch seine 
	extreme Schlankheit bei der "Auto"-Positionierung durch Capella optisch immer ein Stück zu weit 
	nach links gesetzt wird. Das vorliegende Plugin kann es auf Wunsch in die optische Mitte eines Notenkopfes
	setzen und dadurch das gesamte Notenbild insgesamt verbessern.
	||
	2. "Horizontales" Positionieren von globalen Fret-Indexen (acaLead), die durch manuelle Verschiebungen wieder
	an den Notenköpfen ausgerichtet werden sollen.
	||
	3. "Zeigen und Verstecken" von Minus-Platzhaltern "-" für Fingersatz-Zahlen und lokale Fret-Offsets
	in mehrstimmigen klassischen Notensätzen (acaLead, aL.Tabber).
	|
	Werden in Capella Mehrton- bzw. Akkord-Noten, die an einem gemeinsamen Anker positioniert sind, zusätzlich 
	durch eine (Einzahl) Spielanweisung wie einen Fingersatz oder einen lokalen Fret-Index dekoriert, dann kann über die 
	von Capella zur Verfügung gestellte Plugin-Schnittstelle (Python) nicht ermittelt werden, für welche der 
	Mehrton-Noten diese Spielanweisung gelten soll. Mindestens die beiden acaMusic-Plugins acaLead und aL.Tabber 
	sind jedoch auf diese Information angewiesen.
	|
	Handelt es sich z.B. um drei Akkord-Noten mit nur einer Fingersatzzahl (z.B. 4 für den kleinen Finger), dann 
	weiß ein Plugin nicht - egal wie eng und präzise an einer Akkordnote die Fingersatzzahl angebracht wurde - 
	ob sie für die höchste, mittlere oder tiefste Note gelten soll. Dasselbe gilt für die Verwendung von lokalen 
	Fret-Offsets.
	|
	Der Trick besteht nun darin, zusätzlich zu der einen Fingerzahl oder zu dem einen lokalen Fret-Offset noch 
	zwei weitere "-"-Zeichen-Platzhalter an den beiden anderen Noten anzubringen, die NICHT gemeint sind. Aus der 
	vertikalen Abfolge " - 4 - " von der höchsten bis zur tiefsten Note können die Plugins dann herauslesen, dass 
	der Fingersatz für die mittlere Note gedacht ist. Ebenso kann aus der Abfolge " - - 0 ", die tiefste Note für 
	den lokalen Fret-Offset vom Plugin identifiziert werden.
	|
	Falls die Dekoration der Noten mit diesen "-"-Platzhaltern als optisch störend empfunden werden, können sie 
	versteckt werden (weiß auf weiß), ohne dabei ihre Funktion zu verlieren, und im Gegenschritt auch wieder
	sichtbar mit ihrer ursprünglichen Farbe gemacht werden.
	|
	Die Verwendung von globalen und lokalen Fret-Offsets wird ausführlich im acaLead-Plugin-Handbuch beschrieben.
	Dort wird auch die Vorgehensweise im Umgang mit den "-"-Platzhaltern, sowohl für Fingersatz als auch 
	lokalen Fret-Index, beschrieben.
	||
	Ein Handbuch gibt es für das aCaL.Utility-Plugin nicht.
	||
    -------------   www.acaMusic.de   -------------
    ||

<<<

History:  Jun 2026 - Erste Version

"""

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

sys.stdout = open(tempfile.gettempdir()+'capella-acal-utiller.log', 'w')

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

def getChordSymProps(noteObj):
	odrawObjects, drawObjects = getChildren(noteObj, "drawObj") 
	coll_x = []
	coll_y = []
	is_above = True
	is_auto = False

	for drawObj in drawObjects:     
		chord = getGroup(drawObj)
		trans = drawObj.gotoChild('transposable', False)
		group = drawObj.gotoChild("group", False)
		validChord = False
		if group is not None or trans is not None:
			chd = chord
			if type(chd) is list:
				chd = chd[0]
			validChord = acaChords.check_chord(chd, setGermanH)
		if trans is not None and validChord:
			basic = drawObj.gotoChild('basic')
			if basic is not None:
				yPlacement = basic.getAttribute('yPlacement')
				placement = basic.getAttribute('placement')
				if yPlacement == 'below':
					is_above = False
				if placement == 'auto':
					is_auto = True
			t_drawObjs = trans.getElementsByTagName('drawObj')
			for t_drawObj in t_drawObjs:
				t_group = t_drawObj.gotoChild('group')
				if t_group is not None:
					t_g_drawObjs = t_group.getElementsByTagName('drawObj')
					objID = 0
					for t_g_drawObj in t_g_drawObjs:
						text = t_g_drawObj.gotoChild('text')
						t_x = text.getAttribute('x')
						t_y = text.getAttribute('y')
						if t_x is not None and t_y is not None and objID == 0:
							coll_x.append(float(t_x))
							coll_y.append(float(t_y))
						objID = objID + 1
		elif group is not None and validChord:
			g_drawObjs = group.getElementsByTagName('drawObj')
			basic = drawObj.gotoChild('basic')
			if basic is not None:
				yPlacement = basic.getAttribute('yPlacement')
				placement = basic.getAttribute('placement')
				if yPlacement == 'below':
					is_above = False
				if placement == 'auto':
					is_auto = True
			objID = 0
			for g_drawObj in g_drawObjs:
				text = g_drawObj.gotoChild('text')
				t_x = text.getAttribute('x')
				t_y = text.getAttribute('y')
				if t_x is not None and t_y is not None:
					if objID == 0:
						coll_x.append(float(t_x))
						coll_y.append(float(t_y))
					objID = objID + 1
	
	if len(coll_x) > 0 and len(coll_y) > 0:
		return sum(coll_x)/len(coll_x), sum(coll_y)/len(coll_y), is_above, is_auto
	return 0, 0, is_above, is_auto

def getChordSyms(noteObj):
	retval = []
	odrawObjects, drawObjects = getChildren(noteObj, "drawObj")
	for drawObj in drawObjects:
		chord = getGroup(drawObj)
		trans = drawObj.gotoChild('transposable', False)
		group = drawObj.gotoChild("group", False)
		if group is not None or trans is not None:
			chd = chord
			if type(chd) is list:
				chd = chd[0]
			validChord = acaChords.check_chord(chd, setGermanH)
		if len(chord) > 0 and (trans is not None or group is not None) and validChord:
			retval.append(str.join("",chord))
	return retval

def getChordSym(noteObj):
	chord = []
	odrawObjects, drawObjects = getChildren(noteObj, "drawObj")
	for drawObj in drawObjects:
		chord = getGroup(drawObj)
		trans = drawObj.gotoChild('transposable', False)
		group = drawObj.gotoChild("group", False)
		validChord = False
		if group is not None or trans is not None:
			chd = chord
			if type(chd) is list and len(chd) > 0:
				chd = chd[0]
			validChord = acaChords.check_chord(chd, setGermanH)
		if len(chord) > 0 and (trans is not None or group is not None) and validChord:
			return chord
	return ''

def getGroup(drawObj):
	retval = []
	try:
		trans = drawObj.gotoChild('transposable', False)
		group = drawObj.gotoChild("group", False)

		if trans is not None:
			print("getGroup - has trans")
			trans = getTransposable(drawObj)
			print("getGroup - trans", str.join('', trans))
			retval.extend(trans)
		elif group is not None:
			print("getGroup - has group")
			xdrawObjs = childElements(group, "drawObj")
			tempTrans = []
			tempRest = []
			for xdrawObj in xdrawObjs:
				trans = getTransposable(xdrawObj)
				# Force transposables becoming first
				if len(trans) > 0:
					print('getGroup - detected trans:', trans)
					tempTrans.extend(trans)
				else:
					tempRest.extend(getGroup(xdrawObj))
			retval.extend(tempTrans)
			retval.extend(tempRest)
		else:
			print("getGroup - no group")
			text = drawObj.gotoChild("text", False)
			print("getGroup - drawObj:", drawObj)
			print("getGroup - text:", text)
			if text is not None:
				print("getGroup - has text")
				height = 0
				font = text.gotoChild('font', False)
				if font is not None:
					height = font.getAttribute('height')
				content = text.getElementsByTagName('content')
				if content is not None and len(content) > 0:
					print("getGroup - content", content)
					data = content[0].firstChild.data
					if len(data) > 0:
						if data[0] == 'S':
							data = data.replace('S','#')
						elif data[0] == 'Q':
							data = data.replace('Q','b')
						elif data == '/':
							if height > 10:
								data = '<altslash>'
					data = data.replace(" ","")
					print("getGroup - data", data)
					retval.append(data)
	except:
		print("getGroup - No chord symbol found")
	print("getGroup - chord", str.join('', retval))
	return retval

def getTransposable(drawObj):
	retval = []
	try:
		transposable = gotoChild(drawObj, 'transposable', False)
		if transposable is not None:
			print("getTransposable - has transposable")
			base = transposable.getAttribute('base')
			transDrawObjects = childElements(transposable,"drawObj")
			for transDrawObj in transDrawObjects:
				transBase = transDrawObj.getAttribute('base')
				if base is not None and transBase == base:
					retval.extend(getGroup(transDrawObj))
		else:
			print("getTransposable - no transposable")
	except:
		print("getTransposable - No trans chord symbol found")
	return retval

class ChordType:
	def __init__(self, tokenList, major, minor, group, coeff, allow_inversions):
		self.tokenList = tokenList
		self.major = major
		self.minor = minor
		self.group = group
		self.coeff = coeff
		self.allow_inversions = allow_inversions

	def __repr__(self):
		return 'Chord: '+str(self.tokenList)+'), coeff: '+str(self.coeff)+', inv allowed: '+str(self.allow_inversions)+'\n'

class AcaChords:
	def __init__(self):
		self.min_tone = 'C2'
		self.max_tone = 'B3'
		self.octave = 2
		self.chord_types = [
			# Chords based on a Major triad
			ChordType([""],					       True,  False, "maj", [0, 4, 3],       True),  # "Major"
			ChordType(["6"],					      True,  False, "maj", [0, 4, 3, 2],    False), # "Sixth"
			ChordType(["7"],					      True,  False, "maj", [0, 4, 3, 3],    True),  # "Seventh"
			ChordType(["maj7"],					   True,  False, "maj", [0, 4, 3, 4],    True),  # "Major seventh"
			ChordType(["7(b5)", "7/b5", "7b5"],			   False, False, "b5", [0, 4, 2, 4],    False), # "Seven flat five"
			ChordType(["7(#5)", "7/#5", "7#5"],			   False, False, "#5", [0, 4, 4, 2],    False), # "Seven sharp five"
			ChordType(["maj7(#5)", "maj7/#5", "maj7#5"],		  False, False, "#5", [0, 4, 4, 3],    False), # "Major seventh sharp five"
			ChordType(["7(9)", "7/9", "9", "7add9"],		      True,  False, "maj", [0, 4, 3, 3, 4], False), # "Seventh add ninth"
			ChordType(["maj7(9)", "maj7/9", "maj9", "maj7add9"],	  True,  False, "maj", [0, 4, 3, 4, 3], False), # "Major seventh add ninth"
			ChordType(["7(#11)", "7/#11", "7#11", "7add#11"],	     True,  False, "maj", [0, 4, 3, 3, 8], True),  # "Seventh augmented eleventh"
			ChordType(["maj7(#11)", "maj7/#11", "maj7#11", "maj7add#11"], True,  False, "maj", [0, 4, 3, 4, 7], True),  # "Major seventh augmented eleventh"
			ChordType(["6(9)", "6/9", "6add9"],			   True,  False, "maj", [0, 4, 3, 2, 5], False), # "Six-nine"
			ChordType(["(9)", "add9"],				    True,  False, "maj", [0, 4, 3, 7],    True),  # "Add ninth"
			# Chords based on a Minor triad
			ChordType(["m"],					      False, True,  "min", [0, 3, 4],       True),  # "Minor"
			ChordType(["m6"],					     False, True,  "min", [0, 3, 4, 2],    True),  # "(Minor) sixth"
			ChordType(["m7"],					     False, True,  "min", [0, 3, 4, 3],    False), # "(Minor) seventh"
			ChordType(["mmaj7"],					  False, True,  "min", [0, 3, 4, 4],    False), # "(Minor) major seventh"
			ChordType(["m7(b5)", "m7/b5", "m7b5"],			False, False, "b5", [0, 3, 3, 4],    False), # "Half diminished seventh"
			ChordType(["m7(9)", "m7/9", "m9", "m7add9"],		  False, True,  "min", [0, 3, 4, 3, 4], True),  # "(Minor) seventh add ninth"
			ChordType(["m7(11)", "m7/11", "m11", "m7add11"],	      False, True,  "min", [0, 3, 4, 3, 7], False), # "(Minor) seventh add eleventh"
			ChordType(["m6(9)", "m6/9", "m6add9"],			False, True,  "min", [0, 3, 4, 2, 5], False), # "(Minor) six-nine"
			ChordType(["m(9)", "madd9"],				  False, True,  "min", [0, 3, 4, 7],    True),  # "(Minor) add ninth"
			# Diminished Chords (minor Chords with deminished fifths)
			ChordType(["dim", "o", "°", "mb5"],			   False, False, "dim", [0, 3, 3],       False), # "Diminished"
			ChordType(["dim7", "o7", "°7"],			       False, False, "dim", [0, 3, 3, 3],    False), # "Diminished seventh"
			# Suspended Chords (3rd suspended and replaced by a 4th or 2nd)
			ChordType(["sus2"],					   False, False, "sus2", [0, 2, 5],       False), # "Suspended second"
			ChordType(["7sus2", "sus27", "sus2dom7"],		     False, False, "sus2", [0, 2, 5, 3],    False), # "Seventh Suspended second"
			ChordType(["sus4", "sus"],				    False, False, "sus4", [0, 5, 2],       False), # "Suspended fourth"
			ChordType(["7sus4", "sus47", "sus7", "sus4dom7"],	     False, False, "sus4", [0, 5, 2, 3],    False), # "Seventh suspended fourth"
			# Augmented Chords (Major Chords with augmented fifths)
			ChordType(["aug", "+"],				       False, False, "aug", [0, 4, 4],       False), # "Augmented fifth"
			# Power Chords (missing 3rd)
			ChordType(["5", "no3"],				       False, False, "pow", [0, 7],	  False), # "Power"
		]
		self.key_notes = "CDEFGAB"
		self.key_note_tone_number = (1, 3, 5, 6, 8, 10, 12)
		self.use_inversion = True
		self.rebuild_chord_maps()
	
	def rebuild_chord_maps(self):
		self.chords_by_token_map = {}
		self.chords_by_step_coeff_map = {}

		for chord_type in self.chord_types:
			for token in chord_type.tokenList:
				self.chords_by_token_map.update({'x'+token: chord_type})
				for prio in range(0,len(chord_type.coeff)):
					if chord_type.allow_inversions or prio == 0:
						path = ""
						coeff, tonic_index = self.get_inversion(chord_type.coeff, prio)
						for coeff_elem in coeff:
							path = path + str(coeff_elem)
						path = path + str(prio)
						path = path + str(tonic_index)
						self.chords_by_step_coeff_map.update({path: chord_type})

		print(self.chords_by_token_map)
		print(self.chords_by_step_coeff_map)

	def get_token(self, s):
		if s[0] == '#':
			return ('sharp', 1)
		elif s[0] == 'b':
			return ('flat', 1)
		elif s[0] >= 'A' and s[0] <= 'G':
			return ('base_tone', 1)
		elif len(s) > 1:
			if s[0] == '-':
				return ('unknown', 2)
		return ('unknown', 1)

	def get_base_tone(self, s):
		try:
			index = self.key_notes.index(s)
			if index >= 0:
				return self.key_note_tone_numver[index]
		except:
			pass
		return 0

	def aligned_tone_num(self, tone_num):
		tn = tone_num
		while tn <= 0:
			tn = tn + 12
		while tn > 12:
			tn = tn - 12
		return tn

	def has_sharps_circle(self, base_tone, minor, double_def_sharp = True):
		circleOfFifthsMajSharp = [ 1, 3, 5, 8, 10, 12 ]
		circleOfFifthsMajFlat  = [ 2, 4, 6, 9, 11 ]
		circleOfFifthsMinSharp = [ 2, 5, 7, 9, 10, 12 ]
		circleOfFifthsMinFlat  = [ 1, 3, 6, 8, 11 ]
		base_tone = self.aligned_tone_num(base_tone)
		if minor:
			if base_tone in circleOfFifthsMinSharp:
				return True
			elif base_tone in circleOfFifthsMinFlat:
				return False
			else:
				return double_def_sharp
		else:
			if base_tone in circleOfFifthsMajSharp:
				return True
			elif base_tone in circleOfFifthsMajFlat:
				return False
			else:
				return double_def_sharp

	def has_sharps(self, tone, minor, double_def_sharp = True):
		major_chords_sharp = (1, 3, 5, 8, 10, 12)
		minor_chords_sharp = (2, 5, 7, 9, 10, 12)
		tone_num = self.aligned_tone_num(tone)
		if minor:
			for i in range(0, len(minor_chords_sharp)):
				if minor_chords_sharp[i] == tone_num:
					return True
			if tone_num == 4:
				return double_def_sharp   # 4 ^= Eb/D# (inverted)
		else:
			for i in range(0, len(major_chords_sharp)):
				if major_chords_sharp[i] == tone_num:
					return True
			if tone_num == 7:
				return double_def_sharp   # 7 ^= F#/Gb
		return False

	# Return: coeff, tonic_index
	def get_inversion(self, coeff, inv_num):
		tone_vals = []
		tone_val_sum = 0
		num_tones = len(coeff)
		for i in range(0, num_tones):
			tone_val_sum = tone_val_sum + coeff[i]
			tone_vals.append(tone_val_sum)
		for i in range(0, min(inv_num, num_tones)):
			tone_vals[i] = tone_vals[i] + 12
		tonic_val = tone_vals[0]
		tone_vals.sort()
		tonic_i = tone_vals.index(tonic_val)
		new_tone_steps = [0]
		for i in range(1,len(tone_vals)):
			new_tone_steps.append(tone_vals[i] - tone_vals[i-1])
		return (new_tone_steps, tonic_i)

	def chord_to_tones_basstone(self, chord, basstone, octave, transpose = 0):
		retval = []
		print('chord_to_tones_basstone - chord:', chord)
		print('chord_to_tones_basstone - bass:', basstone)
		chord_type = self.get_chord_type(chord)
		print('chord_to_tones_basstone - type:', chord_type)
		coeff = chord_type.coeff
		print('chord_to_tones_basstone - coeff:', coeff)
		tones_n = self.chord_to_tones(chord, octave)
		tones = []
		for tone in tones_n:
			tones.append(re.sub('[0-9]','',tone))
		print('chord_to_tones_basstone - tones:', tones)
		bass_index = 0
		if tones.count(basstone) > 0:
			bass_index = tones.index(basstone)
		print('chord_to_tones_basstone - bass index:', bass_index)
		coefficients = []
		tones = []
		for i in range(0, len(coeff)):
			tone_steps, tonic_idx = self.get_inversion(coeff, i)
			print('chord_to_tones_basstone - ts, ti:', tone_steps, tonic_idx)
			pitch = 0
			for i in range(0, tonic_idx+1):
				pitch = pitch - tone_steps[i]

			tones = []
			for step in tone_steps:
				pitch = pitch + step
				base_tone = self.get_chord_prefix(chord)
				ntone = self.midiPitch(base_tone+str(octave))+pitch
				tone = self.pitchFromMidi(ntone)
				tones.append(tone)

			first_tone = re.sub('[0-9]', '', tones[0])
			print('chord_to_tones_basstone - ts:', tones)
			if first_tone == basstone:
				print('chord_to_tones_basstone - returned tones:', tones)
				return tones
			basstone_num = 0
			try:
				basstone_num = int(basstone)
			except:
				pass
			if basstone_num > 0:
				root = self.get_chord_prefix(chord)
				if acaChords.is_minor(chord):
					scale = acaChords.get_minor_key_scale(root)
					print('chord_to_tones_basstone - '+root+'m-scale:', scale)
				else:
					scale = acaChords.get_major_key_scale(root)
					print('chord_to_tones_basstone - '+root+'-scale:', scale)
				try:
					if scale[basstone_num-1] == first_tone:
						print('chord_to_tones_basstone - returned tones:', tones)
						return tones
				except:
					pass
		return self.chord_to_tones(chord, octave, transpose = 0)

	def get_keys_from_circle(self, index):
		major_keys_pos = ['C','G','D','A','E','B','F#' ]
		major_keys_neg = ['C','F','Bb','Eb','Ab','Db','Gb' ]
		minor_keys_pos = ['Am','Em','Bm','F#m','C#m','G#m','D#m' ]
		minor_keys_neg = ['Am','Dm','Gm','Cm','Fm','Bm','Ebm' ]
		while index > 6:
			index = index - 12
		while index < -6:
			index = index + 12
		if index < 0:
			return (major_keys_neg[-index], minor_keys_neg[-index])
		return (major_keys_pos[index], minor_keys_pos[index])

	def force_sign(self, tone_s, sharp = True):
		flats  = ['Db', 'Eb', 'Gb', 'Ab', 'Bb']
		sharps = ['C#', 'D#', 'F#', 'G#', 'A#']
		srcs = flats
		tgts = sharps
		if sharp == False or sharp == 'b':
			srcs = sharps
			tgts = flats

		# in case of a list
		if type(tone_s) is list:
			retval = []
			for tone in tone_s:
				for i in range(0, 5):
					tone = re.sub('^'+srcs[i], tgts[i], tone)	
				retval.append(tone)
			return retval

		# in case of a string
		for i in range(0, 5):
			tone_s = re.sub('^'+srcs[i], tgts[i], tone_s)	
		return tone_s

	def get_major_key_scale(self, root):
		step_widths = [2, 2, 1, 2, 2, 2]
		return self.get_key_scale(root, step_widths)

	def get_minor_key_scale(self, root):
		step_widths = [2, 1, 2, 2, 1, 2]
		return self.get_key_scale(root, step_widths)

	def get_key_scale(self, root, step_widths):
		retval = []
		tone = root
		for step_width in step_widths:
			retval.append(tone)
			tone = self.pitchFromMidi(self.midiPitch(tone+'2')+step_width)
			tone = re.sub('[0-9]', '', tone)
		retval.append(tone)
		return retval

	def get_chord_type_by_token(self, s):
		return self.chords_by_token_map.get('x'+s)

	def get_chord_variants_by_coefficients(self, coeff):
		path = ""
		for coeff_elem in coeff:
			path = path + str(coeff_elem)
		variants = []
		sub_map = { key: val for key, val in self.chords_by_step_coeff_map.items() if key.startswith(path) and len(key) == len(path)+2 }
		for item in sub_map:
			variants.append((int(item[-2]), int(item[-1])))
		return variants

	def get_chord_type_by_coefficients(self, coeff, prio = 0, tonic_index = 0):
		path = ""
		for coeff_elem in coeff:
			path = path + str(coeff_elem)
		path = path + str(prio)
		path = path + str(tonic_index)
		return self.chords_by_step_coeff_map.get(path)

	def get_chord_type(self, chord):
		return self.get_chord_type_by_token(self.get_chord_suffix(chord))

	def is_minor(self, chord):
		chd = self.get_chord_type(chord)
		if chd is not None:
			return chd.minor
		return False

	def is_major(self, chord):
		chd = self.get_chord_type(chord)
		if chd is not None:
			return chd.major
		return False

	def get_octave_shift(self, tone_num, octave):
		shift = 0
		octave = octave + (tone_num - 1) / 12
		tone_num = (tone_num - 1) % 12 + 1

		while tone_num + 12 * octave < self.midiPitch(self.min_tone):
			octave = octave + 1
			shift = shift + 1
		while tone_num + 12 * octave > self.midiPitch(self.max_tone):
			octave = octave - 1
			shift = shift - 1
		return shift

	def get_transposed_chord_name(self, chord, transpose, prefer_sharp):
		if chord == '':
			return ''
		if chord[0].upper() not in ['A','B','C','D','E','F','G']:
			return ''
		tonic = self.get_tonic(chord)
		base_tone = self.midiPitch(tonic+'2')
		new_tone = self.pitchFromMidi(base_tone + transpose, prefer_sharp)
		new_tone_wo_octave = ''
		for s in new_tone:
			if s < '0' or s > '9':
				new_tone_wo_octave = new_tone_wo_octave + s
		return new_tone_wo_octave + self.get_chord_suffix(chord)

	def check_chord(self, chord, german = False):
		global setGermanH
		sc = self.step_to_chord(self.chord_angsax(chord, german), 'C')
		if sc != '':
			return True
		tones = self.chord_to_tones(self.chord_angsax(chord, german),2)
		if len(tones) > 0:
			return True
		return False

	def chord_to_step(self, chord, key, prefer_sharp = True, seven_flat = True):
		try:
			key_range = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
			prefix = self.get_chord_prefix(chord)
			if len(prefix) < 1:
				return ''
			elif prefix[0].upper() not in key_range:
				return ''
			suffix = self.get_chord_suffix(chord)
			key_root = self.get_chord_prefix(key)
			minor = key.endswith('m')
			print('chord_to_step - chord', chord)
			print('chord_to_step - key', key)
			print('chord_to_step - key_root', key_root)
			print('chord_to_step - prefix', prefix)
			print('chord_to_step - suffix', suffix)
			roman_steps = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII']
			step_mat_flat = ['1', '1#', '2', '2#', '3', '4', '4#', '5', '5#', '6', '7', '7#']
			step_mat_major = ['1', '1#', '2', '2#', '3', '4', '4#', '5', '5#', '6', '6#', '7']
			step_mat_minor = ['1', '1#', '2', '3', '3#', '4', '4#', '5', '6', '6#', '7', '7#']
			key_pitch = acaChords.midiPitch(key_root+'2')
			chord_pitch = acaChords.midiPitch(prefix+'2')
			print('chord_to_step - key_pitch', key_pitch)
			print('chord_to_step - chord_pitch', chord_pitch)
			pitch = chord_pitch - key_pitch
			pitch = pitch % 12
			if minor:
				step = step_mat_minor[pitch]
			elif seven_flat:
				step = step_mat_flat[pitch]
			else:
				step = step_mat_major[pitch]
			step0 = step[0]
			if prefer_sharp == False:
				step0i = int(step0) % 7 + 1
				step = str(step0i) + step[1:].replace('#','b')
			stepi = int(step[0])
			step = roman_steps[stepi-1] + step[1:]
			print('chord_to_step - pitch', pitch)
			print('chord_to_step - step', step)
			return step + suffix
		except:
			pass
		return ''
	
	def step_to_chord(self, step, key, prefer_sharp = True, seven_flat = True):
		try:
			prefix = self.get_chord_prefix(step)
			if len(prefix) < 1:
				return ''
			elif prefix[0].upper() not in ['I', 'V']:
				return ''
			dst_prefix = self.get_chord_prefix(key)
			minor = key.endswith('m')
			print('step_to_chord - step', step)
			print('step_to_chord - key', key)
			print('step_to_chord - prefix', prefix)
			print('step_to_chord - prefix(key)', dst_prefix)
			suffix = self.get_chord_suffix(step)
			print('step_to_chord - suffix', suffix)
			steps = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII']
			keys_flat = ['C', 'D', 'E', 'F', 'G', 'A', 'Bb']
			keys_root = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
			keys_minor = ['C', 'D', 'D#', 'F', 'G', 'G#', 'A#']
			tune = re.sub('.*([#b])', '\\1', prefix)
			root = re.sub('(.*)[#b]', '\\1', prefix)
			print('step_to_chord - tune', tune)
			print('step_to_chord - prefix (filtered)', prefix)
			if steps.count(root) > 0:
				step_num = steps.index(root)
				if minor:
					key = keys_minor[step_num]
				elif seven_flat:
					key = keys_flat[step_num]
				else:
					key = keys_root[step_num]
				abs_step = self.midiPitch(key+'2')
				abs_ref_step = self.midiPitch(dst_prefix+'2')
				offset = abs_ref_step + abs_step
				if tune == '#':
					offset = offset + 1
				elif tune == 'b':
					offset = offset - 1
				return self.get_transposed_chord_name('C'+suffix, offset, prefer_sharp)
		except:
			pass
		return ''
		
	def chord_angsax(self, chord, parse_german_bflat = False):
		scale = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
		roman_steps = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII']
		if len(chord) > 0:
			if re.match('[1-7]', chord[0]):
				index = ord(chord[0]) - ord('1')
				chord = roman_steps[index] + chord[1:]

		if parse_german_bflat:
			if len(chord) > 0:
				if chord[0].lower() == 'b':
					chord = chord[0]+'b'+chord[1:]
				elif chord[0] == 'h': 
					chord = 'b'+chord[1:]
				elif chord[0] == 'H': 
					chord = 'B'+chord[1:]

		if len(chord) > 1:
			if chord[0].lower() in scale:
				tone = chord[0]
				sign = ''
				if chord.lower()[1:].startswith('is'):
					sign = '#'
					chord = chord[3:]
				elif chord.lower()[1:].startswith('s'):
					if not chord.lower()[2:].startswith('us'):
						sign = 'b'
						chord = chord[2:]
					else:
						chord = chord[1:]
				elif chord.lower()[1:].startswith('es'):
					sign = 'b'
					chord = chord[3:]
				else:
					chord = chord[1:]
				chord = tone + sign + chord

		if len(chord) > 0:
			if chord[0].islower():
				skip = 0
				if len(chord) > skip and chord[skip].lower() in scale:
					skip = skip + 1
				while len(chord) > skip and chord[skip].lower() in ['i', 'v']:
					skip = skip + 1
					chord = chord[:skip].upper()+chord[skip:]
				if skip > 0:
					if len(chord) > skip:
						if chord[skip] in ['b', '#']:
							skip = skip + 1
					chord = chord[0].upper()+chord[1:skip]+'m'+chord[skip:]
		return chord

	def chord_to_tones(self, chord, octave, transpose = 0):
		print('chord to tones - chord:', chord)
		use_sharp = False
		tones = []
		coefficients = self.get_chord_type_by_token("").coeff   # major chord is default
		print('chord to tones - coeff:', str(coefficients))
		if len(chord) == 0:
			return []
		chord_suffix = self.get_chord_suffix(chord).split('[')[0]
		print('chord to tones - suffix:', chord_suffix)
		chord_type = self.get_chord_type_by_token(chord_suffix)
		print('chord to tones - type:', chord_type)
		if chord_type is not None:
			coefficients = chord_type.coeff
		else:
			return []
		base_tone = None
		try:
			base_tone = self.midiPitch(self.get_chord_prefix(chord)+str(octave))
		except:
			pass
		if base_tone is None:
			return []
		is_minor = chord_type.minor
		if len(chord) >= 2:
			if chord[1] == '#':
				use_sharp = True
		use_sharp = self.has_sharps(base_tone + transpose, is_minor, use_sharp)
		shift = 0
		tone_step = 0
		print('chord to tones - use_inv, allow_inv', self.use_inversion, chord_type.allow_inversions)
		if not self.use_inversion or not chord_type.allow_inversions:
			for i in range(0, len(coefficients)):
				tone_step = tone_step + coefficients[i]
				shift = self.get_octave_shift(base_tone + tone_step + transpose, 0)
				octave = octave + shift

		# generate the tones
		tone_step = 0
		for i in range(0, len(coefficients)):
			tone_step = tone_step + coefficients[i]
			if self.use_inversion and chord_type.allow_inversions:
				shift = self.get_octave_shift(base_tone + tone_step + transpose, 0)
			tones.append(self.pitchFromMidi(base_tone + tone_step + transpose + 12 * (shift) , use_sharp))
		print('chord to tones - returned tones:', tones)
		return tones

	def get_tonic(self, chord):
		tones = self.chord_to_tones(chord, 0, 0)
		if len(tones) > 0:
			return tones[0]
		return ''

	def get_chord_prefix(self, chord):
		try:
			suffix = self.get_chord_suffix(chord)
			suffix_begin = chord.index(suffix)
			prefix = chord
			if len(suffix) > 0:
				prefix = chord[0:suffix_begin]
		except:
			return ''
		return prefix

	def get_chord_suffix(self, chord):
		repeat = True
		chrd = chord
		while repeat:
			repeat = False
			if len(chrd) >= 1:
				if (chrd[0]>='A' and chrd[0]<='G') or (chrd[0]=='b' or  chrd[0]=='#') or (chrd[0]=='I' or  chrd[0]=='V') or (chrd[0]=='i' or  chrd[0]=='v'):
					chrd = chrd[1:]
					repeat = True
		return chrd

	def altered_note(self, tone, root):
		tone_val = self.midiPitch(tone)
		octless_tone_val = (tone_val + 24 - 1) % 12 + 1
		mods = ['bb', 'b', '', '#', '##']
		contains_octave = False
		for s in tone:
			if s >= '0' and s <= '9':
				contains_octave = True

		for mod in mods:
			octless_altered_tone_val = self.midiPitch(root+mod)
			if octless_altered_tone_val == octless_tone_val or (octless_altered_tone_val + 6) % 12 == (octless_tone_val + 6) % 12:
				octave = (tone_val - octless_altered_tone_val) / 12
				if contains_octave:
					return root + mod + str(octave)
				else:
					return root + mod
		return ''

	def int_to_base_tone_no_chord(self, tone_num, sharp):
		tones_flat = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"]
		tones_sharp = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "Bb", "B"]
		tone = ''
		while tone_num < 0:
			tone_num = tone_num + 12
		tone_num = tone_num % 12
		if sharp:
			tone = tone + tones_sharp[tone_num]
		else:
			tone = tone + tones_flat[tone_num]
		return tone

	def tones_to_chord(self, tones):
		tone_nums = []
		tone_steps = [0]
		sharps_found = 0
		flats_found = 0
		use_enharp_sharp = False
		for tone in tones:
			tone_nums.append(self.midiPitch(tone))
			if tone[1] == '#':
				sharps_found = sharps_found + 1
			elif tone[1] == 'b':
				flats_found = flats_found + 1
		if sharps_found > flats_found:
			use_enharp_sharp = True
		tone_nums.sort()
		index = 0
		for tone_num in tone_nums:
			if index > 0:
				tone_steps.append(tone_num - tone_nums[index - 1])
			index = index + 1	
		chord_variants = self.get_chord_variants_by_coefficients(tone_steps)
		tonic_index = 99
		chord_type = None

		if chord_variants >= 1:
			for chord_variant in chord_variants:
				v_prio = chord_variant[0]
				v_tonic = chord_variant[1]
				x_chord_type = self.get_chord_type_by_coefficients(tone_steps, v_prio, v_tonic)
				if x_chord_type is not None and v_tonic < tonic_index:
					chord_type = x_chord_type
					tonic_index = v_tonic
		else:
			return ''

		if chord_type is not None:
			chord_suffix = chord_type.tokenList[0]
		else:
			chord_suffix = None
		if chord_suffix is not None:
			tone_num = tone_nums[tonic_index]
			chord_prefix = self.int_to_base_tone_no_chord(tone_num, self.has_sharps(tone_num, self.is_minor('C'+chord_suffix), use_enharp_sharp))
			return chord_prefix + chord_suffix
		return ''

	def midiPitch(self, tone):
		step = 0
		i = self.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 pitchFromMidi(self, n, use_sharp = True):
		octave = int(n)/12
		itone = int(n)%12
		tonesSharp = 'CCDDEFFGGAAB'
		tonesFlat  = 'CDDEEFGGAABB'
		steps      = '010100101010'
		toneSharp = tonesSharp[itone]
		toneFlat  = tonesFlat[itone]
		step = -int(steps[itone])
		tone = toneFlat

		# Use Sharp sign if set
		if use_sharp:
			tone = toneSharp
			step = -step

		sign = ''
		if step < 0:
			sign = 'b'
		elif step > 0:
			sign = '#'
		return tone+sign+str(octave)

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):
	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]
	return (base+octave, step)

acaChords = AcaChords()

def getSimpleTexts(noteObj):
	retval = []
	odrawObjs, drawObjs = getChildren(noteObj, 'drawObj')
	
	for drawObj in drawObjs:
		texts  = childElements(drawObj, 'text')
		if len(texts) > 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'))
					content = re.sub('\n$', '', content)
					retval.append(content)
				except:
					pass
	return retval

# 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 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 getChordSymPosOffset(drawObj):
	xOffs, yOffs = (None, None)
	if drawObj.localName == 'transposable':
		trans = drawObj
	else:
		trans = drawObj.gotoChild('transposable', False)
	if drawObj.localName == 'group':
		group = drawObj
	else:
		group = drawObj.gotoChild("group", False)
	if group is not None:
		groupDrawObjs = childElements(group, 'drawObj')
		for groupDrawObj in groupDrawObjs:
			xOffs, yOffs = getChordSymPosOffset(groupDrawObj)
			if xOffs is not None and yOffs is not None:
				return (xOffs, yOffs)
	elif trans is not None:
		transDrawObjs = childElements(trans, 'drawObj')
		for transDrawObj in transDrawObjs:
			xOffs, yOffs = getChordSymPosOffset(transDrawObj)
			if xOffs is not None and yOffs is not None:
				return (xOffs, yOffs)
	else:
		# Remaining regular text
		if drawObj.localName == 'text':
			text = drawObj
		else:
			text = drawObj.gotoChild('text', False)
		if text is not None:
			x_t = text.getAttribute('x')
			y_t = text.getAttribute('y')
			try:
				xOffs = float(x_t)
				yOffs = float(y_t)
			except:
				pass
	return (xOffs, yOffs)

def moveChordSym(drawObj, xOffs, yOffs):
	if drawObj.localName == 'transposable':
		trans = drawObj
	else:
		trans = drawObj.gotoChild('transposable', False)
	if drawObj.localName == 'group':
		group = drawObj
	else:
		group = drawObj.gotoChild("group", False)
	if group is not None:
		groupDrawObjs = childElements(group, 'drawObj')
		for groupDrawObj in groupDrawObjs:
			moveChordSym(groupDrawObj, xOffs, yOffs)
	elif trans is not None:
		transDrawObjs = childElements(trans, 'drawObj')
		for transDrawObj in transDrawObjs:
			moveChordSym(transDrawObj, xOffs, yOffs)
	else:
		# Remaining regular text
		if drawObj.localName == 'text':
			text = drawObj
		else:
			text = drawObj.gotoChild('text', False)
		if text is not None:
			x = 0.0
			y = 0.0
			x_t = text.getAttribute('x')
			y_t = text.getAttribute('y')
			try:
				x = float(x_t)
				y = float(y_t)
			except:
				pass
			text.setAttribute('x', str(x+xOffs))
			text.setAttribute('y', str(y+yOffs))


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 getChordOrStep(noteObj):
	duration = getObjDuration(noteObj)
	if duration is not None:
		simpleTexts = getSimpleTexts(noteObj)
		text = getChordSym(noteObj)
		if len(text) > 0 and simpleTexts.count('*') == 0:
			chordJoined = str.join('',text)
			chordJoined = acaChords.chord_angsax(chordJoined, setGermanH)
			try:
				inum = int(chordJoined)
				chordJoined = ''
			except:
				chordNoAlt = re.sub('(.*)<altslash>.+', '\\1', chordJoined)
				valid = acaChords.check_chord(chordNoAlt, setGermanH)
				if valid:
					return chordJoined
				else:
					return ''
	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 getMarkedChordPitches(score):
	sys1 = curSelection()[0][0]
	sys2 = curSelection()[1][0]
	sta1 = curSelection()[0][1]
	sta2 = curSelection()[1][1]
	voi1 = curSelection()[0][2]
	voi2 = curSelection()[1][2]
	nObj1 = curSelection()[0][3]
	nObj2 = curSelection()[1][3]

	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]
					dura = getObjDuration(obj)
					if dura is not None:
						if (systemID, staffID, voiceID) == (sys1, sta1, voi1) and (systemID, staffID, voiceID) == (sys2, sta2, voi2):
							if nObj1+1 == nObj2 or nObj2+1 == nObj1:
								if nObj1 == objID:
									return getPitches(obj)
								elif nObj2 == objID:
									return getPitches(obj)
					objID = objID + 1
				voiceID = voiceID + 1
			staffID = staffID + 1
		systemID = systemID + 1
	return None
	
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 readChordFile(score):
	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 obj in objs:
					if obj.localName == 'chord':
						chds = getChordSyms(obj)
						simple_texts = getSimpleTexts(obj)
						if len(chds) > 0:
							cts = []
							for chd in chds:
								cts.append(acaChords.get_chord_suffix(chd))
							print('readChordFile: cts:', cts)
							oheads, heads = getChildren(obj, 'head')
							m_pitches = []
							for head in heads:
								pitch = cap2acaTone(head,False)
								m_pitch = acaChords.midiPitch(pitch)
								m_pitches.append(m_pitch)
							m_pitches.sort()
							new_coeff = [0]
							old_m_pitch = None
							for m_pitch in m_pitches:
								if old_m_pitch is not None:
									diff = m_pitch - old_m_pitch
									new_coeff.append(diff)
								old_m_pitch = m_pitch
							print('readChordFile: coeff:', new_coeff)
							ct = acaChords.get_chord_type_by_coefficients(new_coeff) 
							print('readChordFile: chordType:', ct)
							is_embedded = ct is not None
							print('readChordFile: already in acaChords:',is_embedded)
							if ct is not None:
								new_token_list = ct.tokenList
								for token in cts:
									if token not in new_token_list:
										print('readChordFile: adding token',token,'for',chd)
										new_token_list.append(token)
								new_ct = ChordType(new_token_list, ct.major, ct.minor, ct.group, ct.coeff, ct.allow_inversions)
								acaChords.chord_types.remove(ct)
								acaChords.chord_types.append(new_ct)
								acaChords.rebuild_chord_maps()
							else:
								major = False
								minor = False
								group = ''
								if new_coeff[0:3] == [0, 4, 3]:
									major = True
								elif new_coeff[0:3] == [0, 3, 4]:
									minor = True
								elif new_coeff[0:3] == [0, 3, 3]:
									group = "dim"
								elif new_coeff[0:3] == [0, 4, 4]:
									group = "aug"
								elif new_coeff[0:3] == [0, 2, 5]:
									group = "sus2"
								elif new_coeff[0:3] == [0, 5, 2]:
									group = "sus4"
								allow_inv = False
								for text in simple_texts:
									if text.lower().strip() == 'yes':
										allow_inv = True
								new_ct = ChordType(cts, major, minor, group, new_coeff, allow_inv)
								for token in cts:
									ct = acaChords.get_chord_type_by_token(token)
									if ct is not None:
										print('readChordFile: replace',ct.tokenList[0],'by', new_ct)
										acaChords.chord_types.remove(ct)
										acaChords.chord_types.append(new_ct)
										acaChords.rebuild_chord_maps()
									else:
										print('readChordFile: adding new chord', new_ct)
										acaChords.chord_types.append(new_ct)
										acaChords.rebuild_chord_maps()
					objID = objID + 1
				voiceID = voiceID + 1
			staffID = staffID + 1
		systemID = systemID + 1
	print('readChordFile: chord_types', acaChords.chord_types)
	print('readChordFile: Score has '+str(systemID)+' systems.')

def aCaL_UtillerMain(score, noteRange = None):
    
	# Actions (setPluginActionIndex)
	# 0 = RESET
	# 1 = Center Chord Symbols
	# 2 = Center Global Fret Indexes
	if setPluginActionIndex == 1:
		centerChordSyms(score, setCenterIsyms)
	elif setPluginActionIndex == 2:
		centerGFI(score)
	elif setPluginActionIndex == 3:
		if setPlaceholderType == 0:
			criteria = 'fingering'
		else:
			criteria = 'localFretIndex'
		showHidePlaceholdersByCriteria(score, setShowPlaceholders == 1, criteria)

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 getLocalFretIndex(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':
						try:
							_ = int(content.strip())
							return content.lower().replace('fret', '').replace('-','').replace('!','')
						except:
							pass
			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

def getLocalFretIndexCount(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]
					lfi = getLocalFretIndex(obj)
					if len(lfi) > 0:
						count = count + 1
	return count

def getTextColor(drawObj):
	text = drawObj.gotoChild('text', False)
	if text is None:
		return None
	font = text.gotoChild('font', True)
	color = font.getAttribute('color')
	if color == '':
		return None
	return color

def setTextColor(drawObj, new_color):
	text = drawObj.gotoChild('text', False)
	if text is None:
		return
	font = text.gotoChild('font', True)
	if new_color is None:
		color = font.getAttribute('color')
		if color is not None and color != '':
			font.removeAttribute('color')
	else:
		font.setAttribute('color', new_color)

def showHidePlaceholdersByCriteria(score, show = False, criteria = ''):
	hide_color = 'FFFFFF'

	osystens, systems = getChildren(score, 'system')
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:	
			ovoices, voices = getChildren(staff, 'voice')
			for voice in voices:
				oNoteObjs, noteObjs = getChildren(voice, 'noteObjects')
				for noteObj in noteObjs:
					chordDrawObjs = getObjTextObjsByCriteria(noteObj, criteria)
					chordDrawObjsSimple = getObjTextObjsByCriteria(noteObj, 'simple')

					# Show Hidden Local Fret Indexes
					if show == True and criteria == 'localFretIndex':
						for chordDrawObj in chordDrawObjsSimple:
							content = getTextContent(chordDrawObj)
							if content is not None:
								if content == '-':
									color = getTextColor(chordDrawObj)
									if color == hide_color:
										setTextColor(chordDrawObj, estFretIndexFontColor)

					for chordDrawObj in chordDrawObjs:
						content = getTextContent(chordDrawObj)
						if content is not None:
							if content == '-':
								color = hide_color	
								if show == True:
									if criteria == 'localFretIndex':
										color = estFretIndexFontColor
									else:
										color = None
								setTextColor(chordDrawObj, color)
	
def centerGFI(score):
	osystens, systems = getChildren(score, 'system')
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:	
			ovoices, voices = getChildren(staff, 'voice')
			nVoices = len(voices)
			for voice in voices:
				oNoteObjs, noteObjs = getChildren(voice, 'noteObjects')
				for noteObj in noteObjs:
					newX = 0.0
					chordDrawObjs = getObjTextObjsByCriteria(noteObj, 'globalFretIndex')
					for chordDrawObj in chordDrawObjs:
						content = getTextContent(chordDrawObj)
						texts = chordDrawObj.getElementsByTagName('text')
						for text in texts:
							if content is not None:
								content = content.strip().lower().replace('!', '')
								if text.getAttribute('x') != '' and content != 'fret':
									text.setAttribute('x', str(newX))
	
def centerChordSyms(score, centerIsyms):
	osystens, systems = getChildren(score, 'system')
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:	
			ovoices, voices = getChildren(staff, 'voice')
			nVoices = len(voices)
			for voice in voices:
				oNoteObjs, noteObjs = getChildren(voice, 'noteObjects')
				for noteObj in noteObjs:
					chordDrawObjs = getObjTextObjsByCriteria(noteObj, 'chord')
					for chordDrawObj in chordDrawObjs:
						chd = getChordSym(noteObj)
						if type(chd) is list:
							chd = str.join('', chd)
						try:
							valid = acaChords.check_chord(chd, setGermanH)
						except:
							valid = False
						if valid:
							xOffs, yOffs = getChordSymPosOffset(chordDrawObj)
							if centerIsyms:
								basics = chordDrawObj.getElementsByTagName('basic')
								for basic in basics:
									if basic.getAttribute('placement') == 'auto':
										basic.removeAttribute('placement')
								if len(chd) > 0 and chd in ['i', 'I']:
									xOffs = xOffs - 0.4
							moveChordSym(chordDrawObj, -xOffs, 0.0)


config = Config()

isAln = False
def analyzeScore(score):
	global cursorKeys
	global setGermanH
	global setStep7Flat
	global estNotation
	global estStyle
	global estFretIndexFontSize	
	global estFretIndexFontColor
	global estFretIndexStaffName
	global estGlobalFretIndexCount
	global estLocalFretIndexCount
	global estPitchIndexFontColor
	global estPitchIndexFontSize	     
	global estPitchIndexStaffName
	global estPitchIndexCount
	global isAln

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

	cursor = curSelection()[0]
	print('cursor', cursor)

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

	chordNotations = []
	chordStyles = []

	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
			hasChords = False
			for voice in voices:
				oObjs, objs = getChildren(voice, 'noteObjects')
				objID = 0
				for obj in objs:
					objID = objID + 1
				voiceID = voiceID + 1

			if hasChords:
				chordsStaffIDs.append(staffID)

			staffID = staffID + 1
		systemID = systemID + 1


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

# Read Chord File
class ScoreReadChordFile(ScoreChange):
	def changeScore(self, score):
		readChordFile(score)
		global chordScore
		global docChords
		chordScore = score
		docChords = score.parentNode

# Plugin Processing Phase
class ScoreChange(ScoreChange):
	def changeScore(self, score):
		global doc
		doc = score.parentNode
		aCaL_UtillerMain(score)

def beautify(text):
	return text.replace('#', '♯').replace('b', '♭')

# Plugin Dialog
def dialog():
	global config
	global setCenterIsyms
	global setGermanH
	global setPlaceholderType
	global setShowPlaceholders

	global radioMarker
	global radioShowPlaceholders

	global checkCenterIsyms
	global checkGermanH

	global radioPluginAction

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

	config.load()
	setGermanH = config.get('setGermanH', False)
	setCenterIsyms = config.get('setCenterIsyms', True)
	setPluginActionIndex = config.get('setPluginActionIndex', 1)
	setPlaceholderType = config.get('setPlaceholderType', 0)
	setShowPlaceholders = config.get('setShowPlaceholders', 0)

	wdt = 45
	spacer = Label('')

	labelFretIndexColor = Label('Fret-Index Font-Farbe', width=wdt/2)
	labelFretIndexHeight = Label('Fret-Index Font-Größe', width=wdt/2)
	labelGlobalFretIndexNum = Label('Anzahl globale Fret-Indexe', width=wdt/2)
	labelLocalFretIndexNum = Label('Anzahl lokale Fret-Indexe', width=wdt/2)
	labelFretIndexActive = Label('Fret-Index aktuell aktiv', width=wdt/2)
	labelEstPitchIndexColor = Label('Pitch-Index Font-Farbe', width = wdt/2)
	labelEstPitchIndexSize = Label('Pitch-Index Font-Größe', width = wdt/2)
	labelEstPitchIndexNum = Label('Anzahl Pitch-Indexe', width = wdt/2)
	vBoxEstPropLabels = VBox([labelFretIndexColor, labelFretIndexHeight, labelGlobalFretIndexNum, labelLocalFretIndexNum, labelFretIndexActive, labelEstPitchIndexColor, labelEstPitchIndexSize, labelEstPitchIndexNum])

	fColor = '-'
	fSize = '-'
	isConv = '-'
	gfiCount = '-'
	lfiCount = '-'
	if estFretIndexFontColor != '':
		fColor = '#'+estFretIndexFontColor
		fSize = str(estFretIndexFontSize)+' px'
		gfiCount = str(estGlobalFretIndexCount)
		lfiCount = str(estLocalFretIndexCount)
		if isAln:
			isConv = "Ja"
		else:
			isConv = "Nein"
	labelFretIndexColorValue = Label(': '+fColor, width=wdt/2)
	labelFretIndexHeightValue = Label(': '+fSize, fg = estFretIndexFontColor, width=wdt/2)
	labelFretIndexActiveValue = Label(': '+isConv, width=wdt/2)
	labelGlobalFretIndexNumValue = Label(': '+gfiCount, width=wdt/2)
	labelLocalFretIndexNumValue = Label(': '+lfiCount, width=wdt/2)
	colorName = "#"+estPitchIndexFontColor
	if estPitchIndexFontColor == '':       
		colorName = '-' 
	labelEstPitchIndexColorValue = Label(': ' + colorName, width = wdt/2)
	fontSize = str(estPitchIndexFontSize)
	if estPitchIndexFontSize == 0: 
		fontSize = '-'
	labelEstPitchIndexSizeValue = Label(': ' + fontSize, width = wdt/2)
	if estPitchIndexFontColor == '':
		colorName = '-'
		printedPitchIndexCount = '-'
	else:
		printedPitchIndexCount = str(estPitchIndexCount)
	labelEstPitchIndexNumValue = Label(': ' + str(printedPitchIndexCount), width = wdt/2)
	vBoxEstPropValues = VBox([labelFretIndexColorValue, labelFretIndexHeightValue, labelGlobalFretIndexNumValue, labelLocalFretIndexNumValue, labelFretIndexActiveValue, labelEstPitchIndexColorValue, labelEstPitchIndexSizeValue, labelEstPitchIndexNumValue])

	hBoxEstProperties = HBox([vBoxEstPropLabels, vBoxEstPropValues], text = 'Ermittelte Eigenschaften')

	checkCenterIsyms = CheckBox('Generische "I,  i"-Stufennummern-Positionen anpassen', value=setCenterIsyms, width=wdt)
	checkGermanH = CheckBox('H als B interpretieren (→ B = B'+beautify('b')+')', value = setGermanH, width = wdt)
	vBoxHarmonicSettings = VBox([checkCenterIsyms, checkGermanH], height = 1, width = wdt)
	hBoxHarmonicSettings = HBox([vBoxHarmonicSettings], text = 'Akkord-/Stufensymbole-Einstellungen')

	labelFretIndexNoSettings = Label('Keine Einstellungen erforderlich', width = wdt)
	vBoxFretIndexSettings = VBox([labelFretIndexNoSettings], height = 1, width = wdt)
	hBoxFretIndexSettings = HBox([vBoxFretIndexSettings], text = 'Globale Fret-Indexe-Einstellungen')

	markerOpts = ['Fingersatz "-"', 'Lokaler Fret-Index "-"']
	showOpts = ['verstecken', 'zeigen']
	radioMarker = Radio(markerOpts, value = setPlaceholderType, text = 'Platzhalter-Typ', width = wdt/2)
	radioShowPlaceholders = Radio(showOpts, value = setShowPlaceholders, text = 'Aktion' ,width = wdt / 2)
	labelPlaceholderInfo1 = Label('Eine Erklärung zu diesem Menüpunkt befindet sich auf der', width=wdt)
	labelPlaceholderInfo2 = Label('Einleitungs-Seite des Plugins.', width=wdt)
	hBox1ShowPlaceholder	= HBox([radioMarker, radioShowPlaceholders], width = wdt)
	vBoxShowPlaceholder = VBox([hBox1ShowPlaceholder, labelPlaceholderInfo1, labelPlaceholderInfo2])
	hBoxShowPlaceholder = HBox([vBoxShowPlaceholder], text = 'Minus-Platzhalter-Einstellungen')

	pluginActionOpts = ['EINSTELLUNGEN ZURÜCKSETZEN','Akkord-/Stufensymbole horizontal an Notenköpfe ausrichten   ', 'Globale Fret-Indexe horizontal an Notenköpfe ausrichten', 'Minus-Platzhalter-Anzeige ("-")']
	radioPluginAction = Radio(pluginActionOpts, text = 'Plugin-Anwendung' , value=setPluginActionIndex, width = wdt)

	vBox = VBox([hBoxEstProperties, spacer, hBoxHarmonicSettings, spacer, hBoxFretIndexSettings, spacer, hBoxShowPlaceholder, spacer, radioPluginAction], width = wdt + 2)
	dlg = Dialog('aCaL.Utiller', vBox)
	return dlg


if activeScore():
	chordFileName = getPersonalDataDir()+"scripts/Plugins-Chord-Collection.capx"
	try:
		chordFile = open(chordFileName)
		tempOutput = tempfile.mktemp('.capx')
		ScoreReadChordFile(chordFile, tempOutput)
		os.remove(tempOutput)
	except:
		print('warning: Plugin chords collection file not found, using embedded chords, only.')

	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 = radioPluginAction.value() == 0
		if setReset and dialogOkClicked:
			config.dic = {}
			dialogStatus = False
			dialogPass = False
			config.save()
		else:
			dialogPass = True

	if dialogOkClicked:
		setCenterIsyms = checkCenterIsyms.value()
		setGermanH = checkGermanH.value()
		setPluginActionIndex = radioPluginAction.value()
		setPlaceholderType = radioMarker.value()
		setShowPlaceholders = radioShowPlaceholders.value()

		config.set('setCenterIsyms', setCenterIsyms)
		config.set('setGermanH', setGermanH)
		config.set('setPluginActionIndex', setPluginActionIndex)
		config.set('setPlaceholderType', setPlaceholderType)
		config.set('setShowPlaceholders', setShowPlaceholders)

		print(config.dic)
		config.save()

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

