#IPO Edit VERSION 2.6b, by Chris Clawson
# http://www.meloware.com/blender
# webmaster@meloware.com
# June 27, 2001
import Blender
from Blender.Draw import *
from Blender.BGL import *
from Blender import Ipo

#######Modify this next line starting with the word 'filename' to point to your working directory
# Use FOREWARD slashes, keep the quotes, note that I am not using a drive letter

filename="/group.txt"

###################################################################################

menu= Create(0)
menu_d=Create(0)
menu_dat=Create(0)
menu_from=Create(0)
menu_thru=Create(0)
menu_gp_src =Create(0)
menu_gp_dest =Create(0)
menu_gp =Create(0)
menu_file = Create(0)
menu_file_add = Create(0)
menu_child =Create(0)

r_menu= Create(0)
r_menu_d=Create(0)
r_menu_dat=Create(0)
r_menu_from=Create(0)
r_menu_thru=Create(0)


new_group=Create("New Group")
insert=Create(0)
scaler=Create(1.0)
start_key=Create(1)
start_ipo=Create(0)
toggle=Create(0)
scale_it=Create(0)

string1=" "
string2_src="Select IPO|"
string2_dst="Select IPO|"
string2_child="Select IPO"
string3="From |"
string4="At |"
string5="Thru |"
string6="Single"
string7="No time selected"
string8="No Group Created |"
string9="Load File Groups %x0|"
string10="Read"
string11="Write File"
string12=[("Group File OK"),("Group File Not Found ")]
string13="At Current Frame"

slider_max=100
col_r=1
col_g=1
col_b=1
c_red=1
c_green=1
c_blue=1
key_max=0
ipo_num=0
use_slider=0
group_list=[]
group = 0
text_len1=" "
text_len2=" "
warn_keylength =0
child_keylist=[[["LocX"],[]],[["LocY"],[]],[["LocZ"],[]],[["RotX"],[]],[["RotY"],[]],[["RotZ"],[]]]
pi=3.1415926535897931
duration=0


list_size=99

def draw():
	global  string, menu, menu_d, menu_dat, menu_from, menu_thru, menu_child
	global  menu_gp, toggle, insert, new_group, menu_file, scale_it, scaler, start_key, start_ipo

	glClearColor(.2,.2,.2, 0.0)
	glClear(GL_COLOR_BUFFER_BIT)	
	Button("Exit", 1, 10, 5, 70, 15)
	Button("Refresh All", 4, 90, 5, 80, 15)
	menu_dat=Menu(string4, 6, 280, 80, 60, 18, menu_dat.val)
	Button("R", 20, 260, 80, 18, 18)
	menu_from=Menu(string3, 2, 270, 30, 60, 18, menu_from.val)
	Button("R", 21, 250, 30, 18, 18)
	menu_thru=Menu(string5, 2, 360, 30, 60, 18, menu_thru.val)
	Button("R", 22, 340, 30, 18, 18)

	glColor3d(1,1,1)
	glRasterPos2d(10,295)
	Text("Child Keyframer")
	menu_child=Menu(string2_child, 25, 30, 270, 100, 18, menu_child.val)
	Button("R", 23, 10, 270, 18, 18)

	glColor3d(c_red, c_green, c_blue)
	glRasterPos2d(125,295)
	Text(string13)
	Button("Record Key",24,140,270,80,18)
	Button("Update IPO",26,10,245,100,18)
	Button("Start Over",27,120,245,100,18)
	
	menu_gp=Menu(string8, 2, 10, 125, 220, 18, menu_gp.val)
	menu_file=Menu(string9, 15, 10, 155, 220, 18, menu_file.val)
	Button(string11, 16, 240, 155, 90, 18)
	scale_it=Toggle("Scale Next Edit", 9, 350, 245, 120, 18, scale_it.val)

	glColor3d(1,1,1)
	glRasterPos2d(10,210)
	Text("Group/File Operations")	
	new_group=String("New Group Name: ", 9, 10, 185, 220, 18, new_group.val, 32)
	
	insert= Slider("at time:", 14, 300, 55, 150, 18, insert.val, 1, slider_max)

	glColor3d(1,1,1)
	glRasterPos2d(240,295)
	Text("Source Slice Time Scaler")

	scaler= Slider("Scale Factor:", 9, 240, 270, 230, 18, scaler.val, .1, 2)
	glColor3d(.8,.8,1)
	glRasterPos2d(200,370)
	Text("IPO Edit V. 2.6 ")
	glColor3d(1,1,1)
	glRasterPos2d(10,370)
	Text("Select Menu Range")
	start_key=Slider("Keyframe Start At:",9,10,325,300,18, start_key.val, 1, key_max)
	start_ipo=Slider("IPO Start At:",9,10,345,300,18, start_ipo.val, 0, ipo_num)	
	Button("Add", 10, 240, 125, 50, 18)
	Button("Delete", 12, 300, 125, 60, 18) 
	toggle=Toggle("Mode", 13, 195, 5, 50, 15, toggle.val)	
	Button('INSERT', 11 , 380, 80, 60, 18)
	Button('DELETE KEYS', 17, 350, 5, 90, 18)

	glColor3d(1,1,1)
	
	glRasterPos2d(250,8)
	Text(string6)
	
	glRasterPos2d(10,85)
	Text("Destination IPO")
	menu_d= Menu(string2_dst, 3, 145, 80, 100, 18, menu_d.val)
	Button("R", 18, 125, 80, 18, 18)

	glColor3d(1,.75,.75)
	glRasterPos2d(10, 105)
	Text(text_len2)

	glColor3d(1,1,1)
	glRasterPos2d(20,35)
	Text("Source IPO")
	menu= Menu(string2_src, 5, 140, 30, 100, 18, menu.val)
	Button("R", 19, 120, 30, 18, 18)

	glColor3d(1,.75,.75)
	glRasterPos2d(10, 55)
	Text(text_len1)
	
	glColor3d(col_r,col_g,col_b)
	glRasterPos2d(300, 105)
	Text(string7)
	
def event(evt, val):	
	if (evt== QKEY and not val): Exit()

def bevent(evt):
	global text_len1, text_len2,string3, string4, string5, string6,string7
	global use_slider,slider_max
	
	if   (evt== 1): 
		Exit()
	elif (evt== 2): 
		Redraw()
	elif (evt== 3):
		if not len(ipo_list):
			string7="No IPOs in Drawing!"
			error()
			text_len2=""
			return
		if not menu_d.val:
			string7='No Dest IPO'
			error()
			text_len2=""
			return
		error_ipo=report_range()
		if error_ipo:
			string7="One Selected IPO has no Curves!"
			error()
			return
		string4="At %tx0"
		list_id=0
		menu_string=fill_range(menu_d.val-1,list_id)
		string4=string4+menu_string
		slider_max=duration+100
		string7=""
		msg_white()

	elif (evt== 5):
		if not len(ipo_list):
			string7="No IPOs in Drawing!"
			error()
			text_len1=""
			return
		if not menu.val:
			string7='No Source IPO'
			error()
			text_len1=""
			return
		error_ipo=report_range()
		if error_ipo:
			string7="One Selected IPO has no Curves!"
			error()
			return
		string3="From %tx0"
		list_id=1
		menu_string=fill_range(menu.val-1, list_id)
		string3=string3+menu_string

		string5="Thru %tx0"
		list_id=2
		menu_string=fill_range(menu.val-1,list_id)
		string5=string5+menu_string
		string7=""
		msg_white()
	elif (evt== 6):
		use_slider=0
		string7="At Dest Keyframe"
		msg_white()
			
	elif (evt== 9):		#?? Sliding off of a menu generates an evt 9, so do something harmless
		Redraw()
	elif (evt== 10):
		flag1=1
		build_group()
	elif (evt== 11):
		if menu_d.val<1:
			string7="Some Destination IPO must be Selected"
			error()
			return
		dest_found=0
		num=0
  		if toggle.val:
			dest_index=menu_d.val-1
			dest_ipo=ipo_s[dest_index]
			dest_name=dest_ipo.name
			try:
				for size in range (len(group_list)):
					pair=group_list[num]
					dest_group_ipo=pair[1]
					test_dest=ipo_s[dest_group_ipo]
					test_name=test_dest.name

					if test_name==dest_name:
						dest_found=1
					num=num+1
			except:
				string7="No Group Defined"
				error()
				return

			if not dest_found:
				string7="Destination IPO is not a Destination Group Member"
				error()
				return

		if menu_from.val>menu_thru.val:
			string7="From time cannot be Less than Thru time"
			error()
			return
		retry()
		find_key_max()
	elif (evt== 12):
		flag1=delete_gp_item()
		build_group_menu(flag1)
		Redraw()
	elif (evt== 13):
		if toggle.val:
			string6= "Group"
		else:
			string6="Single"
		msg_white()
	elif (evt== 14):
		use_slider =1
		string7="At Slider Value"
		msg_white()
	elif (evt== 15):
		build_file_group()
	elif (evt==16):
		append_file()
	elif (evt==17):
		delete_keys()
		find_key_max()
	elif (evt==18):
		str_id=1
		get_ipos(str_id)
		Redraw()
	elif (evt==19):
		str_id=0
		get_ipos(str_id)
		Redraw()
	elif (evt==20):
		refresh_at()
		Redraw()
	elif (evt==21):
		refresh_from()
		Redraw()
	elif (evt==22):
		refresh_thru()
		Redraw()
	elif (evt==23):
		str_id=2
		get_ipos(str_id)
		Redraw()
	elif (evt==24):
		add_child_key()
		Redraw()
	elif (evt==25):
		find_child_object()
	elif (evt==26):
		keyframe_child()
		Redraw()
	elif (evt==27):
		start_over()
		Redraw()

#Child Keyframer
def find_child_object():
	global ob_child, ob_child_name, ipo_child 

	objects=Blender.Object.Get()
	ipo_s=Ipo.Get()
	ipo_child=ipo_s[menu_child.val-1]
	name=ipo_child.name
	for obj in objects:
		ipo=obj.ipo
		if ipo:
			if name == ipo.name:
				ob_child_name=obj.name
				ob_child = obj
				return
def append_others(tmp_bzt):

	h1_0=0
	h1_1=0
	h2_0=0
	h2_1=0
	f1=0
	f2=0
	f3=0
	h1t="Auto"
	h2t="Auto"
	tmp_bzt.append(h1_0)
	tmp_bzt.append(h1_1)
	tmp_bzt.append(h2_0)
	tmp_bzt.append(h2_0)
	tmp_bzt.append(f1)
	tmp_bzt.append(f2)
	tmp_bzt.append(f3)
	tmp_bzt.append(h1t)
	tmp_bzt.append(h2t)
	return tmp_bzt
def start_over():
	global string13, c_red, c_green, c_blue, child_keylist
	child_keylist=[[["LocX"],[]],[["LocY"],[]],[["LocZ"],[]],[["RotX"],[]],[["RotY"],[]],[["RotZ"],[]]]
	c_red=.6
	c_green=1
	c_blue=.6
	string13="Cleared Recorded"
	
def get_child_bzt(cur_frame,this_name):
	global child_bzt

	tmp_bzt=[]
	tmp_bzt.append(cur_frame)

	if this_name == "LocX":
		tmp_bzt.append(ob_child.mat.loc[0])
		tmp_bzt=append_others(tmp_bzt)
	elif this_name == "LocY":
		tmp_bzt.append(ob_child.mat.loc[1])
		tmp_bzt=append_others(tmp_bzt)
	elif this_name == "LocZ":
		tmp_bzt.append(ob_child.mat.loc[2])
		tmp_bzt=append_others(tmp_bzt)

	elif this_name == "RotX":
		tmp_bzt.append((ob_child.mat.rot[0]/(pi/180))/10)
		tmp_bzt=append_others(tmp_bzt)
	elif this_name == "RotY":
		tmp_bzt.append((ob_child.mat.rot[1]/(pi/180))/10)
		tmp_bzt=append_others(tmp_bzt)
	else: 
		tmp_bzt.append((ob_child.mat.rot[2]/(pi/180))/10)
		tmp_bzt=append_others(tmp_bzt)

	return tmp_bzt

def create_child_bzt(this_list_point):

	bzt= Blender.Ipo.BezTriple()
	bzt.pt[0]=this_list_point[0]
	bzt.pt[1]=this_list_point[1]
	bzt.h1[0]=this_list_point[2]
	bzt.h1[1]=this_list_point[3]
	bzt.h2[0]=this_list_point[4]
	bzt.h2[1]=this_list_point[5]
	bzt.f1=this_list_point[6]
	bzt.f2=this_list_point[7]
	bzt.f3=this_list_point[8]
	bzt.h1t=this_list_point[9]
	bzt.h2t=this_list_point[10]
	return bzt

def add_child_key():
	global child_keylist, string13, c_red, c_green, c_blue
	if menu_child.val <1:
		c_red=1
		c_green=0
		c_blue=0
		string13="No Child Selected!"
		return
	cur_frame=Blender.Get("curframe")	
	ipo_child_curves=ipo_child.curves
	for curve in ipo_child_curves:
		this_name=curve.name
		index=0
		for c_curve in child_keylist:
			c_curve_name=c_curve[0]
			c_curve_name=c_curve_name[0]
			if this_name==c_curve_name:
				tmp_bzt=get_child_bzt(cur_frame,this_name)
				list_bzts=c_curve[1]
				list_bzts.append(tmp_bzt)
				c_curve[1]=list_bzts
				child_keylist[index]=c_curve
			index=index+1
	c_red=.1
	c_green=1
	c_blue=.1
	frame=str(cur_frame)
	string13="Key at Frame "+frame
				
def keyframe_child():
	global string13, c_red, c_green, c_blue, child_keylist
	c_curve=child_keylist[0]	
	c_points=c_curve[1]	
	if  len(c_points)<1:
		c_red=1
		c_green=0
		c_blue=0
		string13="No Keys Recorded!"
		return

	ipo_child_curves=ipo_child.curves
	for curve in ipo_child_curves:
		this_name=curve.name
		these_points=curve.points

		index_listcurve=0
		for c_curve in child_keylist:		#this loop matches the IPO curve with the list curve
			list_curve_name=c_curve[0]
			list_curve_name=list_curve_name[0]
			if this_name==list_curve_name:
				break
			index_listcurve=index_listcurve+1
		c_curve=child_keylist[index_listcurve]	
		c_points=c_curve[1]				#The source list points for the current curve
		index_list=0 #index to list points
		for point in c_points:		#Loop for all new keys in list
			index_pt=0	#index to 'these_points' point
			found=0
			for point in these_points:		#Loop for all points in IPO curve
				this_list_point=c_points[index_list]
				if point.pt[0]<this_list_point[0]:
					bzt=create_child_bzt(this_list_point)
					these_points.insert(index_pt,bzt)
					found=1
					break
				if point.pt[0]== this_list_point[0]:
					bzt=create_child_bzt(this_list_point)
					these_points[index_pt]=bzt
					found=1
					break
				index_pt=index_pt+1
			if not found:
				bzt=create_child_bzt(this_list_point)
				these_points.append(bzt)
			index_list=index_list+1
		curve.points=these_points

	child_keylist=[[["LocX"],[]],[["LocY"],[]],[["LocZ"],[]],[["RotX"],[]],[["RotY"],[]],[["RotZ"],[]]]			

	c_red=0
	c_green=1
	c_blue=0
	string13="IPO Updated!"

#Build Group takes the selected IPO block names, from the 'Destination' and 'Source' menus, and adds them to the group menu list				
def build_group():
	
	global group, group_list
	#	#Build a list of the IPO Blocks in this drawing
	if not group:
		group=1
		group_list =[]
	this_pair=[]
	this_pair.append(menu.val-1)
	this_pair.append(menu_d.val-1)
	group_list.append(this_pair)
	flag1=1
	build_group_menu(flag1)

def build_file_group():
	
	global group, group_list, ipo_s, string7
	ipo_s=Ipo.Get()			#Build a list of the IPO Blocks in this drawing
	
	if not menu_file.val:
		string7='No File Groups'
		error()
		return
	this_selection=groups[menu_file.val-1]
	these_ipos=this_selection[1]
	ipo_index=0
	for size in range (len(these_ipos)):
		
		test=these_ipos[ipo_index] #Get a pair of IPO block names from the selected file group
		this_name=test[0]	#Examine the first name
		index_value1=find_index(this_name)
		
		if index_value1==999999:
			string7='Some Source IPO Names Not in Drawing'
			error()
			return
		this_name=test[1]
		index_value2=find_index(this_name)
		if index_value2==999999:
			string7='Some Dest IPO Names Not in Drawing'
			error()
			return

		if not group:
			group=1
			group_list =[]
		this_pair=[]
		this_pair.append(index_value1)
		this_pair.append(index_value2)
		group_list.append(this_pair)
		flag1=1
		build_group_menu(flag1)
		ipo_index=ipo_index+1
		
#Find the actual index to the IPO block in this drawing, which is referenced in the file    
def find_index(this_name):
    	global  ipo_s
    	local_index=0
	
	for size in range (len(ipo_s)):
		test=ipo_s[local_index]
		name=test.name
		if name==this_name:
			return(local_index)
		local_index=local_index+1
	
	local_index=999999	#999999 is an easily tested error code
	return(local_index)
		
def build_group_menu(flag1):
	global string8, group_list, string7, menu_gp
	
	string8=" Group IPOs %x0"
	dumnum =1
	ipo_s= Ipo.Get()
	length=0
	for size in range (len(group_list)):
		if length<98:
			pair = group_list[dumnum-1]
			ipo=ipo_s[pair[0]]
			string8=string8+('|%s  >' %ipo.name)
			ipo=ipo_s[pair[1]]
			string8=string8+('  %s' %ipo.name)
			string8=string8+('%x')
			if flag1:
				string7='Added '		
				ipo=ipo_s[pair[0]]
				string7=string7+('%s >' %ipo.name)
				ipo=ipo_s[pair[1]]
				string7=string7+('%s' %ipo.name)
				msg_green()
			numbr=str(dumnum)
			string8=string8+(numbr)
			dumnum=dumnum+1
			length=length+1
	if menu_gp.val>len(group_list):
		menu_gp.val=menu_gp.val-1	
#build_file_menu builds a menu list of group associations listed in the file
def build_file_menu():
 	global string9   
	string9="Load File Groups %x0"
	dumnum=1
	length=0
	for size in range (len(groups)):
		this_name= groups[dumnum-1]
		string9=string9+('|%s' %this_name[0])
		string9=string9+('%x')
		numbr=str(dumnum)
		string9=string9+(numbr)
		dumnum=dumnum+1
	string9=string9+('|')


#Add the Current Group to the File
def append_file():
	global group_list, ipo_s, string7, groups
	ipo_s=Ipo.Get()
	new_group_list=[]
	local_index=0
	if not (len(group_list)):
		string7='No Group to Append!'
		error()
		return
	for size in range (len(group_list)):
		pair=group_list[local_index]
		first_index=ipo_s[pair[0]]
		name1=first_index.name
		second_index=ipo_s[pair[1]]
		name2=second_index.name
		both=(name1,name2)
		new_group_list.append(both)
		local_index=local_index+1
	both=(new_group.val,new_group_list)
	try: groups.append(both)
	except:
		groups=[]
		groups.append(both)
	f=open(filename,'w')
	local_index=0
	for size in range (len(groups)):
		entry=groups[local_index]
		name=entry[0]
		if name[-2]=='\015':			#These if statements strip end of line characters
			name=name[0:len(name)-2]
		if name[-1]=='\012':
			name=name[0:len(name)-1]
		ipo_list=entry[1]
		f.write(name)
		f.write('\012')
		ipo_index=0
		for size in range (len(ipo_list)):
			current_pair=ipo_list[ipo_index]
			f.write('\011')
			f.write(current_pair[0])
			f.write('\011')
			f.write(current_pair[1])
			f.write('\012')
			ipo_index=ipo_index+1
		local_index=local_index+1
	f.close()
	read_file()
        		
def delete_gp_item():
	
	global group_list, group, string7, col_g,col_r, col_b
	flag1=0
	try: test=group_list[0]
	except:
		string7="No Group Defined"
		error()
		return flag1
	if not len(group_list):
		group=0
		return flag1
	ipo_s= Ipo.Get()
	pair= group_list[menu_gp.val-1]
	ipo=ipo_s[pair[0]]	
	string7=('Deleted %s >' %ipo.name)
	ipo=ipo_s[pair[1]]
	string7=string7+('%s ' %ipo.name)
	col_r=1
	col_g=1
	col_b=0
	del group_list[menu_gp.val-1]
	if not len(group_list):
		string7="No Group Defined"
		error()
		group=0
		return flag1	
	return flag1

def refresh_from():
	global string3
	if menu.val==0:
		string3="from"
		return
	try:
		this_ipo=ipo_list[menu.val-1]
	except:
		string7="First Select Source Name"
		error()
		return
	error_ipo=report_range()
	if error_ipo:
		string7="Source IPO has no Curves!"
		error()
		return
	string3="From %tx0"
	list_id=1					#1 for 'From' menu
	menu_string=fill_range(menu.val-1,list_id)
	string3=string3+menu_string
	msg_white()

def refresh_thru():
	global string5, string7
	if menu.val==0:
		string5="thru"
		return
	try:
		this_ipo=ipo_list[menu.val-1]
	except:
		string7="First Select Source Name"
		error()
		return
	error_ipo=report_range()
	if error_ipo:
		string7="Source IPO has no Curves!"
		error()
		return
	string5="Thru %tx0"
	list_id=2					#2 for 'Thru menu
	menu_string=fill_range(menu.val-1,list_id)	
	string5=string5+menu_string
	msg_white()

def refresh_at():
	global text_len2, string4, slider_max, string7
	if menu_dat.val==0:
		string4=="At"
		return
	try:
		this_ipo=ipo_list[menu_d.val-1]
	except:
		string7="First Select Destination Name"
		error()
		return
	error_ipo=report_range()
	if error_ipo:
		string7="Destination IPO has no Curves!"
		error()
		return
	string4="At %tx0"
	list_id=0					#0 for 'At' menu
	menu_string=fill_range(menu_d.val-1,list_id)
	string4=string4+menu_string
	slider_max=duration+100
	msg_white()

def refresh():
	str_id=0
	get_ipos(str_id)
	str_id=1
	get_ipos(str_id)
	str_id=2
	get_ipos(str_id)

	refresh_from()
	refresh_thru()
	refresh_at()
	find_key_max()	

#Find existing IPOs in the drawing and format them into the menu string
def get_ipos(str_id):
	global string2_src, string2_dst, ipo_list, string3, string5, string4, ipo_num, string2_child,ipo_s
	stringX= "Select IPO%x0"
	ipo_s = Ipo.Get()
	ipo_num=len(ipo_s)
	dumnum=1
	ipo_list=[]
	nmbr=1
	for ip in ipo_s:		
		the_curves=ip.curves
		if len(the_curves)>0:
			if nmbr > start_ipo.val:
				if (nmbr - start_ipo.val)<99:
					stringX = stringX+('|%s' %ip.name)
					numbr=str(dumnum)
					stringX=stringX+('%x')
					stringX=stringX+(numbr)
			nmbr=nmbr+1		
		ipo_list.append(the_curves)
		dumnum=dumnum+1
	if not str_id:
		string2_src=stringX
		string3="From %x0"
		string5="Thru %x0"
	if str_id==1:
		string2_dst=stringX
		string4="At %x0"
	if str_id==2:
		string2_child=stringX

def find_key_max():
	global key_max
	ipo_s= Ipo.Get()
	key_max=0
	for ip in ipo_s:
		the_curves=ip.curves
		for curve in the_curves:
			points=curve.points
			if key_max<len(points):
				key_max=len(points)		

##FIND THE LOWEST KEY TIME, make a copy, then delete from the lists
def find_and_delete(all_curves,empty):
	lowest=999999
	lowest_member=[]
	any_empty=1
	for member in all_curves:
		if len(member)==1:
			empty=1
		else:
			first_point=member[1]
			val=first_point[1]
			if val<lowest:
				lowest=val
				lowest_member=member[0:2]
			any_empty=0
	if not any_empty:
		empty=0
	index=0
	for member in all_curves:
		if len(member)>1:
			entry=member[1]
			if entry[1]==lowest:
				del member[1]
				all_curves[index]=member
		index=index+1
	return lowest_member, all_curves,empty

#Fill the range menus (from, to, at) with the keyframe values for the selected curves
def fill_range(ipo_index, list_id):
	global at_list, from_list, thru_list, from_string_list, thru_string_list, at_string_list
	this_ipo=ipo_s[ipo_index]
	curves=this_ipo.curves
	list_tmp=[]
	menu_string=' '
	nmbr=1

	#A list of all keys in the ipo
	all_curves=[]
	for curve in curves:
		point_list=[]
		points=curve.points
		point_index=0
		point_list.append(curve.name)
		for point in points:
			point_pair=[]
			point_pair.append(point_index)
			point_pair.append(point.pt[0])
			point_list.append(point_pair)
			point_index=point_index+1
		all_curves.append(point_list)

	#Build a list of points which are at value 'start_key.val'
	index=0
	for member in all_curves:
		try:
			name=member[0]
			member=member[start_key.val:len(member)]
			member.insert(0,name)
			all_curves[index]=member
			index=index+1
		except:
			name=member[0]
			member=[] 
			member.insert(0,name)
			all_curves[index]=member
			index=index+1
	empty=0
	string_list=[]
	while not empty:
		if len(string_list)==list_size:
			break
		lowest_member,all_curves, empty=find_and_delete(all_curves,empty)
		if  len(lowest_member):
			string_list.append(lowest_member)

	for member in string_list:

		values=member[1]
		value=values[1]
		menu_string= menu_string+('|')
		loc_str=str(value)
		list_tmp.append(loc_str)		#Builds a global keylist
		menu_string=menu_string+ loc_str
		menu_string= menu_string+(r'%')
		menu_string= menu_string+('x%s ' %nmbr)
		nmbr=nmbr+1	

	if not list_id:					#save the appropriate global keylist
		at_list=list_tmp
		at_string_list=string_list
	elif list_id==1:
		from_list=list_tmp
		from_string_list=string_list
	else:
		thru_list=list_tmp
		thru_string_list=string_list
	return menu_string

#report the range of an IPO to the screen
#entry: ob.ipo.curves[0].points
def report_range():
	global duration, text_len1, text_len2,string7
	error_ipo=0
 	if menu.val>0:	   
		ipo_index=menu.val-1
		this_ipo=ipo_s[ipo_index]
		this_ipo=this_ipo.curves
		if len(this_ipo)>0:

			start=999999
			end=0
			size=0
			for curve in this_ipo:
				trips=curve.points
				if size <len(trips):
					size=len(trips)
				a_trip=trips[0]
				if a_trip.pt[0]<start:
					start=a_trip.pt[0]
				a_trip=trips[-1]
				if a_trip.pt[0]>end:
					end=a_trip.pt[0]

			start_text=('first keyframe at %s  ' %start) #keyframe location for this triple
			end_text=('last at %s' %end) #keyframe location for the last triple on the curve
			size_text= (' (%s keys)' %size)
			text=start_text+end_text+size_text
			text_len1=text
			string7=""
		else:
			error_ipo=1
			text_len1="Invalid Source IPO"
	if menu_d.val>0:
		ipo_index=menu_d.val-1
	
		this_ipo=ipo_s[ipo_index]
		this_ipo=this_ipo.curves
		if len(this_ipo)>0:

			start_d=999999
			end_d=0
			size_d=0
			for curve in this_ipo:
				trips=curve.points
				if size_d <len(trips):
					size_d=len(trips)
				a_trip=trips[0]
				if a_trip.pt[0]<start_d:
					start_d=a_trip.pt[0]
				a_trip=trips[-1]
				if a_trip.pt[0]>end_d:
					end_d=a_trip.pt[0]
			duration=end_d

			start_text=('first keyframe at %s  ' %start_d) #keyframe location for this triple
			end_text=('last at %s' %end_d) #keyframe location for the last triple on the curve
			size_text= (' (%s keys)' %size_d)
			text=start_text+end_text+size_text
			text_len2=text
			string7=""
		else:
			error_ipo=1
			text_len2="Invalid Destination IPO"

	return error_ipo

#Paste the source slice into the scratchpad
# Entry = GUI menu values
# Exit = Raw source slice pasted into ipo_temp_L

def write_scratchpad():
    
	global ipo_temp, trips_temp, trips_s, ipo_temp_L
	
	source_block= ipo_list[source_ipo]			#Get the indicated  source IPO block

	reference_block = ipo_list[menu.val-1]
	reference_ptloc = reference_block[0]	#first curve (assumes all curves in block have the same no.of keys
	trips_ref = reference_ptloc.points


	ipo_temp_L = [] 			#Create a temporary curve list
	
	for curve in range (len(source_block)):
		source_ptloc= source_block[curve]	#first curve of len(source_block) curves
		trips_s = source_ptloc.points			#the list of bzt's 

		slice_first=from_string_list[menu_from.val-1]
		pt=slice_first[1]
		first_time=pt[1]
		slice_last=thru_string_list[menu_thru.val-1]
		pt=slice_last[1]
		last_time=pt[1]

		slice_position=0
		slice_length=0
		slice_index=0
		first_valid=0
		for pts in trips_s:
			if pts.pt[0]>=first_time:
				if pts.pt[0]<=last_time:
					if not first_valid:
						slice_position=slice_index
						first_valid=1
					slice_length=slice_length+1
			slice_index=slice_index+1
		
		temp_curve=[]	#the blank list for this current curve
		name=source_ptloc.name
		temp_curve.append(name)
		key_list =[[[1,0],[1,1],[1,1],"DUMMY"]]		#blank list to contain keys
		
		
		for keys in range(slice_length):	
			source_bzt= trips_s[slice_position]
			A_bzt=[]
			
			pt_bzt=[]
			pt_bzt.append(source_bzt.pt[0])
			pt_bzt.append(source_bzt.pt[1])
			A_bzt.append(pt_bzt)
			
			h1_bzt=[]
			h1_bzt.append(source_bzt.h1[0])
			h1_bzt.append(source_bzt.h1[1])
			A_bzt.append(h1_bzt)

			h2_bzt=[]
			h2_bzt.append(source_bzt.h2[0])
			h2_bzt.append(source_bzt.h2[1])
			A_bzt.append(h2_bzt)
			
			A_bzt.append(source_bzt.f1)			
			A_bzt.append(source_bzt.f2)
			A_bzt.append(source_bzt.f3)			
			A_bzt.append(source_bzt.h1t)
			A_bzt.append(source_bzt.h2t)
			key_list.append(A_bzt)
			
			slice_position = slice_position +1

		temp_curve.append(key_list)
		ipo_temp_L.append(temp_curve)
		if scale_it.val:			#Scale the length if needed
			scale_range(curve)

#Scale the Curve to a new Length		
def scale_range(curve):
	global ipo_temp_curves, ipo_temp, trips_temp
	

	ipo_temp_ptloc= ipo_temp_L[curve]			#So get a curve in the scratch pad
	trips_temp=ipo_temp_ptloc[1]				

	slice_position = 1 			# This is the first key from the source slice		
	
	source_bzt=trips_temp[slice_position]
	pt_bzt=source_bzt[0]
	reference1=pt_bzt[0]
	length=len(trips_temp)-1
	slice_position=2	#This will be the first key to modify, when we enter the following loop
	try:
		while length:
		
			source_bzt= trips_temp[slice_position]
			pt_bzt=source_bzt[0]	
			reference2=pt_bzt[0]	
		
			distance=(reference2-reference1)*scaler.val
		
			pt_bzt[0]=distance+reference1	#Scale the values now
			source_bzt[0]=pt_bzt
			trips_temp[slice_position]=source_bzt	

			slice_position = slice_position +1
			length=length-1
	except:
		A=0

	ipo_temp_ptloc[1]=trips_temp
	ipo_temp_L[curve]=ipo_temp_ptloc
			
#normalize the scratch pad slice's bzts so that they only indicate their times
#as starting from zero.
#then add the keyframe time of value 'offset' to all the .pt[0] values
# in all the bezier triples of the slice of the scratch pad IPO block to be pasted
	
def normalize_n_offset():				

	for curve in range (len(ipo_temp_L)):
		
		ipo_temp_ptloc = ipo_temp_L[curve]	#the first curve slice
		trips_s=ipo_temp_ptloc[1]	#the slice of this curve's bzts
		index =0
		time_key=from_string_list[menu_from.val-1]
		time_pt=time_key[1]
		old_time=time_pt[1]


		for keys in range (len(trips_s)):

			one_bzt= trips_s[index]		
			one_bzt_pt=one_bzt[0]
			one_bzt_pt[0] = one_bzt_pt[0]-old_time
			one_bzt_pt[0] = one_bzt_pt[0]+offset	#add the offset value to this keyframe
			one_bzt_h1=one_bzt[1]
			one_bzt_h1[0] = one_bzt_h1[0]-old_time
			one_bzt_h1[0] = one_bzt_h1[0]+offset
			one_bzt_h2=one_bzt[2]
			one_bzt_h2[0] = one_bzt_h2[0]-old_time
			one_bzt_h2[0] = one_bzt_h2[0]+offset
			one_bzt[0]= one_bzt_pt
			one_bzt[1]= one_bzt_h1
			one_bzt[2]= one_bzt_h2
			trips_s[index]=one_bzt			
			index = index+1

		one_bzt= trips_s[0]		#return that annoying first keyframe to time 1
		one_bzt_pt=one_bzt[0]
		one_bzt_pt[0]= 1.0
		one_bzt[0]=one_bzt_pt
		trips_s[0]=one_bzt
		ipo_temp_ptloc[1]= trips_s  
		ipo_temp_L[curve]= ipo_temp_ptloc

		
#Add the source_length to all the start times of the bzt's in the destination
#block beginning with the 'At' point 
		
def shift_dest(paste_info):	
	info_index=0
	curve_index=0
	for curve in dest_block:

		this_insert=paste_info[info_index]
		insert_point=this_insert[0]
		dest_ptloc = dest_block[curve_index]
		trips_d = dest_ptloc.points
		slice=trips_d[insert_point[1]:len(trips_d)] 	#Cut a temporary slice of destination from 'at' to end
		length=len(slice) 				#find it's length
		slice=0						#then delete it to save memory
		if length!=1:					#Dont insert the original key that created "Temp"
			insert_point2=insert_point[1]						#the index to the first bzt in the destination block to change
			for bzt_points in range(length):		#add the source_length value to each move_bzt.pt[0]
				move_bzt=trips_d[insert_point2]
				move_bzt.pt[0] = move_bzt.pt[0]+source_length
				move_bzt.h1[0] = move_bzt.h1[0]+source_length
				move_bzt.h2[0] = move_bzt.h2[0]+source_length
				insert_point2=insert_point2+1
			dest_ptloc.points = trips_d			#update destination curve
		info_index=info_index+1
		curve_index=curve_index+1

#Delete Selected Keyframes
def delete_keys():
	global string7
  	num=0
	from_key=from_string_list[menu_from.val-1]
	from_pt=from_key[1]
	thru_key=thru_string_list[menu_thru.val-1]
	thru_pt=thru_key[1]
	length=thru_pt[1]-from_pt[1]

  	if toggle.val:
		for size in range (len(group_list)):
			pair=group_list[num]
			source_ipo=pair[0]
			source_block= ipo_list[source_ipo]
			num=num+1
			for curve in range (len(source_block)):
				source_ptloc = source_block[curve]
				trips_delete = source_ptloc.points
				if length<0:
					string7='Range From/Thru not Valid'
					error()
					return
				index=0								
				for bzt_points in trips_delete:
					if len(trips_delete)==1:
						string7="Cannot Delete Every Key"
						error()
						return
					time=bzt_points.pt[0]
					if time>=from_pt[1]:
						if time<=thru_pt[1]:
							while bzt_points.pt[0]<=thru_pt[1]:
								try:
									del trips_delete[index]
									bzt_points=trips_delete[index]
								except:
									break	
					index=index+1 
			
				source_ptloc.points = trips_delete			#update destination curve
	else:
		source_ipo=menu.val-1
		source_block= ipo_list[source_ipo]
		for curve in range (len(source_block)):
			source_ptloc = source_block[curve]
			trips_delete = source_ptloc.points
			if length<0:
				string7='Range From/Thru not Valid'
				error()
				return
			index=0								
			for bzt_points in trips_delete:
				if len(trips_delete)==1:
					string7="Cannot Delete Every Key"
					error()
					return
				time=bzt_points.pt[0]
				if time>=from_pt[1]:
					if time<=thru_pt[1]:
						while bzt_points.pt[0]<=thru_pt[1]:
							try:
								del trips_delete[index]
								bzt_points=trips_delete[index]
							except:
								break	
				index=index+1 
	
			source_ptloc.points = trips_delete			#update destination curve
	refresh_from()
	refresh_thru()
	string7="Deleted Selected Keys"
	msg_green()	

def mk_paste_bzt(this_bzt):		#Create a BZT from the indicated scratchpad element

	bzt= Blender.Ipo.BezTriple()
	source_bzt_pt=this_bzt[0]
	bzt.pt[0] = source_bzt_pt[0]	#Copy all the attributes over
	bzt.pt[1] = source_bzt_pt[1]

	source_bzt_h1=this_bzt[1]
	bzt.h1[0] = source_bzt_h1[0]	
	bzt.h1[1] = source_bzt_h1[1]

	source_bzt_h2=this_bzt[2]
	bzt.h2[0] = source_bzt_h2[0]	
	bzt.h2[1] = source_bzt_h2[1]

	bzt.f1=this_bzt[3]
	bzt.f2=this_bzt[4]
	bzt.f3=this_bzt[5]
	bzt.h1t=this_bzt[6]
	bzt.h2t=this_bzt[7]
	return bzt
	
def paste(paste_info,insert_point_string,info_list):

	insert_pts=insert_point_string[1]
	
	#First we need to know what keys to weld together
	#Build a list of weld flags with an entry for each destination curve
	insert_list=[]
	info_index=0
	for curve in range (len(dest_block)):
		dest_ptloc = dest_block[curve]		#set up curve and insert point for destination
		dest_name= dest_ptloc.name		#new
		trips_d = dest_ptloc.points

		temp_index=0
		found_curve = 0
		length=len(ipo_temp_L)
		while length >0:					#Look for a matching source curve name for this destination curve
			one_temp_curve= ipo_temp_L[temp_index]
			one_temp_name= one_temp_curve[0]
			if one_temp_name==dest_name:
				found_curve =1
				break
			temp_index=temp_index+1
			length=length-1
		if found_curve:

			this_info=paste_info[info_index]
			insert_point=this_info[0]
			weld_z=this_info[1]

			ipo_temp_ptloc = ipo_temp_L[temp_index]	#Paste's Source
			temp_name=ipo_temp_ptloc[0]	
			trips_s=ipo_temp_ptloc[1]		#and these are the points to paste
			if len(trips_s)>1:

				key_index_d =insert_pts[0]    	#insert key index, from the 'at' menu value
				key_time_d = insert_pts[1]	#insert key time from the 'at' menu value

				paste_key = trips_s[1]				#first key after dummy
				key_pt=paste_key[0] 				#
				key_time_s = key_pt[0]				#scratchpad's first key time
				key_index_s = key_pt[1]				#scratchpad's first key index 
	
				insert=0
				insert_point=insert_point_string[1]
	
				position = len(trips_d)-1			#an index value to the last destination key
				last_pt=trips_d[-1]
				last_pt_time=last_pt.pt[0]
	
				if key_time_s< last_pt_time:	#flag these as an insert operation
					insert=1
				if len(trips_s)==1:
					insert=1
				if weld_z:				#weld_z=1 reminds us that this paste will fit between two destination keys
					insert=0
				insert_list.append(insert)
				info_index=info_index+1
			else:
				insert_list.append(0)
	#Now insert the source slice into the destination curve
	#
	# Two types of pastes are possible, inserts and appends. An insert operation places the source edit within
	# the range of an existing curve, the index 'remaining' begins by pointing to the end of the source slice,
	# and inserts it at the destination curve's insert point key index. The loop then works down the list of
	# source slice keys as the index value 'remaining' is decremented.
	# An append paste uses 'src_index', starting at 1, to skip the dummy, and appends the source keys to the end
	# of the destination curve, as src_index is incremented.
	#
	insert_index=0
	info_index=0

	for curve in range (len(dest_block)):
		dest_ptloc = dest_block[curve]		#set up curve and insert point for destination
		dest_name= dest_ptloc.name		#new
		trips_d = dest_ptloc.points

		temp_index=0
		found_curve = 0
		length=len(ipo_temp_L)
		while length >0:					#Look for a matching source curve name for this destination curve
			one_temp_curve= ipo_temp_L[temp_index]
			one_temp_name= one_temp_curve[0]
			if one_temp_name==dest_name:
				found_curve =1
				break
			temp_index=temp_index+1
			length=length-1
		if found_curve:					#We found a matching curve to paste	
			paste_data=paste_info[info_index]
			insert_point=paste_data[0]
			weld_z=paste_data[1]
			middle_key=paste_data[2]
			weld_last=paste_data[3]
			ipo_temp_ptloc = ipo_temp_L[temp_index]
			temp_name=ipo_temp_ptloc[0]	
			trips_s=ipo_temp_ptloc[1]		#and these are the points to paste

			length = len(trips_s)-1
			remaining = length		
			src_index = 1
			insert=insert_list[insert_index]
			info_data=info_list[insert_index]
			if len(trips_s)>1:
				for i in trips_s:		#Insert the source bzt's for this curve
		
					if insert:		#process an insert operation
						if not length-remaining:	#weld the value of trips_s[-1] with trips_d[insert_point]
							if not use_slider:
								if not middle_key:	
									bzt_s=trips_s[remaining]			
									bzt_d=trips_d[insert_point[1]]
									bzt_s_pt=bzt_s[0]			
									bzt_s_val=bzt_s_pt[1] 
									bzt_d_val=bzt_d.pt[1]
						
									bzt_d_val = (bzt_s_val+bzt_d_val)/2
									bzt_d.pt[1]= bzt_d_val
									#Let's convert the Handles to "Vect" this is the first key
									bzt_d.h1t="Vect"
									bzt_d.h2t="Vect"
								else:
									this_bzt=trips_s[remaining]
									bzt=mk_paste_bzt(this_bzt)
									trips_d.insert(insert_point[1],bzt)
							else:
								if not weld_last:
									this_bzt=trips_s[remaining]
									bzt=mk_paste_bzt(this_bzt)
									#Let's convert the Handles to "Vect" this is the first key
									bzt.h1t="Vect"
									bzt.h2t="Vect"
									trips_d.insert(insert_point[1],bzt)
								else:	
									bzt_s=trips_s[remaining]			
									bzt_d=trips_d[insert_point[1]]
									bzt_s_pt=bzt_s[0]			
									bzt_s_val=bzt_s_pt[1] 
									bzt_d_val=bzt_d.pt[1]
									if not middle_key:
										bzt_d_val = (bzt_s_val+bzt_d_val)/2
										bzt_d.pt[1]= bzt_d_val
										#Let's convert the Handles to "Vect" this is the first key
										bzt_d.h1t="Vect"
										bzt_d.h2t="Vect"
									else:
										bzt_s=mk_paste_bzt(bzt_s)
										trips_d.insert(insert_point[1],bzt_s)		
						else:
							if  remaining:
								this_bzt=trips_s[remaining]
								bzt=mk_paste_bzt(this_bzt)
								trips_d.insert(insert_point[1],bzt)						
					
					elif not insert and not weld_z:		#process an append operation
						if src_index==1:		#weld the value of trips_s[1] with trips_d[insert_point]
							if not use_slider:

								bzt_d=trips_d[(len(trips_d)-1)]
								bzt_s=trips_s[src_index]							
								bzt_s_pt=bzt_s[0]
								bzt_s=trips_s[src_index]			
								bzt_d=trips_d[(len(trips_d)-1)]
								bzt_s_pt=bzt_s[0] 
								bzt_s_val=bzt_s_pt[1] 
								bzt_d_val=bzt_d.pt[1]
								bzt_d_time=bzt_d.pt[0]
								if bzt_s_pt[0]==bzt_d.pt[0]:
									bzt_d_val = (bzt_s_val+bzt_d_val)/2
									#Let's convert the Handles to "Vect" this is the first key
									bzt_d.h1t="Vect"
									bzt_d.h2t="Vect"
									bzt_d.pt[1]= bzt_d_val
								else:
									this_bzt=trips_s[src_index]
									bzt=mk_paste_bzt(this_bzt)
									trips_d.append(bzt)

							else:
								this_bzt=trips_s[src_index]
								bzt=mk_paste_bzt(this_bzt)
								#Let's convert the Handles to "Vect" this is the first key
								bzt.h1t="Vect"
								bzt.h2t="Vect"
								trips_d.append(bzt)					
						else:
							if src_index < len(trips_s):
								this_bzt=trips_s[src_index]
								bzt=mk_paste_bzt(this_bzt)
								trips_d.append(bzt)					
							
					elif  weld_z:				#process an insert operation
						if   remaining==len(trips_s)-1:	#but weld the value of trips_s[1] with trips_d[insert_point]
							if not use_slider:
								test= trips_d[insert_point[1]]
								time=test.pt[0]
								at_key=at_string_list[menu_dat.val-1]
								at_pts=at_key[1]

								if time<at_pts[1]:
									this_bzt=trips_s[remaining]
									bzt=mk_paste_bzt(this_bzt)
									trips_d.insert(insert_point[1],bzt)
								else:
									if not middle_key:	
										del trips_d[insert_point[1]]
										this_bzt=trips_s[remaining]
										bzt=mk_paste_bzt(this_bzt)
										trips_d.insert(insert_point[1],bzt)
									else:
										this_bzt=trips_s[remaining]
										bzt=mk_paste_bzt(this_bzt)
										trips_d.insert(insert_point[1],bzt)			
							else:
								this_bzt=trips_s[remaining]
								bzt=mk_paste_bzt(this_bzt)
								trips_d.insert(insert_point[1],bzt)	
						else:
							if src_index < len(trips_s):
								this_bzt=trips_s[remaining]
								bzt=mk_paste_bzt(this_bzt)
								trips_d.insert(insert_point[1],bzt)	
				 
					remaining = remaining -1
					src_index = src_index +1
				dest_ptloc.points = trips_d	#Reassign updated curve to the IPO
		insert_index=insert_index+1
		info_index=info_index+1

def slider_check(dest_ipo, insert_time):
    	


	curves=ipo_list[dest_ipo]	
	did_is_equal=0	
	info_list=[]
	for curve in curves:

		is_equal=0		#1=there is a bzt equal to the 'at' time
		equal_pos=[]	# here is the curve name and bzt index

		next_done=0		#1= there is a bzt greater than the 'at' time
		next_key=0		# here is the key's time
		next_pos=[]		# here is the curve name and bzt index

		last_key=0		# The last key time before the 'at' time	
		last_pos=[]		# the curve name and index of the last key before 'at' time

		last_pos_name="None"
		last_pos_bzt=0

		trips_d=curve.points
		new_tuple=[]
		for bzt in trips_d:
			if bzt.pt[0] == insert_time: # this bzt's key time = to the 'AT' time?
				if not did_is_equal:
					did_is_equal=1
					is_equal= 1
					equal_pos.append(curve.name)
					equal_pos_bzt=trips_d.index(bzt)
					equal_pos.append(equal_pos_bzt)#equal_pos will have curve name and bzt index

			if bzt.pt[0] > insert_time:
				if not next_done:
					next_done=1
					next_key= bzt.pt[0]
					next_pos.append(curve.name)
					next_pos_bzt= trips_d.index(bzt)
					next_pos.append(next_pos_bzt)
						
			if bzt.pt[0] < insert_time:
				last_key = bzt.pt[0]
				last_pos_name=curve.name
				last_pos_bzt = trips_d.index(bzt)
					
		last_pos.append(last_pos_name)
		last_pos.append(last_pos_bzt)		
		new_tuple.append(is_equal)
		new_tuple.append(equal_pos)
		new_tuple.append(next_key)
		new_tuple.append(last_key)
		new_tuple.append(next_pos)
		new_tuple.append(last_pos)
		new_tuple.append(curve.name)
		info_list.append(new_tuple)
	return info_list
	
def validate():
   	global string7, invalid, warn_keylength
	
	invalid=0
	ipo_s=Ipo.Get()
	destination=ipo_s[dest_ipo]
	source=ipo_s[source_ipo]
	d_curves=destination.curves
	d_name=destination.name
	s_curves=source.curves
	s_name=source.name
	
	s_curve=s_curves[0]
	s_points=s_curve.points
	keys_s=len(s_points)
	
	d_curve=d_curves[0]
	d_points=d_curve.points
	keys_d=len(d_points)
	
	for one in range(len(s_curves)):
		s_curve=s_curves[one]
		s_cname=s_curve.name
		
		d_curve=d_curves[one]
		d_cname=d_curve.name

		if  s_cname!=d_cname:
			string7=('%s and ' %s_name)
			string7=string7+('%s curves dont match!' %d_name)
			error()
			invalid=1
			return invalid
		
		s_points=s_curve.points
		d_points=d_curve.points

	return invalid		
		 
def execute():
	global offset, dest_block, source_length, use_slider,string7
	
	write_scratchpad()	#paste the source slice into the scratchpad

	dest_block = ipo_list[dest_ipo]	#Get pointed to the destination block

	dest_ptloc = dest_block[0]		#and it's first curve of len(dest_block) curves
	trips_d = dest_ptloc.points		#list of destination curve bzt's
	
	shift_d=1			#test this later to see if we need to shift destination keys
	at_start= 0			#flag to indicate paste to be inserted at beginning of dest curve
	at_end = 0			#flag to indicate paste to be appended to end of dest curve
	merge_point = 0			#0 to merge first paste key with last dest key

	
	#Get some information about the destination Block
	#We are examining only the first curve of the block, and are assuming that
	# the key positions and their times are the same for each curve in the block
	#Find the amount of time occupied by the source slice


	start_key=from_string_list[menu_from.val-1]
	start=start_key[1]
	end_key=thru_string_list[menu_thru.val-1]
	end=end_key[1]		

	source_length = end[1] - start[1]	#This should be the time occupied by the slice

	insert_point= menu_dat.val -1
	
	if use_slider:
		insert_time=insert.val

	else:
		insert_key=at_string_list[insert_point]
		insert_pt=insert_key[1]
		insert_time=insert_pt[1]
	
	info_list = slider_check(dest_ipo,insert_time)

	insert_point_string=at_string_list[menu_dat.val-1]
	
	insert_point_data=insert_point_string[1]
	insert_point=[]
	insert_point.append(insert_point_string[0])	#The 'at' curve name
	insert_point.append(insert_point_data[0])		#the 'at' key index

	weld_z=0
	middle_key=0
	weld_last=0
	info_index=0
	paste_info=[]
	for curve in dest_block:

		this_info=info_list[info_index]
		is_equal=this_info[0]
		equal_pos=this_info[1]
		next_key=this_info[2]
		last_key=this_info[3]
		next_pos=this_info[4]
		last_pos=this_info[5]
		new_tuple=[]

		if use_slider:
			if is_equal:					#Slider is at a Key value, use the key value
				insert_point=equal_pos
				use_slider=0
			elif not next_key:				#An append beyond the last dest key
				shift_d=0
				insert_point=["None", -1] 
			if shift_d:
				offset = insert_point_data[1]
			if next_key and last_key:			# This is an insert into the dest curve
				insert_last= insert.val+source_length
				if next_key >= insert_last:		#The paste fits between two dest keys
					shift_d=0 
					offset= insert.val
					insert_point=next_pos
				else:
					offset= insert.val
					insert_point=next_pos
			else:
				offset= insert.val
				insert_point=last_pos
				from_key=from_string_list[menu_from.val-1]
				from_pts=from_key[1]
				if (insert.val+source_length)<from_pts[1]:
					shift_d=0
				if (insert.val+source_length)==from_pts[1]:
					shift_d=0
					weld_last=1
				from_key=from_string_list[menu_from.val-1]
				from_pts=from_key[1]
				points=curve.points
				set=0
				for point in points:
					if set:
						break
					if point.pt[0]==from_pts[1]:
						set=1
					if point.pt[0]>from_pts[1]:
						set=1
						middle_key=1
		else:
			if is_equal:
				insert_point=equal_pos
			elif next_key>0:

				insert_point=next_pos
				insert_point[1]=insert_point[1]-1

				at_key=at_string_list[menu_dat.val-1]
				at_pts=at_key[1]
				points=curve.points
				loop_index=0
				for point in points:
					time=point.pt[0]
					if time> at_pts[1]:
						insert_point[1]=loop_index
						middle_key=1 
						from_key=from_string_list[menu_from.val-1]
						from_pts=from_key[1]
						for point in points:
							time=point.pt[0]
							if time==from_pts[1]:
								insert_point=next_pos
								insert_point[1]=insert_point[1]-1
								middle_key=0 
								break
						break
					loop_index=loop_index+1
			else:
				insert_point=["None",-1]
			insert_last=insert_time+source_length
			if next_key >= insert_last:			#The paste fits between two dest keys
				shift_d=0
				offset=insert_point_data[1]
				weld_z=1
			else:
				offset=insert_point_data[1]
		new_tuple.append(insert_point)
		new_tuple.append(weld_z)
		new_tuple.append(middle_key)
		new_tuple.append(weld_last)
		paste_info.append(new_tuple)
		info_index=info_index+1

		
	normalize_n_offset()			#Normalize the scratchpad times, and then insert the times
									#these keys will exist in the destination curve
	if shift_d:
		shift_dest(paste_info)	#Shift the portion of the destination curves, which will
						#be changed by insertion of a new segment from the scratchpad
	paste(paste_info,insert_point_string,info_list)			#Do the final paste, and then clean up things
	
def error():
    global col_r,col_g, col_b
    col_r=1
    col_g=0
    col_b=0
    Redraw()

def msg_green():
    global col_r, col_g, col_b
    col_r=0
    col_g=1
    col_b=0
    Redraw()
    
def msg_white():
    global col_r, col_g, col_b    
    col_r=1
    col_g=1
    col_b=1
    Redraw()
 
def read_file():
 	
	global filename, filetxt, string7, pointerf, this_line
	global source_name, dest_name, indexf, groups, string7
 	   
	try:
		f=open(filename) #open group file
		filetxt= f.readlines()
		f.close()
		groups=[]
		pointerf=0
		looper=len(filetxt)
		
		
		if not (len(filetxt)):
			string7='Group File Empty!'
			error()
			return
		
		while looper !=1:
			this_line=filetxt[pointerf]
			source_name=""
			dest_name=""
			indexf=0
			group_list=[]

			if this_line[indexf]!= '\011':
				group_name=this_line
				group_name_tmp=group_name
				pointerf=pointerf+1
			else:
				while this_line[0] == '\011':
					member()
					both=source_name,dest_name
					group_list.append(both)
					looper=looper-1
					pointerf=pointerf+1
					if pointerf>len(filetxt)-1:
						both=group_name_tmp,group_list
						groups.append(both)
						string7=(string12[0]) #Place a message success line
						build_file_menu()
						msg_green()
						return
					this_line=filetxt[pointerf]
				both=group_name_tmp,group_list
				groups.append(both)
	
	except IOError:
		string7=(string12[1]+filename)
		error()
def member():
	global source_name, dest_name, this_line, pointerf, indexf   
	indexf=1
	source_name=""
	dest_name=""
	while this_line[indexf] != '\011':
		source_name=source_name+this_line[indexf]
		indexf=indexf+1
	indexf=indexf+1
	while this_line[indexf] != '\012':
		dest_name=dest_name+this_line[indexf]
		indexf=indexf+1
	#pointerf=pointerf+1
	this_line=filetxt[pointerf]
	if this_line[0]!='\011':
		return()
    
def run_process():
	global source_ipo, dest_ipo, string7
	invalid=1
	warn_keylength=0
	if not use_slider:
		test_at=at_string_list[menu_dat.val-1]
		this_ipo=ipo_s[menu_d.val-1]
		curves=this_ipo.curves
	
		for curve in curves:
			menu_name=test_at[0]
			if menu_name==curve.name:
				points=curve.points
				values=test_at[1]
				index=values[0]
				pt_value=values[1]
				try:
					point=points[index]
				except:
					string7="AT menu is invalid, refresh it"
					error()
					warn_keylength=1
					return invalid ,warn_keylength
				if point.pt[0]!=pt_value:
					string7="AT menu is invalid, refresh it"
					error()
					warn_keylength=1
					return invalid ,warn_keylength
	test_from=from_string_list[menu_from.val-1]
	this_ipo=ipo_s[menu.val-1]
	curves=this_ipo.curves
	
	for curve in curves:
		menu_name=test_from[0]
		if menu_name==curve.name:
			points=curve.points
			values=test_from[1]
			index=values[0]
			pt_value=values[1]
			try:
				point=points[index]
			except:
				string7="From menu is invalid, refresh it"
				error()
				warn_keylength=1
				return invalid ,warn_keylength
			if point.pt[0]!=pt_value:
				string7="From menu is invalid, refresh it"
				error()
				warn_keylength=1
				return invalid ,warn_keylength
	test_thru=thru_string_list[menu_thru.val-1]
	this_ipo=ipo_s[menu.val-1]
	curves=this_ipo.curves
	
	for curve in curves:
		menu_name=test_thru[0]
		if menu_name==curve.name:
			points=curve.points
			values=test_thru[1]
			index=values[0]
			pt_value=values[1]
			try:
				point=points[index]
			except:
				string7="Thru menu is invalid, refresh it"
				error()
				warn_keylength=1
				return invalid ,warn_keylength
			if point.pt[0]!=pt_value:
				string7="Thru menu is invalid, refresh it"
				error()
				warn_keylength=1
				return invalid ,warn_keylength
	if toggle.val:
		if not group:
			string7="No Group Defined!"
			error()
	  		return invalid ,warn_keylength
		elif  menu_from.val<1:
			string7="No From Value!"
			error()
			return invalid,warn_keylength
		elif  menu_thru.val<1:
			string7="No Thru Value!"
			error()
			return invalid,warn_keylength
		elif not use_slider:
			if  menu_dat.val<1:
				string7="No At Value!"
				error()
				return invalid,warn_keylength
	  	num=0
		for size in range (len(group_list)):
			pair=group_list[num]
			source_ipo=pair[0]
			dest_ipo = pair[1]
			#invalid=validate()
			#if invalid:
			#	return invalid
			num=num+1
		num=0
		for size in range (len(group_list)):
			pair=group_list[num]
			source_ipo=pair[0]
			dest_ipo = pair[1]
			execute()
			num=num+1
			
	else:
		if not use_slider:
			if  menu_dat.val<1:
				string7="No At Value!"
				error()
				return invalid,warn_keylength
		if  menu_from.val<1:
			string7="No From Value!"
			error()
			return invalid,warn_keylength
		elif  menu_thru.val<1:
			string7="No Thru Value!"
			error()
			return invalid,warn_keylength
		elif  menu_d.val<1:
			string7="No Dest IPO!"
			error()
			return invalid,warn_keylength
		elif  menu.val<1:
			string7="No Source IPO!"
			error()
			return invalid,warn_keylength
		source_ipo=menu.val-1
		dest_ipo = menu_d.val-1
	
		execute()

	string7='DONE!'
	msg_green()
	invalid=0
	return invalid,warn_keylength
def retry():
	
	invalid, warn_keylength=run_process()
	if invalid:
		if warn_keylength:
			return
		refresh()
		invalid=run_process()
	ipo_s=Ipo.Get()
	for ip in ipo_s:
		Ipo.Recalc(ip)
				
Register(draw, event, bevent)

read_file()


str_id=0	
get_ipos(str_id)
str_id=1
get_ipos(str_id)
str_id=2
get_ipos(str_id)
find_key_max()

############################
############################
############################
############################
############################
############################
############################