# -*- coding: UTF-8 -*-
""" capellaScript -- (C) 2025 Ken Haiker, acaMusic (www.acamusic.de)
                         Implementation: Brian Schueler
>>> acaChords
    Rev 2.6.12 (23.07.2026) --- Plugin allgemein für Musiker
    ||
    Das Plugin addiert zu einer Notenzeile, die Akkord-Angaben enthält (wie z.B. die "transponierbaren Akkordsymbole"), 
    eine mehrstimmige Akkordspur.
    ||
    Das Plugin wurde insbesondere für die Harmonisierung von Liedern mit generischen relativen Akkord-Stufennummern 
    (z.B. I  ii  V7  IV  usw.) nach der Methode von acaChords Notation geschrieben. 
    |Jedoch beherrscht es auch die klassische Lesart mit absoluten Akkorden (z.B. C  Dm  G7  F  usw.). 
    ||
    Unterstützte Derivate:
    |[Dur]: 6, 7, maj7, 7(9), maj7(9), maj9, 7(#11), maj7(#11), 7(b5), 7(#5), maj7(#5), 6(9), (add9)
    |[Moll]: 6, 7, maj7, 7(9), 7(b5), 7(11), 11, 6(9), (add9)
    |[Sonstige]: dim, °, dim7, sus2, 7sus2, sus4, 7sus4, 7sus, aug, +,  5, no3, Slash-Chords
    ||
    Marker: Pitch-Index, P+, P-, Dur, Maj(or), Moll, Min(or),  *,  (,  )
    ||
    Weitere Informationen, ein ausführliches Plugin-Handbuch sowie unzählige bekannte Lieder, die mit “Akkorden” bzw. 
    Stufennummern in acaChords Notation versehen sind, gibt es auf der Homepage 
    ||
    -------------   www.acaMusic.de   -------------
    ||
<<<

History:  Mai 2025 - 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-acachords.log', 'w')

tagAcaChordsChord = '13113-1200'

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)

# 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'
        elif name == 'noteObj':
                namePlural = 'noteObjects'
        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)

def getDurationByBase(base, dots = 0, tuplet = 0):
	base = base.split('/')
	value = 0
	if len(base) == 2:
		value = float(base[0])/float(base[1])
	else:
		value = float(base[0])

	# Tuplet Note
	if tuplet > 0:
		if tuplet == 3:
			value = value * 2.0 / 3.0

	# Dotted Note
	dot_value = value
	if dots > 0:
		for i in range(0,dots):
			dot_value = dot_value / 2
			value = value + dot_value
	return value

# 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

		dots = duration[0].getAttribute('dots')
		if dots is None or dots == '':
			dots = '0'

		tuplet = '0'
		tupletNode = duration[0].gotoChild('tuplet', False)
		if tupletNode is not None:
			tuplet = tupletNode.getAttribute('count')

		return getDurationByBase(baseT, int(dots), int(tuplet))
	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 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
	
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 and setUsePitchIndex:
					color = fonts[0].getAttribute('color')
					height = int(fonts[0].getAttribute('height'))
					if height == pitchIndexSize and color == pitchIndexColor and content.strip().lower() != 'pitch':
						return content.lower().replace('pitch ', '')
			except:
				pass
	return u''

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

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 ''
		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
		try:
			sc = self.step_to_chord(self.chord_angsax(chord, german), 'C')
		except:
			sc = ''
		if sc != '':
			return True
		try:
			tones = self.chord_to_tones(self.chord_angsax(chord, german),2)
		except:
			tones = ''
		if len(tones) > 0:
			return True
		return False

	def step_to_chord(self, step, key, prefer_sharp = True, seven_flat = True):
		try:
			prefix = self.get_chord_prefix(step)
			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 objClefSign(clef = 'treble'):
	newEl = doc.createElement('clefSign')
	newEl.setAttribute('clef', clef)
	return newEl

def addText(object, content = u'', x = 0, y = 0, fontFace = u'Times New Roman', height = 12, charset = 1, tag = ''):
	drawObjs = object.gotoChild('drawObjects', True)
	drawObj = drawObjs.gotoChild('drawObj', True)

	basic = drawObj.gotoChild('basic', True)
	basic.setAttribute('scope', 'brace')
	basic.setAttribute('placement', 'auto')
	basic.setAttribute('placementHint', 'performanceInstruction')
	basic.setAttribute('tag', tagAcaChordsChord)

	text = drawObj.gotoChild('text', True)
	text.setAttribute('x', str(x))
	text.setAttribute('y', str(y))

	font = text.gotoChild('font', True)
	font.setAttribute('charSet', str(charset))
	font.setAttribute('height', str(height))
	font.setAttribute('face', fontFace)

	contentEl = text.gotoChild('content', True)
	textName = doc.createTextNode(content)
	contentEl.appendChild(textName)

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, setOct4)
			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 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 addNote(voice, newNoteObj):
	oNoteObjs, noteObjs = getChildren(voice, 'noteObjects')
	newObjs = []
	for noteObj in noteObjs:
		newObjs.append(noteObj)
	newObjs.append(newNoteObj)
	setChildren(oNoteObjs, newObjs, 'noteObjects')

def getTopStaves(score):
	retval = []
	osystems, systems = getChildren(score, 'system')
	for system in systems:
		staffID = 0
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:
			if staffID == 0:
				retval.append(staff)
			staffID = staffID + 1
	return retval

def getStaffDistances(staff):
	extraDistance = staff.gotoChild('extraDistance', False)
	top = 0
	bottom = 0
	if extraDistance is not None:
		try:
			top = int(extraDistance.getAttribute('top'))
		except:
			pass
		try:
			bottom = int(extraDistance.getAttribute('bottom'))
		except:
			pass
	return (top, bottom)

def setStaffDistances(staff, top = 0, bottom = 0):
	extraDistance = staff.gotoChild('extraDistance', False)
	if extraDistance is not None:
		staff.removeChild(extraDistance)

	if top != 0 or bottom != 0:
		extraDistance = staff.gotoChild('extraDistance', True)
		if top != 0:
			extraDistance.setAttribute('top', str(top))
		if bottom != 0:
			extraDistance.setAttribute('bottom', str(bottom))

def clearStaff(staff):
	ovoices, voices = getChildren(staff, 'voice')
	for voice in voices:
		clearVoice(voice)

def copyStaff(src_staff, dest_staff):
	clearStaff(dest_staff)
	osrc_voices, src_voices = getChildren(src_staff, 'voice')
	odest_voices, dest_voices = getChildren(dest_staff, 'voice')
	numVoices = len(src_voices)
	for i in range(0, numVoices):
		src_voice = src_voices[i]
		dest_voice = dest_voices[i]

		osrc_noteObjs, src_noteObjs = getChildren(src_voice, 'noteObjects')
		odest_noteObjs, dest_noteObjs = getChildren(dest_voice, 'noteObjects')
		numObjs = len(src_noteObjs)

		for o in range(0, numObjs):
			obj = src_noteObjs[o]
			dest_noteObjs.append(obj)
		setChildren(odest_noteObjs, dest_noteObjs, 'noteObjects')
	setChildren(odest_voices, dest_voices, 'voices')

def clearVoice(voice):
	oNoteObjs, noteObjs = getChildren(voice, 'noteObjects')
	newObjs = []
	for noteObj in noteObjs:
		name = noteObj.localName
		print(name)
		oNoteObjs.removeChild(noteObj)
	setChildren(oNoteObjs, newObjs, 'noteObjects')

acaChords = AcaChords()
acaChords.max_tone = 'B4'

clefList = [('treble', 'Violin'), ('bass', 'Bass'), ('N2', 'Kein'), ('alto', 'Alt'), ('tenor', 'Tenor'), ('C1', 'Sopran'), ('C2', 'Mezzosopran'),('C5', 'Bariton'), ('F3', 'Bariton F')]

# 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 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 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 getPauseMark(noteObj):
	texts = getSimpleTexts(noteObj)
	pauseBegin = False
	pauseEnd = False
	for text in texts:
		if text.lower() == 'p+':
			pauseBegin = True
		elif text.lower() == 'p-':
			pauseEnd = True
	if pauseEnd:
		return '-'
	elif pauseBegin:
		return '+'
	return ''

def getGenderHint(noteObj, keyPosRanges, excludePosRanges, globalFretIndexPosRanges, globalPitchIndexPosRanges, pos):
	simpleTexts = getSimpleTexts(noteObj)
	for text in simpleTexts:
		if text.lower() in ['major', 'maj', 'dur']:
			print("getGenderHint - got MAJOR mark!")
			return 'major'
		elif text.lower() in ['minor', 'min', 'moll']:
			print("getGenderHint - got MINOR mark!")
			return 'minor'

	chord = getChordOrStep(noteObj, excludePosRanges, pos)
	if len(chord) > 0:
		if chord[0] in ['i', 'I', 'v', 'V']:
			schord = chord.split('[')
			if len(schord) == 2 and schord[1].count(']') == 1:
				pitches = getPitches(noteObj)
				if len(pitches) == 1:
					step = re.sub('(.*?)\[.*?\](.*?)','\\1\\2',chord)
					print("getGenderHint - schord, chord, step", schord, chord, step)
					hintTone = schord[1].split(']')[0]
					hintToneNum = '0'
					hintToneTune = '0'
					if hintTone.endswith('#'):
						hintToneNum = re.sub('([0-9]).*', '\\1', hintTone)
						hintToneTune = '1'
					elif hintTone.endswith('b'):
						hintToneNum = re.sub('([0-9]).*', '\\1', hintTone)
						hintToneTune = '-1'
					elif hintTone.find('+') > 0:
						hintToneNum = hintTone.split('+')[0]
						hintToneTune = hintTone.split('+')[1]
					elif hintTone.find('-') > 0:
						hintToneNum = hintTone.split('-')[0]
						hintToneTune = '-'+hintTone.split('-')[1]
					else:
						hintToneNum = hintTone
						hintToneTune = 0

					print('getGenderHint - hint tone detected: (tone, tone index, tune)', hintTone, hintToneNum, hintToneTune)
					try:
						hintToneNum = int(hintToneNum)
					except:
						hintToneNum = 0
					try:
						hintToneTune = int(hintToneTune)
					except:
						hintToneTune = 0

					pitchIndex = getItemAtPos(globalPitchIndexPosRanges, pos)
					try:
						if type(pitchIndex) is list:
							pitchIndex = pitchIndex[0]
						pitchIndex = int(pitchIndex)
					except:
						pitchIndex = 0
						
					print('getGenderHint - pitch index:', pitchIndex)

					pitch = pitches[0][:-1]
					tkey = getItemAtPos(keyPosRanges, pos)
					print('getGenderHint - tkey:', tkey)
					if type(tkey) is list:
						tkey = tkey[0]
					if tkey == '':
						tkey = '0'
					key = acaChords.get_keys_from_circle(int(tkey))
					root_major = key[0]
					root_minor = re.sub("m$", "", key[1])
					print('getGenderHint - root_major before:', root_major)
					print('getGenderHint - root_minor before:', root_minor)
					root_major = acaChords.get_transposed_chord_name(root_major, pitchIndex, acaChords.has_sharps_circle(acaChords.get_base_tone(root_major), False, True))
					root_minor = acaChords.get_transposed_chord_name(root_minor, pitchIndex, acaChords.has_sharps_circle(acaChords.get_base_tone(root_minor), True, True))
					print('getGenderHint - root_major after:', root_major)
					print('getGenderHint - root_minor after:', root_minor)
					major_scale = acaChords.get_major_key_scale(root_major)
					minor_scale = acaChords.get_minor_key_scale(root_minor)
					major_scale = acaChords.force_sign(major_scale, '#')
					minor_scale = acaChords.force_sign(minor_scale, '#')
					print('getGenderHint - hintToneNum:', hintToneNum)
					print('getGenderHint - hintToneTune:', hintToneTune)
					print('getGenderHint - key:', key)
					print('getGenderHint - '+root_major+'-scale :', major_scale)
					print('getGenderHint - '+root_minor+'m-scale:', minor_scale)
					print('getGenderHint - chord (step):', step)
					chord_major = acaChords.step_to_chord(step, root_major, seven_flat = setStep7Flat)
					chord_minor = acaChords.step_to_chord(step, root_minor, seven_flat = setStep7Flat)
					print('getGenderHint - chord (major-based):', chord_major)
					print('getGenderHint - chord (minor-based):', chord_minor)
					step_major = acaChords.get_chord_prefix(chord_major)
					step_minor = acaChords.get_chord_prefix(chord_minor)

					if acaChords.check_chord(step, setGermanH):
						if acaChords.is_minor(step):
							step_major_scale = acaChords.get_minor_key_scale(step_major)
							step_minor_scale = acaChords.get_minor_key_scale(step_minor)
						else:
							step_major_scale = acaChords.get_major_key_scale(step_major)
							step_minor_scale = acaChords.get_major_key_scale(step_minor)
						step_major_scale = acaChords.force_sign(step_major_scale, '#')
						step_minor_scale = acaChords.force_sign(step_minor_scale, '#')
						print('getGenderHint - '+chord_major+'-scale:', step_major_scale)
						print('getGenderHint - '+chord_minor+'-scale:', step_minor_scale)

						major_tones_o = acaChords.chord_to_tones(chord_major,2)
						minor_tones_o = acaChords.chord_to_tones(chord_minor,2)
						major_tones = []
						minor_tones = []
						for tone in major_tones_o:
							major_tones.append(re.sub('[0-9]','',tone))
						for tone in minor_tones_o:
							minor_tones.append(re.sub('[0-9]','',tone))
						major_tones = acaChords.force_sign(major_tones, '#')
						minor_tones = acaChords.force_sign(minor_tones, '#')
						print('getGenderHint - tones (major-based):', major_tones)
						print('getGenderHint - tones (minor-based):', minor_tones)
						fretIndex = 0
						globalFretIndex = getItemAtPos(globalFretIndexPosRanges, pos)
						try:
							if type(globalFretIndex) is list:
								globalFretIndex = globalFretIndex[0]
							globalFretIndex = int(globalFretIndex)
						except:
							globalFretIndex = None
						localFretIndex = None
						localFretIndexTextObjs = getObjTextObjsByCriteria(noteObj, 'localFretIndex')
						if len(localFretIndexTextObjs) == 1 and globalFretIndex is not None:
							operation = 'rep'
							content = getTextContent(localFretIndexTextObjs[0]).strip()
							if len(content) > 1:
								if content[0] == '+':
									operation = 'add'
									content = content[1:]
								elif content[0] == '-':
									operation = 'sub'
									content = content[1:]
							try:
								localFretIndex = int(content)
							except:
								pass
						if localFretIndex is not None and globalFretIndex is not None:
							if operation == 'add':
								fretIndex = globalFretIndex + localFretIndex
							elif operation == 'sub':
								fretIndex = globalFretIndex - localFretIndex
							else:
								fretIndex = localFretIndex
						else:
							if globalFretIndex is not None:
								fretIndex = globalFretIndex
						print('getGenderHint - fret index', fretIndex)
						pitch = re.sub('[0-9]', '', acaChords.pitchFromMidi(acaChords.midiPitch(pitch+"2")-hintToneTune+fretIndex))
						print('getGenderHint - pitch:', pitch)
						index_of_pitch_maj = -1
						index_of_pitch_min = -1
						if step_major_scale.count(pitch) > 0:
							index_of_pitch_maj = step_major_scale.index(pitch) + 1
						if step_minor_scale.count(pitch) > 0:
							index_of_pitch_min = step_minor_scale.index(pitch) + 1
						print('getGenderHint - index of pitch in '+chord_major+' scale:', index_of_pitch_maj)
						print('getGenderHint - index of pitch in '+chord_minor+' scale:', index_of_pitch_min)
						if hintToneNum == index_of_pitch_maj:
							print("getGenderHint - Matched tone on MAJOR scale!")
							return 'major'
						elif hintToneNum == index_of_pitch_min:
							print("getGenderHint - Matched tone on MINOR scale!")
							return 'minor'
						else:
							print("getGenderHint - Tone does not match any scale!")
	return ''

def getExcludeMark(noteObj):
	duration = getObjDuration(noteObj)
	if duration is not None:
		simpleTexts = getSimpleTexts(noteObj)
		text = getChordSym(noteObj)
		if text is not None and text != '':
			if simpleTexts.count(')') > 0:
				return '-'
			elif simpleTexts.count('(') > 0:
				return 'x'
	return ''
	

def getChordOrStep(noteObj, excludePosRanges, pos):
	exc = getItemAtPos(excludePosRanges, pos)
	inc = hasItemAtPos(excludePosRanges, pos)
	if type(exc) is list and len(exc) > 0:
		exc = exc[0]
	if type(inc) is list and len(inc) > 0:
		inc = inc[0]
	if exc == 'x' or inc == '-':
		return ''
	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:
					if setIgnoreUnknownChords:
						return ''
					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 getBarline(noteObj):
	if noteObj.localName == 'barline':
		typ = 'b'
		try:
			typ = noteObj.getAttribute('type')
		except:
			pass
		if typ is None or typ == '':
			typ = 'beat'
		return typ
	return ''

def getExcludePositions(staff, countOnly = False):
	return getItemPositions(getExcludeMark, staff, countOnly)

def getChordPositions(staff, excludePosRanges, countOnly = False):
	return getItemPositions(getChordOrStep, staff, countOnly, excludePosRanges)

def getGlobalFretIndexPositions(staff, countOnly = False):
	return getItemPositions(getGlobalFretIndex, staff, countOnly)

def getPauseMarkPositions(staff, countOnly = False):
	return getItemPositions(getPauseMark, staff, countOnly)

def getPitchIndexPositions(staff, countOnly = False):
	return getItemPositions(getPitchIndex, staff, countOnly)

def getKeyPositions(staff, countOnly = False):
	return getItemPositions(getKeys, staff, countOnly)

def getBarlinePositions(staff, countOnly = False):
	return getItemPositions(getBarline, staff, countOnly)

def getGenderHintPositions(staff, keyPosRanges, excludePosRanges, globalFretIndexPosRanges, globalPitchIndexPosRanges, countOnly = False):
	return getItemPositions(getGenderHint, staff, countOnly, keyPosRanges, excludePosRanges, globalFretIndexPosRanges, globalPitchIndexPosRanges)

def trimRanges(itemPositionRanges):
	# Filter out None or '-' items after first element
	posRanges = []
	first = True
	lastValidItem = '-'
	for itemPosRange in itemPositionRanges:
		(begin, end), item = itemPosRange
		empty = False
		if item is None or item == '-' or item == '' or ( type(item) is list and len(item)>0 and '-' in item ):
			empty = True
		else:
			first = False
		if not empty or first:
			lastValidItem = item
		item = lastValidItem
		itemPosRange = (begin, end), item
		posRanges.append(itemPosRange)
	return posRanges

def tidyRanges(itemPositionRanges):
	# Filter out zero time ranges
	posRanges = []
	for itemPosRange in itemPositionRanges:
		(begin, end), item = itemPosRange
		if begin is None:
			begin = 0.0
			itemPosRange = ((begin, end), item)
		if abs(end-begin) > 1/256:
			posRanges.append(itemPosRange)

	# Fusion of splitted time ranges on same item
	retVal = []
	orgPosRange = None
	posRangeID = 0
	(l_begin, l_end), l_item = posRanges[-1]
	lastPosRange = (l_end, l_end), l_item
	posRanges.append(lastPosRange)
	for posRange in posRanges:
		(begin, end), item = posRange
		if orgPosRange is not None:
			(o_begin, o_end), o_item = orgPosRange
			if o_item != item or posRangeID >= len(posRanges)-1:
				thisEnd = begin
				if posRangeID >= len(posRanges)-1:
					thisEnd = end
				retVal.append(((o_begin, thisEnd), o_item))
				orgPosRange = posRange
		else:
			orgPosRange = posRange
		posRangeID = posRangeID + 1
	if retVal == []:
		retVal = posRanges
	return retVal
		
def getItemPositions(objFunc, staff, countOnly = False, posRanges = None, posRanges2 = None, posRanges3 = None, posRanges4 = None):
	ovoices, voices = getChildren(staff, 'voice')
	itemPosList = []
	for voice in voices:
		pos = -0.001
		onoteObjects, noteObjects = getChildren(voice, 'noteObjects')
		for obj in noteObjects:
			duration = getObjDuration(obj)
			if posRanges4 is not None:
				text = objFunc(obj, posRanges, posRanges2, posRanges3, posRanges4, pos)
			elif posRanges3 is not None:
				text = objFunc(obj, posRanges, posRanges2, posRanges3, pos)
			elif posRanges2 is not None:
				text = objFunc(obj, posRanges, posRanges2, pos)
			elif posRanges is not None:
				text = objFunc(obj, posRanges, pos)
			else:
				text = objFunc(obj)
			if len(text) > 0:
				itemPosList.append((pos, text))
			voltas = obj.getElementsByTagName('volta')
			if voltas.length > 0 and not countOnly:
				itemPosList.append((pos-0.001, 'volta'))

			if duration is not None:
				pos = pos + duration
	itemPosList.sort()
	print("itemPosList", itemPosList)
	return itemPosList

def genItemPositionRanges(itemPosList, totalTime, initItem = [], countOnly = False):
	lastPos = None
	pos = -0.001
	retval = []
	prevItem = initItem
	item = ''
	if len(prevItem) > 0:
		item = prevItem[0]

	for itemPos in itemPosList:
		text = itemPos[1]
		pos = itemPos[0]
		if text == 'volta':
			if len(prevItem) < 2:
				prevItem.insert(0, item)
			else:
				oldItem = item
				item = prevItem[-1]
				prevItem = prevItem[1:]
				prevItem[0] = oldItem
			text = item

		try:
			print('text', text)
			item = text

			if len(prevItem) > 0:
				retval.append(((lastPos, pos), prevItem[:]))
			if len(prevItem) == 0:
				prevItem.append(item)
			else:
				prevItem[0] = item

			lastPos = pos
		except:
			print('CRASH!!!')
			pass

	if len(prevItem) > 0 and lastPos is not None:
		retval.append(((lastPos, totalTime), prevItem[:]))
	else:
		if not countOnly:
			retval.append(((0.0, totalTime), prevItem[:]))
	return retval

# Get Item at specific position
def getItemAtPos(itemRanges, pos):
	for itemRange in itemRanges:
		if pos >= itemRange[0][0] and pos < itemRange[0][1]:
			return itemRange[1]
	return ''

def hasItemAtPos(itemRanges, pos):
	for itemRange in itemRanges:
		if itemRange[0][0] is not None:
			if pos >= itemRange[0][0]-0.001 and pos < itemRange[0][0]+0.002:
				return itemRange[1]
	return None

def setLayoutClef(layout, clef):
	notation = layout.gotoChild('notation', True)
	notation.setAttribute('defaultClef', clef)

def setInstrumentProperty(layout, tag, value):
	instrument = layout.gotoChild('instrument', True)
	names = instrument.getElementsByTagName(tag)
	for name in names:
		instrument.removeChild(name)
	instrument.setAttribute(tag, value)
	contentEl = doc.createElement(tag)
	textName = doc.createTextNode(value)
	contentEl.appendChild(textName)
	instrument.appendChild(contentEl)

def setNoteProps(note):
	display = note.gotoChild('display', True)
	if display is not None:
		if setCollision:
			display.setAttribute('noAutoXShift', 'true')
		else:
			try:
				display.removeAttribute('noAutoXShift')
			except:
				pass
		if setSmall:
			display.setAttribute('small', 'true')
		else:
			try:
				display.removeAttribute('small')
			except:
				pass

		if not display.hasAttributes():
			note.removeChild(display)

def getStavesByLayout(score, name):
	retval = []
	osystens, systems = getChildren(score, 'system')
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		newStaves = []
		for staff in staves:
			if staff.getAttribute('layout') == name:
				retval.append(staff)
	return retval
		

def concatStaves(staves):
	print('concatStaves:', staves)
	newStaff = staves[0].cloneNode(deep = True)
	ovoices, voices = getChildren(newStaff, 'voice')
	print('concatStaves - numStaves:', len(staves))
	print('concatStaves - numVoices:', len(voices))
	voiceNo = 0
	for voice in voices:
		voiceNoteObjs = voice.gotoChild('noteObjs')
		for staff in staves[1:]:
			osvoices, svoices = getChildren(staff, 'voice')
			osVoiceNo = 0
			for svoice in svoices:
				if osVoiceNo == voiceNo:
					onoteObjects, noteObjects = getChildren(svoice, 'noteObjects')
					noteObjID = 0
					for noteObj in noteObjects:
						newNoteObj = noteObj.cloneNode(deep=True)
						# Add key sign 0 (C/Am) if nothing is given
						if noteObjID == 1:
							if noteObjects[0].localName == 'clefSign' and noteObj.localName != 'keySign':
								newKeySign = doc.createElement('keySign') 
								newKeySign.setAttribute('fifths', '0')
								addNote(voice, newKeySign)
								print('concatStaves - add (brand new) keySign:', newKeySign)
						addNote(voice, newNoteObj)
						print('concatStaves - add note:', newNoteObj)
						noteObjID = noteObjID + 1
				osVoiceNo = osVoiceNo + 1
		voiceNo = voiceNo + 1
	return newStaff	

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 removeStavesByName(score, staffName):
	layout = score.gotoChild("layout", False)
	ostaffLayouts, staffLayouts = getChildren(layout, 'staffLayout', forcedPlural = 'staves')
	for staffLayout in staffLayouts:
		description = staffLayout.getAttribute('description')
		if description == staffName:
			print('removeStavesByName - removing layout for staff',staffName)
			staffLayouts.remove(staffLayout)
	setChildren(ostaffLayouts, staffLayouts, 'staffLayout')

	osystens, systems = getChildren(score, 'system')
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		for staff in staves:
			if staff.getAttribute('layout') == staffName:
				print('removeStavesByName - removing staff',staffName)
				staves.remove(staff)
		setChildren(ostaves, staves, 'staff')

def timeSignToDuration(time):
	frac = time.split('/')
	return float(frac[0])/float(frac[1])

def isBoundWith(noteObj1, noteObj2):
	oheads1, heads1 = getChildren(noteObj1, 'head')
	oheads2, heads2 = getChildren(noteObj2, 'head')
	if len(heads1) != len(heads2):
		return False

	numHeads = len(heads1)
	for i in range(0, numHeads):
		head1 = heads1[i]
		head2 = heads2[i]
		tie1 = head1.gotoChild('tie', False)
		tie2 = head2.gotoChild('tie', False)
		tie1begin = False
		tie2end = False
		if tie1 is not None:
			if tie1.getAttribute('begin') == 'true':
				tie1begin = True
		if tie2 is not None:
			if tie2.getAttribute('end') == 'true':
				tie2end = True
		if tie1begin and tie2end:
			return True
	return False

def getCollapsedPause(pauseObj, duration):
	maxDots = 3
	maxPart = 128
	newNoteBase = '1/1'
	newNoteDots = 0
	newNoteDuration = 0
	maxLoops = 20
	while int(newNoteBase.split('/')[1]) <= maxPart and newNoteDots <= maxPart and abs(newNoteDuration - duration) >= 1.0/maxPart and maxLoops > 0:
		newNoteDuration = getDurationByBase(newNoteBase, newNoteDots, newNoteDuration)
		if newNoteDuration > duration:
			newNoteBase = newNoteBase.split('/')
			newNoteBase = newNoteBase[0]+'/'+str(int(newNoteBase[1])*2)
		elif newNoteDuration < duration:
			if newNoteDots < maxDots:
				newNoteDots = newNoteDots + 1
		maxLoops = maxLoops - 1

	print('getCollapsedPause - dura / dura:', newNoteDuration, duration)
	if abs(newNoteDuration - duration) >= 1.0/maxPart:
		return None
	newNote = pauseObj.cloneNode(deep = True)
	duration = newNote.gotoChild('duration', True)
	if duration is not None:
		duration.setAttribute('base', newNoteBase)	
		if newNoteDots > 0:
			duration.setAttribute('dots', str(newNoteDots))
		else:
			try:
				duration.removeAttribute('dots')
			except:
				pass
	return newNote
		

def getCollapsedNote(noteObj1, noteObj2, duration):
	tie2begin = False

	oheads1, heads1 = getChildren(noteObj1, 'head')
	oheads2, heads2 = getChildren(noteObj2, 'head')
	if len(heads1) != len(heads2):
		return False

	numHeads = len(heads1)
	for i in range(0, numHeads):
		head2 = heads2[i]
		tie2 = head2.gotoChild('tie', False)
		tie2begin = False
		if tie2 is not None:
			if tie2.getAttribute('begin') == 'true':
				tie2begin = True

	maxDots = 3
	maxPart = 128
	newNoteBase = '1/1'
	newNoteDots = 0
	newNoteTupletCount = 0
	newNoteDuration = 0
	maxLoops = 20
	while int(newNoteBase.split('/')[1]) <= maxPart and newNoteDots <= maxPart and abs(newNoteDuration - duration) >= 1.0/maxPart and maxLoops > 0:
		newNoteDuration = getDurationByBase(newNoteBase, newNoteDots, newNoteTupletCount)
		if newNoteDuration - 1.0/maxPart > duration:
			newNoteBase = newNoteBase.split('/')
			newNoteBase = newNoteBase[0]+'/'+str(int(newNoteBase[1])*2)
		elif newNoteDuration + 1.0/maxPart < duration:
			if newNoteDots < maxDots:
				newNoteDots = newNoteDots + 1
		maxLoops = maxLoops - 1

	print('getCollapsedNote - dura / dura / dots / base:', newNoteDuration, duration, newNoteDots, newNoteBase)
	if abs(newNoteDuration - duration) >= 1.0/maxPart:
		return None
	newNote = noteObj1.cloneNode(deep = True)
	duration = newNote.gotoChild('duration', True)
	if duration is not None:
		duration.setAttribute('base', newNoteBase)	
		if newNoteDots > 0:
			duration.setAttribute('dots', str(newNoteDots))
		else:
			try:
		 		duration.removeAttribute('dots')
			except:
				pass
		tuplet = duration.gotoChild('tuplet')
		if tuplet is not None:
			duration.removeChild(tuplet)
		if newNoteTupletCount > 0:
			tuplet = duration.gotoChild('tuplet', True)
			tuplet.setAttribure('count', str(newNoteTupletCount))
	oheads, heads = getChildren(newNote, 'head')
	for head in heads:
		if tie2begin and setBindings:
			tie = head.gotoChild('tie', True)
			tie.setAttribute('begin', 'true')
		else:
			tie = head.gotoChild('tie', False)
			if tie is not None:
				try:
					if not setBindings:
						tie.removeAttribute('end')
				except:
					pass
				try:
					tie.removeAttribute('begin')
				except:
					pass
	return newNote
	

def fixVoltasInVoice(orgVoice, newVoice):
	voltaNotes = []
	voltaDuras = []
	currVoltaDrawObj = None
	voltaEnd = None
	voltaList = []
	pos = 0.0

	# Source Voice
	oNoteObjs, noteObjs = getChildren(orgVoice, 'noteObjects')
	numObjs = len(noteObjs)
	for i in range(0, numObjs+1):
		if i < numObjs:
			obj = noteObjs[i]
		else:
			obj = None
		j = i + 1

		# Get Volta Information
		if obj is not None and obj.localName in [ 'chord', 'rest' ]:
			oDrawObjs, drawObjs = getChildren(obj, 'drawObjects')
			for drawObj in drawObjs:
				volta = drawObj.gotoChild('volta', False)
				if volta is not None:
					currVoltaDrawObj = drawObj
					basic = drawObj.gotoChild('basic', False)
					noteRange = 0
					if basic is not None:
						try:
							noteRange = int(basic.getAttribute('noteRange'))
						except:
							pass
						rangeDura = 0.0
						for j in range(i, min(i+noteRange+1, numObjs)):
							if noteObjs[j] is not None and noteObjs[j].localName in [ 'chord', 'rest' ]:
								jDura = getObjDuration(noteObjs[j])
								print('fixVoltasInVoice - volta dura:', jDura)
								rangeDura = rangeDura + jDura
						print('fixVoltasInVoice - volta rangeDura:', rangeDura)
						voltaEnd = pos + rangeDura
						voltaDuras.append(rangeDura)
			if currVoltaDrawObj is not None:
				voltaNotes.append(obj)

		if currVoltaDrawObj and pos >= voltaEnd - 0.001:
			print('fixVoltasInVoice - voltaNotes:', voltaNotes)
			currVoltaDrawObj = None
			voltaList.append(voltaNotes)
			voltaNotes = []
			
		if obj is not None and obj.localName in [ 'chord', 'rest' ]:
			pos = pos + getObjDuration(obj)

	pos = 0.0
	currVoltaDrawObj = None
	voltaEnd = None
	voltaID = 0
	targetVoltaNotes = []

	# Target Voice
	oNoteObjs, noteObjs = getChildren(newVoice, 'noteObjects')
	numObjs = len(noteObjs)
	for i in range(0, numObjs+1):
		if i < numObjs:
			obj = noteObjs[i]
		else:
			obj = None
		j = i + 1

		# Get Volta Information
		if obj is not None and obj.localName in [ 'chord', 'rest' ]:
			oDrawObjs, drawObjs = getChildren(obj, 'drawObjects')
			for drawObj in drawObjs:
				volta = drawObj.gotoChild('volta', False)
				if volta is not None:
					currVoltaDrawObj = drawObj
					voltaEnd = pos + voltaDuras[voltaID]
					print('fixVoltasInVoice - target volta duration:', voltaDuras[voltaID])
					voltaID = voltaID + 1

		if currVoltaDrawObj is not None:
			targetVoltaNotes.append(obj)

		if obj is not None and obj.localName in [ 'chord', 'rest' ]:
			pos = pos + getObjDuration(obj)

			if currVoltaDrawObj and pos >= voltaEnd - 0.001:
				print('fixVoltasInVoice - targetVoltaNotes:', targetVoltaNotes)
				voltaNoteCount = max(0, len(targetVoltaNotes)-1)
				print('fixVoltasInVoice - setting volta\'s noteRange to', voltaNoteCount)
				currVoltaBasic = currVoltaDrawObj.gotoChild('basic', False)
				if currVoltaBasic:
					currVoltaBasic.setAttribute('placement', 'auto')
					currVoltaBasic.setAttribute('noteRange', str(voltaNoteCount))
				currVoltaDrawObj = None
				targetVoltaNotes = []

def collapseBlockDurations(noteObjs, time):
	frag = time.split('/')
	timeDura = float(frag[0])/float(frag[1])
	numObjs = len(noteObjs)
	noteBegin = None
	noteEnd = None
	pauseBegin = None
	pauseEnd = None
	prevObj = None
	nextObj = None
	noteDura = 0.0
	pauseDura = 0.0
	retval = []
	
	for i in range(0, numObjs+1):
		if i < numObjs:
			obj = noteObjs[i]
		else:
			obj = None
		j = i + 1
		if j < numObjs:
			nextObj = noteObjs[j]
		else:
			nextObj = None

		noteCont = False
		pauseCont = False

		if obj is not None and obj.localName == 'rest':
			if pauseBegin is None:
				pauseCont = True
				pauseBegin = i
				pauseEnd = i
				pauseDura = getObjDuration(obj)
			else:
				pauseCont = True
				pauseEnd = i
				pauseDura = pauseDura + getObjDuration(obj)
			if nextObj is not None:
				if nextObj.localName != 'rest':
					pauseCont = False
			else:
				pauseCont = False
	
		if obj is not None and obj.localName == 'chord':
			if noteBegin is None:
				noteCont = True
				noteBegin = i
				noteEnd = i
				noteDura = getObjDuration(obj)
			if i > noteBegin:
				if isBoundWith(prevObj, obj):
					noteCont = True
					noteEnd = i
					noteDura = noteDura + getObjDuration(obj)
			if nextObj is not None:
				print('collapseBlockDurations - note '+str(i)+' is bound:', isBoundWith(obj, nextObj))
				if not isBoundWith(obj, nextObj):
					noteCont = False

		if obj is not None and obj.localName not in [u'rest', u'chord']:
			print('collapseBlockDurations - place obj['+str(i)+'] as:', obj.localName)
			retval.append(obj)

		if not noteCont and noteBegin is not None and noteEnd is not None:
			print('collapseBlockDurations - place noteObj['+str(noteBegin)+','+str(noteEnd)+'] with duration', noteDura)
			newNotes = getCollapsedNote(noteObjs[noteBegin], noteObjs[noteEnd], noteDura)
			if newNotes is None:
				return noteObjs
			retval.append(newNotes)
			noteBegin = None
		if not pauseCont and pauseBegin is not None and pauseEnd is not None:
			print('collapseBlockDurations - place pause['+str(pauseBegin)+','+str(pauseEnd)+'] with duration', pauseDura)
			newPause = getCollapsedPause(noteObjs[pauseBegin],pauseDura)
			if newPause is None:
				return noteObjs
			retval.append(newPause)
			pauseBegin = None

		prevObj = obj
	return retval

def collapseVoiceDurations(voice, time = '4/4'):
	timeDura = timeSignToDuration(time)
	oobjs, objs = getChildren(voice, 'noteObjects')
	numObjs = len(objs)
	orgPos = 0.0
	beatPos = timeSignToDuration(time)
	pos = 0.0
	block = []
	newObjs = []
	
	for i in range(0, numObjs):
		obj = objs[i]

		duration = getObjDuration(obj)

		if obj.localName == 'timeSign':
			time = obj.getAttribute('time')
			timeDura = timeSignToDuration(time)
			print('collapseVoiceDurations - updated time to (frag, dura):', time, timeDura)
			beatPos = orgPos + timeDura

		if pos >= orgPos and obj.localName in ['chord', 'rest']:
			print('collapseVoiceDurations - beat pos from/to:', orgPos, orgPos + timeDura)
			orgPos = orgPos + timeDura
			beatPos = orgPos + timeDura

		print('collapseVoiceDurations - pos is now:', pos)
		if duration is not None:
			pos = pos + duration
		block.append(obj)
		beatBreak = False
		if obj.localName == 'barline':
			beatBreak = True
			orgPos = pos
		if pos >= orgPos - 0.001 or i+1 >= numObjs or beatBreak:   # why the i+1?
			print('collapseVoiceDurations - collapsing '+str(len(block))+' noteObjects, timeSign:', time)
			newBlock = collapseBlockDurations(block, time)
			for obj in newBlock:
				oheads, heads = getChildren(obj, 'head')
				for head in heads:
					tie = head.gotoChild('tie', False)
					if tie is not None and not setBindings:
						head.removeChild(tie)
			newObjs.extend(newBlock)
			block = []
	clearVoice(voice)
	for obj in newObjs:
		addNote(voice, obj)
	return time

def collapseDurations(staff, time = '4/4'):
	timeSign = time
	ovoices, voices = getChildren(staff, 'voice')
	for voice in voices:
		voiceClone = voice.cloneNode(deep = True)
		timeSign = collapseVoiceDurations(voice, time)
		if setVoltaInChordStaff:
			fixVoltasInVoice(voiceClone, voice)
	return timeSign

# Get duration for a block of note/chord/rest
def getBlockDuration(block):
	duration = 0
	for obj in block:
		duration = duration + getObjDuration(obj)
	return duration

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 createChordStaves(score):
	if chordsStaffName == '-':
		msgNoChords()
		return

	layout = score.gotoChild("layout")
	ostaffLayouts, staffLayouts = getChildren(layout, 'staffLayout', forcedPlural = 'staves')

	aCNfound = False
	for staffLayout in staffLayouts:
		description = staffLayout.getAttribute('description')
		if description == chordsStaffName and aCNStaffName == '-' and not aCNfound:
			acnLayout = staffLayout.cloneNode(deep=True)
		elif description == aCNStaffName:
			acnLayout = staffLayout
			aCNfound = True

	acnLayout.removeAttribute('description')
	acnLayout.setAttribute('description', setGenStaffName)
	acnLoNotation = acnLayout.gotoChild('notation')
	acnLoBarlines = acnLoNotation.gotoChild('barlines')
	if not setBeatMarks:
		acnLoBarlines.setAttribute('mode', 'none')
	else:
		acnLoBarlines.setAttribute('mode', 'internal')

	setInstrumentProperty(acnLayout, 'name', setInstrumentName)
	setInstrumentProperty(acnLayout, 'abbrev', '')
	setLayoutClef(acnLayout, clefList[setClef][0])
	reCreate = False
	if aCNStaffName == '-':
		staffLayouts.extend([acnLayout])
	else:
		reCreate = True
	setChildren(ostaffLayouts, staffLayouts, 'staves')

	topStaffExtraDistances = []
	topStaves = getTopStaves(score)
	for topStaff in topStaves:
		topStaffExtraDistances.append(getStaffDistances(topStaff))
	print('topStaves:', topStaves)	
	print('topStaffExtraDistances:', topStaffExtraDistances)	

	if chordsStaffName == '-':
		msgNoChords()
		return

	chordStaves = getStavesByLayout(score, chordsStaffName)
	wholeChordStaff = concatStaves(chordStaves)
	wTotalTime = getStaffTotalTime(wholeChordStaff)

	if setUseBrackets:
		wExcludePositions = getExcludePositions(wholeChordStaff)
		wExcludePosRanges = genItemPositionRanges(wExcludePositions, wTotalTime, [''])
	else:
		wExcludePositions = []
		wExcludePosRanges = []
	print('whole exclusion marks', wExcludePosRanges)

	wChordPositions = getChordPositions(wholeChordStaff, wExcludePosRanges)
	wChordPosRanges = genItemPositionRanges(wChordPositions, wTotalTime, [])
	print('whole chords positions', wChordPositions)
	print('whole chords', wChordPosRanges)

	wGlobalFretIndexPositions = getGlobalFretIndexPositions(wholeChordStaff)
	wGlobalFretIndexPosRanges = genItemPositionRanges(wGlobalFretIndexPositions, wTotalTime, ['0'])
	print('whole global fret indexes', wGlobalFretIndexPosRanges)

	wPauseMarkPositions = getPauseMarkPositions(wholeChordStaff)
	wPauseMarkPosRanges = genItemPositionRanges(wPauseMarkPositions, wTotalTime, ['-'])
	print('whole pause marks', wPauseMarkPosRanges)

	wPitchIndexPositions = getPitchIndexPositions(wholeChordStaff)
	wPitchIndexPosRanges = genItemPositionRanges(wPitchIndexPositions, wTotalTime, [])
	print('whole pitch indexes', wPitchIndexPosRanges)

	wKeyPositions = getKeyPositions(wholeChordStaff)
	wKeyPosRanges = genItemPositionRanges(wKeyPositions, wTotalTime, ['0'])
	print('whole keys', wKeyPosRanges)
	print('whole keys (tidy)', tidyRanges(wKeyPosRanges))

	wGenderHintPositions = getGenderHintPositions(wholeChordStaff, wKeyPosRanges, wExcludePosRanges, wGlobalFretIndexPosRanges, wPitchIndexPosRanges)
	wGenderHintPosRanges = genItemPositionRanges(wGenderHintPositions, wTotalTime, [])
	print('whole gender hint', wGenderHintPosRanges)
	wGenderHintPosRanges = tidyRanges(wGenderHintPosRanges)
	print('whole gender hint (tidy)', wGenderHintPosRanges)
	wGenderHintPosRanges = trimRanges(wGenderHintPosRanges)
	print('whole gender hint (trimmed)', wGenderHintPosRanges)

	wBarlinePositions = getBarlinePositions(wholeChordStaff)
	wBarlinePosRanges = genItemPositionRanges(wBarlinePositions, wTotalTime, [])
	print('whole barlines', wBarlinePosRanges)
	print('Song total time:', wTotalTime)

	osystens, systems = getChildren(score, 'system')
	chord = []
	pos = 0.0
	timeSign = '4/4'
	systemID = 0
	for system in systems:
		ostaves, staves = getChildren(system, 'staff')
		newStaves = []
		newStaff = None
		acnStaff = None
		for staff in staves:
			if staff.getAttribute('layout') == aCNStaffName:
				acnStaff = staff
			if staff.getAttribute('layout') == chordsStaffName:
				newStaff = staff.cloneNode(deep=True)
				staffName = newStaff.getAttribute('layout')
				print("staffName", staffName)
				if staffName is not None:
					newStaff.removeAttribute('layout')
					newStaff.setAttribute('layout', setGenStaffName)

				dummy2 = 0
				mainVoice = None
				ovoices, voices = getChildren(newStaff, 'voice')
				for voice in voices:
					if dummy2 == 0:
						mainVoice = voice
						acnVoice = mainVoice.cloneNode(deep=True)
					dummy2 = dummy2 + 1

				totalTime = getStaffTotalTime(staff)
				chordPositions = getChordPositions(staff, wExcludePosRanges)
				chordPosRanges = genItemPositionRanges(chordPositions, totalTime, chord)
				for voice in voices:
					ovoices.removeChild(voice)

					print('init:', chord)
					if len(chordPosRanges) > 0:
						chord = chordPosRanges[-1][1]

				print('pos ranges:', chordPosRanges)

				clearVoice(acnVoice)
				addNote(acnVoice, objClefSign(clef=clefList[setClef][0]))
				oobjs, objs = getChildren(mainVoice, 'noteObjects')
				numObjs = len(objs)
				voltaDrawObj = None
				for i in range(0, numObjs):
					obj = objs[i]
					dura = getObjDuration(obj)
					durations = obj.getElementsByTagName('duration')
					duration = None
					if len(durations) == 1:
						duration = durations[0].cloneNode(deep=True)
					oDrawObjs, drawObjs = getChildren(obj, 'drawObjects')
					chd = getItemAtPos(wChordPosRanges, pos)
					tpitidx = getItemAtPos(wPitchIndexPosRanges, pos)
					pitidx = 0
					try:
						pitidx = int(tpitidx[0])
					except:
						pass
					print('chd:', chd)
					print('pitch, tpitch:', pitidx, tpitidx)
					if type(chd) is list:
						chd = chd[0]
					chd = re.sub('(.*?)\[.*?\](.*?)','\\1\\2', chd)
					if len(chd) > 0:
						if chd.upper()[0] in ['I', 'V']:
							key = getItemAtPos(wKeyPosRanges, pos)
							keys = acaChords.get_keys_from_circle(int(key[0]))
							if setAutoGender:
								gender = getItemAtPos(wGenderHintPosRanges, pos)
								if len(gender)>0:
									gender = gender[0]
								else:
									gender = ''
								if gender == 'major':
									stepOne = keys[0]
								elif gender == 'minor':
									stepOne = keys[1]
								else:
									if setMajor:
										stepOne = keys[0]
									else:
										stepOne = keys[1]
							elif setMajor:
								stepOne = keys[0]
							else:
								stepOne = keys[1]
							chd_chd = chd
							extra = ''
							if chd.find('<altslash>') > 0:
								chd_chd = chd.split('<')[0]
								chd_bass = chd.split('>')[1]
								extra = '<altslash>' + chd_bass
							chd = acaChords.step_to_chord(chd_chd, stepOne, seven_flat = setStep7Flat) + extra
					if acaChords.check_chord(chd, setGermanH):
						chd = acaChords.get_transposed_chord_name(chd, pitidx, setSharp)

					if drawObjs is not None:
						for drawObj in drawObjs:
							volta = drawObj.gotoChild('volta', False)
							if volta is not None and setVoltaInChordStaff:
								voltaDrawObj = drawObj

					if dura is not None and dura > 0:
						tie_begin = True
						tie_end = True
						if setBindings or setCollapsedChords:
							hiPosChord = hasItemAtPos(wChordPosRanges, pos+dura)
							hiPosPitchIndex = hasItemAtPos(wPitchIndexPosRanges, pos + dura)
							hiPosPauseMark = hasItemAtPos(wPauseMarkPosRanges, pos + dura)
							hiPosBarline = hasItemAtPos(wBarlinePosRanges, pos + dura)
							if hiPosChord is not None or \
							   hiPosPitchIndex is not None or \
							   hiPosPauseMark is not None or \
							   hiPosBarline is not None or \
							   pos+dura >= wTotalTime:
								barlineType = None
								if hiPosBarline and hiPosChord is None and hiPosPauseMark is None and hiPosPitchIndex is None:
									barlineType = getItemAtPos(wBarlinePosRanges, pos + dura)
								if type(barlineType) is list:
									barlineType = barlineType[0]
								if barlineType != 'beat':
									tie_begin = False
							hiPosChord = hasItemAtPos(wChordPosRanges, pos)
							hiPosPitchIndex = hasItemAtPos(wPitchIndexPosRanges, pos)
							hiPosPauseMark = hasItemAtPos(wPauseMarkPosRanges, pos)
							hiPosBarline = hasItemAtPos(wBarlinePosRanges, pos)
							if hiPosChord is not None or \
							   hiPosPitchIndex is not None or \
							   hiPosPauseMark is not None or \
							   hiPosBarline is not None:
								barlineType = None
								if hiPosBarline and hiPosChord is None and hiPosPauseMark is None and hiPosPitchIndex is None:
									barlineType = getItemAtPos(wBarlinePosRanges, pos)
								if type(barlineType) is list:
									barlineType = barlineType[0]
								if barlineType != 'beat':
									tie_end = False
						else:
							tie_begin = False	
							tie_end = False	
						# Slash-Chord: Chord with Bass Note
						if chd.find('<altslash>') > 0:
							chd_chd = chd.split('<')[0]
							chd_bass = chd.split('>')[1]
							chd_tones = acaChords.chord_to_tones_basstone(chd_chd, chd_bass, int(setOctave))
							chd = chd_chd
						else:
							chd_tones = acaChords.chord_to_tones(chd,int(setOctave))
						chd_tones = acaChords.force_sign(chd_tones, setSharp)
						if getItemAtPos(wPauseMarkPosRanges, pos)[0] == '+':
							chd_tones = []
						note = objChord(chd_tones, '1/4', tie_begin=tie_begin, tie_end=tie_end)
						addText(note, '', x=0, y=10)
						setNoteProps(note)
						print('place chord:', chd)
						print('tie begin:', tie_begin)
						print('tie end  :', tie_end)
						remDurations = note.getElementsByTagName('duration')
						if len(remDurations) > 0 and duration is not None:
							note.replaceChild(duration, remDurations[0])
						if voltaDrawObj is not None:
							noteDrawObjs = note.gotoChild('drawObjects', True)
							noteDrawObjs.appendChild(voltaDrawObj)
							voltaDrawObj = None
						addNote(acnVoice, note)
					elif obj.localName in [ 'keySign', 'barline' ]:
						if setGlobalSign:
							addNote(acnVoice, obj.cloneNode(deep=True))
					elif obj.localName not in ['clefSign', 'chord']:
						addNote(acnVoice, obj.cloneNode(deep=True))
					if dura is not None:
						pos = pos + dura

				setChildren(ovoices, [acnVoice], 'voices')
				if setCollapsedChords:
					timeSign = collapseDurations(newStaff, timeSign)
				newStaves.append(newStaff)
		# create new staves
		if reCreate:
			print('createChordStaves - recreating staff')
			print('createChordStaves - acnStaff:', acnStaff)
			print('createChordStaves - newStaff:', newStaff)
			if acnStaff is not None and newStaff is not None:
				clearStaff(acnStaff)
				copyStaff(newStaff, acnStaff)
				setChildren(ostaves, staves, 'staves')
		else:
			print('createChordStaves - creating staff')
			top = topStaffExtraDistances[min(systemID+1, len(systems)-1)][0]
			try:
				setStaffDistances(newStaff, top + int(setDistAdditional), 0)
			except:
				pass
			staves.extend(newStaves)
			setChildren(ostaves, staves, 'staves')
		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("!","").strip().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 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

config = Config()

def msgNoChords():
	messageBox('Keine Chords/Steps in Cursorzeile', 'Es wurden keine Chords/Steps in der Cursorzeile gefunden.  Diese bitte mit dem Plugin "Transponierbares Akkordsymbol" setzen.')

def msgPitchMissingColor():
	messageBox('Achtung', 'Der Pitch-Index muss farblich gekennzeichnet sein (Standard: Rot).')

isAln = False
def analyzeScore(score):
	global numACNStaves
	global aCNStaffName
	global chordsStaffName
	global markedChord
	global instrumentName
	global cursorKeys
	global cursorTime
	global estFretIndexFontSize	
	global estFretIndexFontColor
	global estFretIndexStaffName
	global pitchIndexColor
	global pitchIndexSize
	global pitchIndexStaffName
	global pitchIndexParsed
	global pitchIndexCount
	global setIgnoreUnknownChords
	global setGermanH
	global setStep7Flat
	global setUsePitchIndex
	global isAln

	setIgnoreUnknownChords = True
	setGermanH = False
	setStep7Flat = True
	setUsePitchIndex = True

	numACNStaves = 0
	chordsStaffName = '-'
	instrumentName = '-'
	aCNStaffName = '-'
	markedChord = '-'
	cursorKeys = ['-', '-']
	cursorTime = '-'
	chordsStaffIDs = []
	acnStaffIDs = []

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

	estFretIndexFontColor, estFretIndexFontSize, estFretIndexStaffName, isAln = getFretColorAndSize(score)

	pitchIndexColor, pitchIndexSize, pitchIndexStaffName, pitchIndexParsed = getColorAndSizeForKeyword(score, u'Pitch')
	pitchIndexCount = getPitchIndexCount(score, pitchIndexStaffName)
	if pitchIndexSize > 0 and (pitchIndexColor == '000000' or pitchIndexColor == ''):
		msgPitchMissingColor()

	currTime = ''
	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')
			numChordNotes = 0
			hasChords = False
			voiceID = 0
			for voice in voices:
				oObjs, objs = getChildren(voice, 'noteObjects')
				objID = 0
				for obj in objs:
					time = obj.getAttribute('time')
					if time is not None and time != '':
						currTime = time

					if cursor == (systemID, staffID, voiceID, objID):
						cursorTime = currTime

					chord = str.join('', getChordSym(obj))
					print('checking chord', chord)
					if (acaChords.check_chord(chord) or acaChords.check_chord(chord, True)) and staffID == cursor[1]:
						hasChords = True

					oDrawObjects, drawObjects = getChildren(obj, 'drawObj')
					for drawObj in drawObjects:
						basic = drawObj.gotoChild('basic')
						if basic is not None:
							tag = basic.getAttribute('tag')	
							if tag == tagAcaChordsChord:
								numChordNotes = numChordNotes + 1
					objID = objID + 1
				voiceID = voiceID + 1
			if hasChords:
				chordsStaffIDs.append(staffID)

			if numChordNotes > 0:
				acnStaffIDs.append(staffID)
				numACNStaves = numACNStaves + 1
			staffID = staffID + 1
		systemID = systemID + 1

	# Examine Chord Staff
	if len(chordsStaffIDs) > 0:
		val = chordsStaffIDs[0]
		print('analyzeScore - count(val) <> len(chordsStaffIDs):', chordsStaffIDs.count(val) , len(chordsStaffIDs))
		if chordsStaffIDs.count(val) == len(chordsStaffIDs):
			layout = score.gotoChild("layout")
			ostaffLayouts, staffLayouts = getChildren(layout, 'staffLayout', forcedPlural = 'staves')
			layout = staffLayouts[val]
			chordsStaffName = layout.getAttribute('description')

	# Examive aCN Staff
	if len(acnStaffIDs) > 0:
		val = acnStaffIDs[0]
		print('analyzeScore - count(val) <> len(acnStaffIDs):', acnStaffIDs.count(val) , len(acnStaffIDs))
		if acnStaffIDs.count(val) == len(acnStaffIDs):
			layout = score.gotoChild("layout")
			ostaffLayouts, staffLayouts = getChildren(layout, 'staffLayout', forcedPlural = 'staves')
			layout = staffLayouts[val]
			aCNStaffName = layout.getAttribute('description')
			if aCNStaffName == None:
				aCNStaffName = '-'
			instrument = layout.gotoChild('instrument')
			if instrument is not None:
				instrumentName = instrument.getAttribute('name')
				if instrumentName is None:
					instrumentName = '-'
	print('aCNStaffName', aCNStaffName)

	obj = cursorObj()
	mcTones = getMarkedChordPitches(score)
	print("mcTones", mcTones)
	if mcTones is not None:
		markedChord = acaChords.tones_to_chord(mcTones)
		if markedChord is None or markedChord == '':
			markedChord = '-'
		print("marked Chord", markedChord)

	if chordsStaffName == '-':
		msgNoChords()
		return

	chordStaves = getStavesByLayout(score, chordsStaffName)
	wholeChordStaff = concatStaves(chordStaves)
	wTotalTime = getStaffTotalTime(wholeChordStaff)

	wExcludePositions = getExcludePositions(wholeChordStaff)
	wExcludePosRanges = genItemPositionRanges(wExcludePositions, wTotalTime, [''])
	print('whole exclusion marks', wExcludePosRanges)

	wGlobalFretIndexPositions = getGlobalFretIndexPositions(wholeChordStaff)
	wGlobalFretIndexPosRanges = genItemPositionRanges(wGlobalFretIndexPositions, wTotalTime, ['0'])
	print('whole global fret indexes', wGlobalFretIndexPosRanges)

	wPitchIndexPositions = getPitchIndexPositions(wholeChordStaff)
	wPitchIndexPosRanges = genItemPositionRanges(wPitchIndexPositions, wTotalTime, [])
	print('whole pitch indexes', wPitchIndexPosRanges)

	wKeyPositions = getKeyPositions(wholeChordStaff)
	wKeyPosRanges = genItemPositionRanges(wKeyPositions, wTotalTime, ['0'])
	print('whole keys', wKeyPosRanges)
	print('whole keys (tidy)', tidyRanges(wKeyPosRanges))

	wGenderHintPositions = getGenderHintPositions(wholeChordStaff, wKeyPosRanges, wExcludePosRanges, wGlobalFretIndexPosRanges, wPitchIndexPosRanges)
	wGenderHintPosRanges = genItemPositionRanges(wGenderHintPositions, wTotalTime, [])
	print('whole gender hint', wGenderHintPosRanges)
	wGenderHintPosRanges = tidyRanges(wGenderHintPosRanges)
	print('whole gender hint (tidy)', wGenderHintPosRanges)
	wGenderHintPosRanges = trimRanges(wGenderHintPosRanges)
	print('whole gender hint (trimmed)', wGenderHintPosRanges)
			
	pos = getPosAtCursor(score)
	print('pos at cursor', pos)

	obj = cursorObj()
	if obj != 0:
		# Get Key at Cursor Position
		key = obj.curKey()
		gender = ''
		ckeys = acaChords.get_keys_from_circle(key)
		trans = 0
		if pos is not None:
			trans = getItemAtPos(wPitchIndexPosRanges, pos)
			try:
				if type(trans) is list:
					trans = trans[0]
				trans = int(trans)
			except:
				trans = 0
		ckeyMaj = acaChords.get_transposed_chord_name(ckeys[0], trans, True)
		ckeyMin = acaChords.get_transposed_chord_name(ckeys[1], trans, True)
		toneMaj = acaChords.get_tonic(ckeyMaj)
		toneMin = acaChords.get_tonic(ckeyMin)
		toneMaj = acaChords.aligned_tone_num(acaChords.midiPitch(toneMaj)+1)
		toneMin = acaChords.aligned_tone_num(acaChords.midiPitch(toneMin)+1)
		useSharpMaj = acaChords.has_sharps_circle(toneMaj,False)
		useSharpMin = acaChords.has_sharps_circle(toneMin,True)
		ckeyMaj = acaChords.get_transposed_chord_name(ckeys[0], trans, useSharpMaj)
		ckeyMin = acaChords.get_transposed_chord_name(ckeys[1], trans, useSharpMin)
		ckeys = [ckeyMaj, ckeyMin]
		if pos is not None:
			gender = getItemAtPos(wGenderHintPosRanges, pos)
			if type(gender) is list:
				if len(gender) >0:
					gender = gender[0]
				else:
					gender = ''
		print('gender at cursor, pos', gender, pos)
		if gender == 'major':
			ckeys = [ ckeys[0], '('+ckeys[1]+')' ]
		elif gender == 'minor':
			ckeys = [ '('+ckeys[0]+')', ckeys[1] ]
		cursorKeys = ckeys
	
	

# 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
		createChordStaves(score)

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

# Plugin Dialog
def dialog():
	global config
	global setGenStaffName
	global setInstrumentName
	global setDistAdditional
	global setSmall
	global setCollision
	global setGlobalSign
	global setSharp
	global setBeatMarks
	global setCollapsedChords
	global setIgnoreUnknownChords
	global setBindings
	global setStep7Flat
	global setGermanH
	global setOct4
	global setMinTone
	global setMaxTone
	global setOctave
	global setUseInversion
	global setClef
	global setMajor
	global setAutoGender
	global setUsePitchIndex
	global setUseBrackets
	global setVoltaInChordStaff
	global setCreateStaff

	global editGenStaffName
	global editInstrumentName
	global editDistAdditional
	global checkSmall
	global checkCollision
	global checkGlobalSign
	global checkSharp
	global checkBeatMarks
	global checkSyncedChords
	global checkIgnoreUnknownChords
	global checkBindings
	global checkStep7Flat
	global checkGermanH
	global checkOct4
	global editMinTone
	global editMaxTone
	global editOctave
	global checkUseInversion
	global comboClef
	global checkMajor
	global checkAutoGender
	global checkUsePitchIndex
	global checkUseBrackets
	global checkVoltaInChordStaff
	global checkReset

	global isAln
	global estFretIndexFontColor
	global estFretIndexFontSize
	global estFretIndexStaffName

	config.load()
	setGenStaffName = config.get('setGenStaffName', 'aCN')
	setInstrumentName = config.get('setInstrumentName', 'Chords')
	setDistAdditional = config.get('setDistAdditional', 0)
	setSmall = config.get('setSmall', False)
	setCollision = config.get('setCollision', True)
	setGlobalSign = config.get('setGlobalSign', True)
	setSharp = config.get('setSharp', True)
	setBeatMarks = config.get('setBeatMarks', True)
	setCollapsedChords = config.get('setCollapsedChords', True)
	setIgnoreUnknownChords = config.get('setIgnoreUnknownChords', True)
	setBindings = config.get('setBindings', True)
	setStep7Flat = config.get('setStep7Flat', True)
	setGermanH = config.get('setGermanH', False)
	setOct4 = config.get('setOct4', True)
	setMinTone = config.get('setMinTone', 'C3')
	setMaxTone = config.get('setMaxTone', 'B5')
	setOctave = config.get('setOctave', '4')
	setUseInversion = config.get('setUseInversion', False)
	setClef = config.get('setClef', 0) # treble
	setMajor = config.get('setMajor', True)
	setAutoGender = config.get('setAutoGender', True)
	setUsePitchIndex = config.get('setUsePitchIndex', True)
	setUseBrackets = config.get('setUseBrackets', True)
	setVoltaInChordStaff = config.get('setVoltaInChordStaff', True)
	setCreateStaff = config.get('setCreateStaff', True)

	if aCNStaffName != '-':
		setGenStaffName = aCNStaffName

	if instrumentName != '-':
		setInstrumentName = instrumentName

	wdt = 28
	wdtRight = 41
	wdtFoffs = 5
	spacer = Label('')

	labelChordStaffName = Label('Akkord-/Stufen-Notenzeilenname', width = wdtRight*2/3)
	labelEstKey = Label('Tonart (an Cursorposition)', width = wdtRight*2/3)
	labelEstBase = Label('Taktart (an Cursorposition)', width = wdtRight*2/3)
	fretIndexColor = Label('Fret-Index Font-Farbe', width=wdtRight*2/3)
	fretIndexHeight = Label('Fret-Index Font-Größe', width=wdtRight*2/3)
	fretIndexActive = Label('Fret-Index aktuell aktiv', width=wdtRight*2/3)
	labelEstPitchIndexColor = Label('Pitch-Index Font-Farbe', width = wdtRight*2/3)
	labelEstPitchIndexSize = Label('Pitch-Index Font-Größe', width = wdtRight*2/3)
	labelEstPitchIndexNum = Label('Anzahl Pitch-Indexe', width = wdtRight*2/3)
	vBoxEstPropertiesLabels = VBox([labelChordStaffName, labelEstKey, labelEstBase, fretIndexColor, fretIndexHeight, fretIndexActive, labelEstPitchIndexColor, labelEstPitchIndexSize, labelEstPitchIndexNum], width = wdtRight)
	
	labelChordStaffNameValue = Label(': ' + chordsStaffName, width = wdtRight/3)
	labelEstKeyValue = Label(': ' + beautify(cursorKeys[0])+' / '+beautify(cursorKeys[1]), width = wdtRight/3)
	labelEstBaseValue = Label(': ' + cursorTime, width = wdtRight/3)
	fColor = '-'
	fSize = '-'
	isConv = '-'
	if estFretIndexFontColor != '':
		fColor = '#'+estFretIndexFontColor
		fSize = str(estFretIndexFontSize)+' px'
		if isAln:
			isConv = "Ja"
		else:
			isConv = "Nein"
	fretIndexColorValue = Label(': '+fColor, width=wdtRight/3)
	fretIndexHeightValue = Label(': '+fSize, fg = estFretIndexFontColor, width=wdtRight/3)
	fretIndexActiveValue = Label(': '+isConv, width=wdtRight/3)
	colorName = "#"+pitchIndexColor
	if pitchIndexColor == '':
		colorName = '-'
		printedPitchIndexCount = '-'
	else:
		printedPitchIndexCount = str(pitchIndexCount)
	labelEstPitchIndexColorValue = Label(': ' + colorName, width = wdtRight/3)
	fontSize = str(pitchIndexSize) + ' px'
	if pitchIndexSize == 0:
		fontSize = '-'
	labelEstPitchIndexSizeValue = Label(': ' + fontSize, width = wdtRight/3)
	labelEstPitchIndexNumValue = Label(': ' + printedPitchIndexCount, width = wdtRight/3)
	vBoxEstPropertiesValues = VBox([labelChordStaffNameValue, labelEstKeyValue, labelEstBaseValue, fretIndexColorValue, fretIndexHeightValue, fretIndexActiveValue, labelEstPitchIndexColorValue, labelEstPitchIndexSizeValue, labelEstPitchIndexNumValue], width = wdtRight)
	hBoxEstProperties = HBox([vBoxEstPropertiesLabels, vBoxEstPropertiesValues], text = 'Ermittelte Eigensch.  aus Notenzeile mit Akkord-/Stufensymbolen')


	labelEstChord = Label('acaChords-Reverse: Akkordname', width = wdtRight*2/3)
	labelEstChord2 = Label('(aus übereinander liegenden Noten)', width = wdtRight*2/3)
	labelEstChordStaves = Label('Anzahl Akkord-Notenzeilen', width = wdtRight*2/3)
	vBoxEstChordLabels = VBox([labelEstChord, labelEstChord2, labelEstChordStaves])

	labelEstChordValue = Label('', width = wdtRight/3)
	labelEstChord2Value = Label(': ' + markedChord, width = wdtRight/3)
	labelEstChordStavesValue = Label(': ' + str(numACNStaves), width = wdtRight/3)
	vBoxEstChordValues = VBox([labelEstChordValue, labelEstChord2Value, labelEstChordStavesValue])

	hBoxEstGenProperties = HBox([vBoxEstChordLabels, vBoxEstChordValues], text = 'Ermittelte Eigenschaften aus erzeugter Akkord-Notenzeile')

	labelGenStaffName = Label('Notenzeilenname', width = wdt / 2 + wdtFoffs)
	editGenStaffName = Edit(value = setGenStaffName, width = wdt / 2 - wdtFoffs)
	hBoxGenStaffName = HBox([labelGenStaffName, editGenStaffName], width = wdt)
	labelInstrumentName = Label('Instrumentenbezeichnung', width = wdt / 2 + wdtFoffs)
	editInstrumentName = Edit(value = setInstrumentName, width = wdt / 2 - wdtFoffs)
	hBoxInstrumentName = HBox([labelInstrumentName, editInstrumentName], width = wdt)
	labelDists = Label('Nur bei Erst-Erzeugung der Akkord-Notenzeile:', width = wdt + 5)
	labelDistAdditional = Label('Zusätzl.  "Notenzeilen"-Abstand nach oben', width = wdt + wdtFoffs)
	editDistAdditional = Edit(value = str(setDistAdditional), width = wdt / 2 - wdtFoffs - 5)
	hBoxDistAdditional = HBox([labelDistAdditional, editDistAdditional], width = wdt)

	checkSmall = CheckBox('Kleine Noten', value=setSmall, width = wdt)
	checkCollision = CheckBox('Auto-Kollisionsvermeidung abschalten', value=setCollision, width=wdt)
	checkGlobalSign = CheckBox('Globale Versetzungszeichen übernehmen (oder keine)', value=setGlobalSign, width=wdt+10)
	checkSharp = CheckBox('Lokale Vorzeichen ♯ bevorzugen', value=setSharp, width=wdt)
	checkBeatMarks = CheckBox('Taktstriche verwenden', value=setBeatMarks, width=wdt)
	checkBindings = CheckBox('Akkorde mit Haltebögen verbinden', value=setBindings, width=wdt)
	checkSyncedChords = CheckBox('Akkorddauer = Taktdauer (oder Notendauer)', value=setCollapsedChords, width=wdt + 5)
	checkIgnoreUnknownChords = CheckBox('Unbekannte Akkorde/Stufen ignorieren (oder Pause)', value=setIgnoreUnknownChords, width=wdt+10)
	checkStep7Flat = CheckBox('Dur-Stufen VII und vii = Tonleiterposition 7♭ (oder 7)', value=setStep7Flat, width=wdt+10)
	checkGermanH = CheckBox('H als B interpretieren (→ B = B♭)', value=setGermanH, width=wdt)

	labelOctave = Label('Oktave Akkord-Grundton', width = wdt / 2 + wdtFoffs)
	checkOct4 = CheckBox('Mittleres C ist Oktave 4 (oder in Capella Oktave 5)', value=setOct4, width=wdt + 9)
	editOctave = Edit(value = setOctave, width = wdt / 2 - wdtFoffs)
	hBoxOctave = HBox([labelOctave, editOctave], width = wdt)
	labelMinTone = Label('Tiefste Note', width = wdt / 2 + wdtFoffs)
	editMinTone = Edit(value = setMinTone, width = wdt / 2 - wdtFoffs)
	hBoxMinTone = HBox([labelMinTone, editMinTone], width = wdt)
	labelMaxTone = Label('Höchste Note', width = wdt / 2 + wdtFoffs)
	editMaxTone = Edit(value = setMaxTone, width = wdt / 2 - wdtFoffs)
	hBoxMaxTone = HBox([labelMaxTone, editMaxTone], width = wdt)
	checkUseInversion = CheckBox('Akkord bei Bedarf invertieren (wenn erlaubt)', value=setUseInversion, width=wdt + 5)
	labelClef = Label('Notenschlüssel', width = wdt / 2)
	comboClef = ComboBox([x[1] for x in clefList], value = setClef, width = wdt / 2 + 1)
	hBoxClef = HBox([labelClef, comboClef], width = wdt)
	
	checkReset = CheckBox('EINSTELLUNGEN ZURÜCKSETZEN', value=False, width=wdtRight)
	checkAutoGender = CheckBox('Globalen Tonartencharakter automatisch ermitteln', value=setAutoGender, width=wdtRight)
	checkMajor = CheckBox('Globaler Tonartencharakter (Gender) = Dur', value=setMajor, width=wdtRight)
	checkUsePitchIndex = CheckBox('Pitch-Index (intr.  Tonartenkorr.) berücksichtigen', value=setUsePitchIndex, width=wdtRight)
	checkUseBrackets = CheckBox('Klammer-Deaktivierungsmarker "(,)" anwenden', value=setUseBrackets, width=wdtRight)
	checkVoltaInChordStaff   = CheckBox('Voltenklammern auf Akkord-Notenzeile übertragen', value=setVoltaInChordStaff, width=wdtRight)
	labelStaffInfo = Label('Eine aCN-Notenzeile (mit den Akkordtönen) wird', width = wdtRight)
	labelStaffInfo2 = Label('einmalig erzeugt,  falls sie noch nicht existiert.', width = wdtRight)
	labelStaffInfo3 = Label('Bei Änderungen werden die Noteninhalte überschrie-', width = wdtRight)
	labelStaffInfo4 = Label('ben.  Das Löschen der aCN-Notenzeile erfolgt im', width = wdtRight)
	labelStaffInfo5 = Label('Capella Mustersystem.', width = wdtRight)
	pluginBoxV= VBox([checkReset, checkAutoGender, checkMajor, checkUsePitchIndex, checkUseBrackets, checkVoltaInChordStaff, spacer, labelStaffInfo, labelStaffInfo2, labelStaffInfo3, labelStaffInfo4, labelStaffInfo5])
	pluginBox = HBox([pluginBoxV], text = 'Plugin-Anwendung')

	vBoxPropGenChordStaff = VBox([spacer, hBoxGenStaffName, hBoxInstrumentName, spacer, labelDists, hBoxDistAdditional, spacer, checkGlobalSign, checkSharp, checkBindings, checkSyncedChords, checkIgnoreUnknownChords, checkStep7Flat, checkGermanH, spacer, checkOct4, hBoxOctave, hBoxMaxTone, hBoxMinTone, checkUseInversion, hBoxClef], width = wdt)
	hBoxPropGenChordStaff = HBox([vBoxPropGenChordStaff], text = 'Einstellbare Eigenschaften für erzeugte Akkord-Notenzeile')

	vBoxLeft = VBox([hBoxEstProperties, spacer, hBoxEstGenProperties, spacer, pluginBox])
	vBoxRight = VBox([hBoxPropGenChordStaff])
	hBox = HBox([vBoxRight, spacer, vBoxLeft])
	vBox = VBox([hBox])
	dlg = Dialog('acaChords', 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 = checkReset.value()
		if setReset and dialogOkClicked:
			config.dic = {}
			dialogStatus = False
			dialogPass = False
			config.save()
		else:
			dialogPass = True

	if dialogOkClicked:
		setGenStaffName = editGenStaffName.value()
		setInstrumentName = editInstrumentName.value()
		setDistAdditional = editDistAdditional.value()
		setSmall = checkSmall.value()
		setCollision = checkCollision.value()
		setGlobalSign = checkGlobalSign.value()
		setSharp = checkSharp.value()
		setBeatMarks = checkBeatMarks.value()
		setCollapsedChords = checkSyncedChords.value()
		setIgnoreUnknownChords = checkIgnoreUnknownChords.value()
		setBindings = checkBindings.value()
		setStep7Flat = checkStep7Flat.value()
		setGermanH = checkGermanH.value()
		setOct4 = checkOct4.value()
		setMinTone = editMinTone.value()
		setMaxTone = editMaxTone.value()
		setOctave = editOctave.value()
		setUseInversion = checkUseInversion.value()
		setClef = comboClef.value()
		setMajor = checkMajor.value()
		setAutoGender = checkAutoGender.value()
		setUsePitchIndex = checkUsePitchIndex.value()
		setUseBrackets = checkUseBrackets.value()
		setVoltaInChordStaff = checkVoltaInChordStaff.value()

		config.set('setGenStaffName', setGenStaffName)
		config.set('setInstrumentName', setInstrumentName)
		config.set('setDistAdditional', setDistAdditional)
		config.set('setSmall', setSmall)
		config.set('setCollision', setCollision)
		config.set('setGlobalSign', setGlobalSign)
		config.set('setSharp', setSharp)
		config.set('setBeatMarks', setBeatMarks)
		config.set('setCollapsedChords', setCollapsedChords)
		config.set('setIgnoreUnknownChords', setIgnoreUnknownChords)
		config.set('setBindings', setBindings)
		config.set('setStep7Flat', setStep7Flat)
		config.set('setGermanH', setGermanH)
		config.set('setOct4', setOct4)
		config.set('setMinTone', setMinTone)
		config.set('setMaxTone', setMaxTone)
		config.set('setOctave', setOctave)
		config.set('setUseInversion', setUseInversion)
		config.set('setClef', setClef)
		config.set('setMajor', setMajor)
		config.set('setAutoGender', setAutoGender)
		config.set('setUsePitchIndex', setUsePitchIndex)
		config.set('setUseBrackets', setUseBrackets)
		config.set('setVoltaInChordStaff', setVoltaInChordStaff)
		config.set('setCreateStaff', setCreateStaff)

		acaChords.min_tone = config.get('setMinTone')
		acaChords.max_tone = config.get('setMaxTone')
		acaChords.use_inversion = config.get('setUseInversion', True)

		print(config.dic)
		config.save()

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

