/**
 * $Id: editipo.c,v 1.34 2004/08/03 21:58:07 ton Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */


/* this code feels over-complex, mostly because I choose in the past to devise a system
  that converts the Ipo blocks (linked to Object, Material, etc), into a copy of that
  data which is being worked on;  the 'editipo'.
  The editipo then can have 'ipokey' data, which is optimized for editing curves as if
  it were key positions. This is still a great feature to work with, which makes ipo editing
  in Blender still valuable. However, getting this beast under control was hard, even
  for me... (ton) */

#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef WIN32
#include <unistd.h>
#else
#include <io.h>
#include "BLI_winstuff.h"
#endif   
#include "MEM_guardedalloc.h"
#include "PIL_time.h"

#include "BLI_blenlib.h"
#include "BLI_arithb.h"

#include "DNA_constraint_types.h"
#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "DNA_lamp_types.h"
#include "DNA_sequence_types.h"
#include "DNA_sound_types.h"
#include "DNA_camera_types.h"
#include "DNA_material_types.h"
#include "DNA_texture_types.h"
#include "DNA_key_types.h"
#include "DNA_screen_types.h"
#include "DNA_scene_types.h"
#include "DNA_ipo_types.h"
#include "DNA_curve_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "DNA_view3d_types.h"
#include "DNA_group_types.h"
#include "DNA_ika_types.h"

#include "BKE_utildefines.h"
#include "BKE_action.h"
#include "BKE_anim.h"
#include "BKE_material.h"
#include "BKE_texture.h"
#include "BKE_ipo.h"
#include "BKE_key.h"
#include "BKE_ika.h"
#include "BKE_displist.h"
#include "BKE_global.h"
#include "BKE_group.h"

#include "BIF_butspace.h"
#include "BIF_editkey.h"
#include "BIF_editseq.h"
#include "BIF_editview.h"
#include "BIF_interface.h"
#include "BIF_mywindow.h"
#include "BIF_poseobject.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_toolbox.h"
#include "BIF_poseobject.h"

#include "BDR_drawobject.h"
#include "BDR_editobject.h"

#include "BSE_trans_types.h"
#include "BSE_editipo_types.h"
#include "BSE_drawipo.h"
#include "BSE_editipo.h"
#include "BSE_editaction.h"
#include "BSE_edit.h"
#include "BSE_drawview.h"
#include "BSE_headerbuttons.h"

#include "blendef.h"
#include "mydevice.h"
#include "render.h"

/* forwards */
#define BEZSELECTED(bezt)   (((bezt)->f1 & 1) || ((bezt)->f2 & 1) || ((bezt)->f3 & 1))

#define IPOTHRESH	0.9
#define ISPOIN(a, b, c)                       ( (a->b) && (a->c) )
#define ISPOIN3(a, b, c, d)           ( (a->b) && (a->c) && (a->d) )
#define ISPOIN4(a, b, c, d, e)        ( (a->b) && (a->c) && (a->d) && (a->e) )   

extern int ob_ar[];
extern int ma_ar[];
extern int seq_ar[];
extern int cu_ar[];
extern int key_ar[];
extern int wo_ar[];
extern int la_ar[];
extern int cam_ar[];
extern int snd_ar[];
extern int ac_ar[];
extern int co_ar[];
extern int te_ar[];

char *ob_ic_names[OB_TOTNAM] = { "LocX", "LocY", "LocZ", "dLocX", "dLocY", "dLocZ",
						 "RotX", "RotY", "RotZ", "dRotX", "dRotY", "dRotZ",
						 "SizeX", "SizeY", "SizeZ", "dSizeX", "dSizeY", "dSizeZ",
						 "Layer", "Time", "ColR", "ColG", "ColB", "ColA",
						 "FStreng", "FFall", "Damping", "RDamp", "Perm" };
char *obeff_ic_names[3] = { "EffX", "EffY", "EffZ" };
char *co_ic_names[CO_TOTNAM] = { "Inf" };
char *mtex_ic_names[TEX_TOTNAM] = { "OfsX", "OfsY", "OfsZ", "SizeX", "SizeY", "SizeZ",
						  "texR", "texG", "texB", "DefVar", "Col", "Nor", "Var",
						  "Disp" };
char *tex_ic_names[TE_TOTNAM] = { "NSize", "NDepth", "NType", "Turb", "Vnw1", "Vnw2",
								   "Vnw3", "Vnw4", "MinkMExp", "DistM", "ColT", "iScale",
								   "DistA", "MgType", "MgH", "Lacu", "Oct", "MgOff",
								   "MgGain", "NBase1", "NBase2" };
char *ma_ic_names[MA_TOTNAM] = { "R", "G", "B", "SpecR", "SpecG", "SpecB", "MirR",
						 "MirG", "MirB", "Ref", "Alpha", "Emit", "Amb", "Spec",
						 "Hard", "SpTra", "Ior", "Mode", "HaSize", "Translu",
						 "RayMir", "FresMir", "FresMirI", "FresTra", "FresTraI",
						 "TraGlow" };
char *seq_ic_names[SEQ_TOTNAM] = { "Fac" };
char *cu_ic_names[CU_TOTNAM] = { "Speed" };
char *key_ic_names[KEY_TOTNAM] = { "Speed", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5",
									"Key 6", "Key 7", "Key 8", "Key 9", "Key 10",
									"Key 11", "Key 12", "Key 13", "Key 14", "Key 15",
									"Key 16", "Key 17", "Key 18", "Key 19", "Key 20",
									"Key 21", "Key 22", "Key 23", "Key 24", "Key 25",
									"Key 26", "Key 27", "Key 28", "Key 29", "Key 30",
									"Key 31" };
char *wo_ic_names[WO_TOTNAM] = { "HorR", "HorG", "HorB", "ZenR", "ZenG", "ZenB", "Expos",
						 "Misi", "MisDi", "MisSta", "MisHi", "StarR", "StarB",
						 "StarG", "StarDi", "StarSi" };
char *la_ic_names[LA_TOTNAM] = { "Energ", "R", "G", "B", "Dist", "SpoSi", "SpoBl",
								  "Quad1", "Quad2", "HaInt" };
char *cam_ic_names[CAM_TOTNAM] = { "Lens", "ClSta", "ClEnd" };
char *snd_ic_names[SND_TOTNAM] = { "Vol", "Pitch", "Pan", "Atten" };
char *ac_ic_names[AC_TOTNAM] = {"LocX", "LocY", "LocZ", "SizeX", "SizeY",
						  "SizeZ", "QuatX", "QuatY", "QuatZ", "QuatW"};
char *ic_name_empty[1] ={ "" };

char *getname_ac_ei(int nr) {
	switch(nr) {
	case AC_LOC_X:
	case AC_LOC_Y:
	case AC_LOC_Z:
		return ac_ic_names[nr-1];
	case AC_SIZE_X:
	case AC_SIZE_Y:
	case AC_SIZE_Z:
		return ac_ic_names[nr-10];
	case AC_QUAT_X:
	case AC_QUAT_Y:
	case AC_QUAT_Z:
	case AC_QUAT_W:
		return ac_ic_names[nr-19];
	default:
		return ic_name_empty[0]; /* empty */
	}
}

char *getname_co_ei(int nr)
{
	switch(nr){
	case CO_ENFORCE:
		return co_ic_names[nr-1];
	}
	return ic_name_empty[0];
}

char *getname_ob_ei(int nr, int colipo)
{
	if(!colipo && (nr>=OB_EFF_X && nr <=OB_EFF_Z)) {
		return obeff_ic_names[nr-OB_EFF_X];
	} else {
		if(nr>=OB_LOC_X && nr <= OB_PD_PERM) return ob_ic_names[nr-1];
	}
	return ic_name_empty[0];
}

char *getname_tex_ei(int nr)
{
	if(nr>=TE_NSIZE && nr<=TE_N_BAS2) return tex_ic_names[nr-1];

	return ic_name_empty[0];
}

char *getname_mtex_ei(int nr)
{
	if(nr>=MAP_OFS_X && nr<=MAP_DISP) return mtex_ic_names[nr-1];
	
	return ic_name_empty[0];
}

char *getname_mat_ei(int nr)
{
	if(nr>=MA_MAP1) return getname_mtex_ei((nr & (MA_MAP1-1)));
	else {
		if(nr>=MA_COL_R && nr<=MA_ADD) return ma_ic_names[nr-1];
	}
	return ic_name_empty[0];
}

char *getname_world_ei(int nr)
{
	if(nr>=MA_MAP1) return getname_mtex_ei((nr & (MA_MAP1-1)));
	else {
		if(nr>=WO_HOR_R && nr<=WO_STARSIZE) return wo_ic_names[nr-1];
	}
	return ic_name_empty[0];
}

char *getname_seq_ei(int nr)
{
	if(nr == SEQ_FAC1) return seq_ic_names[nr-1];
	return ic_name_empty[0];
}

char *getname_cu_ei(int nr)
{
	if(nr==CU_SPEED) return cu_ic_names[nr-1];
	return ic_name_empty[0];
}

char *getname_key_ei(int nr)
{
	if(nr>=KEY_SPEED && nr<=31) return key_ic_names[nr];
	return ic_name_empty[0];
}

char *getname_la_ei(int nr)
{
	if(nr>=MA_MAP1) return getname_mtex_ei((nr & (MA_MAP1-1)));
	else {
		if(nr>=LA_ENERGY && nr<=LA_HALOINT) return la_ic_names[nr-1];
	}
	return ic_name_empty[0];
}

char *getname_cam_ei(int nr)
{
	if(nr>=CAM_LENS && nr<=CAM_END) return cam_ic_names[nr-1];
	return ic_name_empty[0];
}

char *getname_snd_ei(int nr)
{
	if(nr>=SND_VOLUME && nr<=SND_ATTEN) return snd_ic_names[nr-1];
	return ic_name_empty[0];
}

IpoCurve *find_ipocurve(Ipo *ipo, int adrcode)
{
	if(ipo) {
		IpoCurve *icu= ipo->curve.first;
		while(icu) {
			if(icu->adrcode==adrcode) return icu;
			icu= icu->next;
		}
	}
	return NULL;
}

void boundbox_ipocurve(IpoCurve *icu)
{
	BezTriple *bezt;
	float vec[3]={0.0,0.0,0.0};
	float min[3], max[3];
	int a;
	
	if(icu->totvert) {
		INIT_MINMAX(min, max);
		
		if(icu->bezt ) {
			a= icu->totvert;
			bezt= icu->bezt;
			while(a--) {
				if(icu->vartype & IPO_BITS) {
					vec[0]= bezt->vec[1][0];
					vec[1]= 0.0;
					DO_MINMAX(vec, min, max);
					
					vec[1]= 16.0;
					DO_MINMAX(vec, min, max);
				}
				else {
					if(icu->ipo==IPO_BEZ && a!=icu->totvert-1) {
						DO_MINMAX(bezt->vec[0], min, max);
					}
					DO_MINMAX(bezt->vec[1], min, max);
					if(icu->ipo==IPO_BEZ && a!=0) {
						DO_MINMAX(bezt->vec[2], min, max);
					}
				}
				
				bezt++;
			}
		}
		if(min[0]==max[0]) max[0]= (float)(min[0]+1.0);
		if(min[1]==max[1]) max[1]= (float)(min[1]+0.1);
		
		icu->totrct.xmin= min[0];
		icu->totrct.ymin= min[1];
		icu->totrct.xmax= max[0];
		icu->totrct.ymax= max[1];
	}
	else {
		icu->totrct.xmin= icu->totrct.ymin= 0.0;
		icu->totrct.xmax= EFRA;
		icu->totrct.ymax= 1.0;
	}
}

void boundbox_ipo(Ipo *ipo, rctf *bb)
{
	IpoCurve *icu;
	int first= 1;
	
	icu= ipo->curve.first;
	while(icu) {
			
		boundbox_ipocurve(icu);
				
		if(first) {
			*bb= icu->totrct;
			first= 0;
		}
		else BLI_union_rctf(bb, &(icu->totrct));
		
		icu= icu->next;
	}
}



void editipo_changed(SpaceIpo *si, int doredraw)
{
	EditIpo *ei;
	View2D *v2d;
	Key *key;
	KeyBlock *kb;
	int a, first=1;


	ei= si->editipo;
	if(ei==0)
		return;

	for(a=0; a<si->totipo; a++, ei++) {
		
		if(ei->icu) {
			
				/* twice because of ittererating new autohandle */
			calchandles_ipocurve(ei->icu);
			calchandles_ipocurve(ei->icu);
			
			if(ei->flag & IPO_VISIBLE) {
		
				boundbox_ipocurve(ei->icu);
				sort_time_ipocurve(ei->icu);
				if(first) {
					si->v2d.tot= ei->icu->totrct;
					first= 0;
				}
				else BLI_union_rctf(&(si->v2d.tot), &(ei->icu->totrct));
			}
		}
	}

	v2d= &(si->v2d);	

	/* keylines? */
	if(si->blocktype==ID_KE) {
		key= (Key *)si->from;
		if(key && key->block.first) {
			kb= key->block.first;
			if(kb->pos < v2d->tot.ymin) v2d->tot.ymin= kb->pos;
			kb= key->block.last;
			if(kb->pos > v2d->tot.ymax) v2d->tot.ymax= kb->pos;
		}
	}
	
	
	/* is there no curve? */
	if(first) {
		v2d->tot.xmin= 0.0;
		v2d->tot.xmax= EFRA;
		v2d->tot.ymin= (float)-0.1;
		v2d->tot.ymax= (float)1.1;
	
		if(si->blocktype==ID_SEQ) {
			v2d->tot.xmin= -5.0;
			v2d->tot.xmax= 105.0;
			v2d->tot.ymin= (float)-0.1;
			v2d->tot.ymax= (float)1.1;
		}
	}
	
	si->tot= v2d->tot;	
	
	if(doredraw) {
		/* if you always call do_ipo: you get problems with insertkey, for example
		 * when inserting only a 'loc' the 'ob->rot' value then is changed.
		 */

		if(si->blocktype==ID_OB) { 			
				/* clear delta loc,rot,size (when free/delete ipo) */
			clear_delta_obipo(si->ipo);
			
		}
	
		do_ipo(si->ipo);

		allqueue(REDRAWIPO, 0);
		allqueue (REDRAWACTION, 0);
		allqueue(REDRAWNLA, 0);
		allqueue(REDRAWBUTSOBJECT, 0);
		
		if(si->blocktype==ID_OB) {
			Object *ob= (Object *)si->from;			
			if(ob && ob->type==OB_IKA) itterate_ika(ob);
			allqueue(REDRAWVIEW3D, 0);
			allqueue(REDRAWNLA, 0);
		}

		else if(si->blocktype==ID_MA) allqueue(REDRAWBUTSSHADING, 0);
		else if(si->blocktype==ID_TE) allqueue(REDRAWBUTSSHADING, 0);
		else if(si->blocktype==ID_WO) allqueue(REDRAWBUTSSHADING, 0);
		else if(si->blocktype==ID_LA) allqueue(REDRAWBUTSSHADING, 0);
//		else if(si->blocktype==ID_SO) allqueue(REDRAWBUTSSOUND, 0);
		else if(si->blocktype==ID_CA) {
			allqueue(REDRAWBUTSEDIT, 0);
			allqueue(REDRAWVIEW3D, 0);
		}
		else if(si->blocktype==ID_SEQ) clear_last_seq();
		else if(si->blocktype==ID_AC){
			do_all_actions();
			allqueue(REDRAWACTION, 0);
			allqueue(REDRAWNLA, 0);
		}
		else if(si->blocktype==ID_KE) {
			do_spec_key((Key *)si->from);
			allqueue(REDRAWVIEW3D, 0);
		}
		else if(si->blocktype==ID_CU) {
			calc_curvepath(OBACT);
			allqueue(REDRAWVIEW3D, 0);
		}
	}

	if(si->showkey) make_ipokey();
}

void scale_editipo()
{
	/* comes from buttons, scale with G.sipo->tot rect */
	
	EditIpo *ei;
	BezTriple *bezt;
	float facx, facy;
	int a, b;	
	
	facx= (G.sipo->tot.xmax-G.sipo->tot.xmin)/(G.sipo->v2d.tot.xmax-G.sipo->v2d.tot.xmin);
	facy= (G.sipo->tot.ymax-G.sipo->tot.ymin)/(G.sipo->v2d.tot.ymax-G.sipo->v2d.tot.ymin);

	ei= G.sipo->editipo;
	if(ei==0) return;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			bezt= ei->icu->bezt;
			b= ei->icu->totvert;
			while(b--) {
				
				bezt->vec[0][0]= facx*(bezt->vec[0][0] - G.sipo->v2d.tot.xmin) + G.sipo->tot.xmin;
				bezt->vec[1][0]= facx*(bezt->vec[1][0] - G.sipo->v2d.tot.xmin) + G.sipo->tot.xmin;
				bezt->vec[2][0]= facx*(bezt->vec[2][0] - G.sipo->v2d.tot.xmin) + G.sipo->tot.xmin;
			
				bezt->vec[0][1]= facy*(bezt->vec[0][1] - G.sipo->v2d.tot.ymin) + G.sipo->tot.ymin;
				bezt->vec[1][1]= facy*(bezt->vec[1][1] - G.sipo->v2d.tot.ymin) + G.sipo->tot.ymin;
				bezt->vec[2][1]= facy*(bezt->vec[2][1] - G.sipo->v2d.tot.ymin) + G.sipo->tot.ymin;

				bezt++;
			}
		}
	}
	editipo_changed(G.sipo, 1);
	allqueue(REDRAWNLA, 0);
	allqueue (REDRAWACTION, 0);
	allqueue(REDRAWIPO, 0);
}


Ipo *get_ipo_to_edit(ID **from)
{
	Object *ob= OBACT;
	
	*from= 0;
	
	
	if (G.sipo->pin) {
		*from = G.sipo->from;
		return G.sipo->ipo;
	}

	if(G.sipo->blocktype==ID_SEQ) {
		extern Sequence *last_seq;
		
		*from= (ID *)last_seq;
		if(last_seq) return last_seq->ipo;
	}
	else if(G.sipo->blocktype==IPO_CO){
		if (ob && ob->activecon){
			*from= (ID*) ob;
			return ob->activecon->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_AC) {
		bActionChannel *chan;
		if (ob && ob->action){
			*from= (ID *) ob->action;
			chan= get_hilighted_action_channel(ob->action);
			if (chan)
				return chan->ipo;
			else{
				*from = NULL;
				return NULL;
			}
		}

	}
	else if(G.sipo->blocktype==ID_WO) {
		World *wo= G.scene->world;
		*from= (ID *)wo;
		if(wo) return wo->ipo;
	}
	else if(G.sipo->blocktype==ID_OB) {
		if(ob) {
			*from= (ID *)ob;
			return ob->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_TE) {
		if(ob) {
			Tex *tex= give_current_texture(ob, ob->actcol);
			*from= (ID *)tex;
			if(tex) return tex->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_MA) {
		if(ob) {
			Material *ma= give_current_material(ob, ob->actcol);
			*from= (ID *)ma;
			if(ma) return ma->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_KE) {
		if(ob) {
			Key *key= give_current_key(ob);
			*from= (ID *)key;
			if(key) return key->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_CU) {
		if(ob && ob->type==OB_CURVE) {
			Curve *cu= ob->data;
			*from= (ID *)cu;
			return cu->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_LA) {
		if(ob && ob->type==OB_LAMP) {
			Lamp *la= ob->data;
			*from= (ID *)la;
			return la->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_CA) {
		if(ob && ob->type==OB_CAMERA) {
			Camera *ca= ob->data;
			*from= (ID *)ca;
			if(ca) return ca->ipo;
		}
	}
	else if(G.sipo->blocktype==ID_SO) {

//		if (G.buts && G.buts->mainb == BUTS_SOUND) {
//			bSound *sound = G.buts->lockpoin;
//			*from= (ID *)sound;
//			if(sound) return sound->ipo;
//		}
	}

	return NULL;
}

unsigned int ipo_rainbow(int cur, int tot)
{
	float dfac, fac, sat;

	dfac= (float)(1.0/( (float)tot+1.0));

	/* this calculation makes 2 different cycles of rainbow colors */
	if(cur< tot/2) fac= (float)(cur*2.0*dfac);
	else fac= (float)((cur-tot/2)*2.0*dfac +dfac);
	
	if(fac>0.5 && fac<0.8) sat= (float)0.4;
	else sat= 0.5;
	
	return hsv_to_cpack(fac, sat, 1.0);
}		

void make_ob_editipo(Object *ob, SpaceIpo *si)
{
	EditIpo *ei;
	int a, len, colipo=0;
	char *name;
	
	if(ob->type==OB_MESH) colipo= 1;

	ei= si->editipo= MEM_callocN(OB_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= OB_TOTIPO;
	
	for(a=0; a<OB_TOTIPO; a++) {
		name = getname_ob_ei(ob_ar[a], colipo);
		strcpy(ei->name, name);
		ei->adrcode= ob_ar[a];
		
		if ELEM6(ei->adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z, OB_DROT_X, OB_DROT_Y, OB_DROT_Z) ei->disptype= IPO_DISPDEGR;
		else if(ei->adrcode==OB_LAY) ei->disptype= IPO_DISPBITS;
		else if(ei->adrcode==OB_TIME) ei->disptype= IPO_DISPTIME;

		ei->col= ipo_rainbow(a, OB_TOTIPO);

		if(colipo) {
			len= strlen(ei->name);
			if(len) {
				if( ei->name[ len-1 ]=='R') ei->col= 0x5050FF;
				else if( ei->name[ len-1 ]=='G') ei->col= 0x50FF50;
				else if( ei->name[ len-1 ]=='B') ei->col= 0xFF7050;
			}
		}
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}
}


void make_seq_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	ei= si->editipo= MEM_callocN(SEQ_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= SEQ_TOTIPO;
	
	
	for(a=0; a<SEQ_TOTIPO; a++) {
		name = getname_seq_ei(seq_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= seq_ar[a];
		
		ei->col= ipo_rainbow(a, SEQ_TOTIPO);
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		else ei->flag |= IPO_VISIBLE;
		
		ei++;
	}
}

void make_cu_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	ei= si->editipo= MEM_callocN(CU_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= CU_TOTIPO;
	
	
	for(a=0; a<CU_TOTIPO; a++) {
		name = getname_cu_ei(cu_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= cu_ar[a];
		
		ei->col= ipo_rainbow(a, CU_TOTIPO);
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		else ei->flag |= IPO_VISIBLE;
		 
		ei++;
	}
}

void make_key_editipo(SpaceIpo *si)
{
	Key *key;
	KeyBlock *kb=NULL;
	EditIpo *ei;
	int a;
	char *name;
	
	ei= si->editipo= MEM_callocN(KEY_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= KEY_TOTIPO;
	
	key= (Key *)G.sipo->from;
	if(key) kb= key->block.first;
	
	for(a=0; a<KEY_TOTIPO; a++, ei++) {
		if(kb && kb->name[0] != 0) strncpy(ei->name, kb->name, 32);	// length both same
		else {
			name = getname_key_ei(key_ar[a]);
			strcpy(ei->name, name);
		}
		ei->adrcode= key_ar[a];
		
		ei->col= ipo_rainbow(a, KEY_TOTIPO);
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		else if(a==0) ei->flag |= IPO_VISIBLE;
		 
		if(kb) kb= kb->next;
	}
	
	ei= si->editipo;
	if(key && key->type==KEY_RELATIVE) {
		strcpy(ei->name, "----");
	}
	else {
		ei->flag |= IPO_VISIBLE;
	}
}

int texchannel_to_adrcode(int channel)
{
	switch(channel) {
		case 0: return MA_MAP1;
		case 1: return MA_MAP2; 
		case 2: return MA_MAP3; 
		case 3: return MA_MAP4; 
		case 4: return MA_MAP5; 
		case 5: return MA_MAP6; 
		case 6: return MA_MAP7; 
		case 7: return MA_MAP8; 
		default: return 0;
	}
}

void make_mat_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a, len;
	char *name;
	
	if(si->from==0) return;
	
	ei= si->editipo= MEM_callocN(MA_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= MA_TOTIPO;
	
	for(a=0; a<MA_TOTIPO; a++) {
		name = getname_mat_ei(ma_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= ma_ar[a];
		
		if(ei->adrcode & MA_MAP1) {
			ei->adrcode-= MA_MAP1;
			ei->adrcode |= texchannel_to_adrcode(si->channel);
		}
		else {
			if(ei->adrcode==MA_MODE) ei->disptype= IPO_DISPBITS;
		}
		
		ei->col= ipo_rainbow(a, MA_TOTIPO);
		
		len= strlen(ei->name);
		if(len) {
			if( ei->name[ len-1 ]=='R') ei->col= 0x5050FF;
			else if( ei->name[ len-1 ]=='G') ei->col= 0x50FF50;
			else if( ei->name[ len-1 ]=='B') ei->col= 0xFF7050;
		}
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}
}

void make_texture_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	if(si->from==0) return;    
	
	ei= si->editipo= MEM_callocN(TE_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= TE_TOTIPO;
	
	for(a=0; a<TE_TOTIPO; a++) {
		name = getname_tex_ei(te_ar[a]);
		strcpy(ei->name, name);
			ei->adrcode= te_ar[a];

		ei->col= ipo_rainbow(a, TE_TOTIPO);
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}
}

void make_world_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a, len;
	char *name;
	
	if(si->from==0) return;
	
	ei= si->editipo= MEM_callocN(WO_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= WO_TOTIPO;
	
	for(a=0; a<WO_TOTIPO; a++) {
		name = getname_world_ei(wo_ar[a]);
		
		strcpy(ei->name, name); 
		ei->adrcode= wo_ar[a];
		
		if(ei->adrcode & MA_MAP1) {
			ei->adrcode-= MA_MAP1;
			ei->adrcode |= texchannel_to_adrcode(si->channel);
		}
		else {
			if(ei->adrcode==MA_MODE) ei->disptype= IPO_DISPBITS;
		}
		
		ei->col= ipo_rainbow(a, WO_TOTIPO);
		
		len= strlen(ei->name);
		if(len) {
			if( ei->name[ len-1 ]=='R') ei->col= 0x5050FF;
			else if( ei->name[ len-1 ]=='G') ei->col= 0x50FF50;
			else if( ei->name[ len-1 ]=='B') ei->col= 0xFF7050;
		}
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}
}

void make_lamp_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	ei= si->editipo= MEM_callocN(LA_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= LA_TOTIPO;
	
	
	for(a=0; a<LA_TOTIPO; a++) {
		name = getname_la_ei(la_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= la_ar[a];

		if(ei->adrcode & MA_MAP1) {
			ei->adrcode-= MA_MAP1;
			ei->adrcode |= texchannel_to_adrcode(si->channel);
		}

		ei->col= ipo_rainbow(a, LA_TOTIPO);
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}
}

void make_camera_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	ei= si->editipo= MEM_callocN(CAM_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= CAM_TOTIPO;
	
	
	for(a=0; a<CAM_TOTIPO; a++) {
		name = getname_cam_ei(cam_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= cam_ar[a];

		ei->col= ipo_rainbow(a, CAM_TOTIPO);
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}
}

int make_constraint_editipo(Ipo *ipo, EditIpo **si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	ei= *si= MEM_callocN(CO_TOTIPO*sizeof(EditIpo), "editipo");
	
	for(a=0; a<CO_TOTIPO; a++) {
		name = getname_co_ei(co_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= co_ar[a];

		ei->col= ipo_rainbow(a, CO_TOTIPO);
		
		ei->icu= find_ipocurve(ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}

	return CO_TOTIPO;
}
int make_action_editipo(Ipo *ipo, EditIpo **si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	ei= *si= MEM_callocN(AC_TOTIPO*sizeof(EditIpo), "editipo");
	
	for(a=0; a<AC_TOTIPO; a++) {
		name = getname_ac_ei(ac_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= ac_ar[a];

		ei->col= ipo_rainbow(a, AC_TOTIPO);
		
		ei->icu= find_ipocurve(ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}

	return AC_TOTIPO;
}

void make_sound_editipo(SpaceIpo *si)
{
	EditIpo *ei;
	int a;
	char *name;
	
	ei= si->editipo= MEM_callocN(SND_TOTIPO*sizeof(EditIpo), "editipo");
	
	si->totipo= SND_TOTIPO;
	
	
	for(a=0; a<SND_TOTIPO; a++) {
		name = getname_snd_ei(snd_ar[a]);
		strcpy(ei->name, name);
		ei->adrcode= snd_ar[a];

		ei->col= ipo_rainbow(a, SND_TOTIPO);
		
		ei->icu= find_ipocurve(si->ipo, ei->adrcode);
		if(ei->icu) {
			ei->flag= ei->icu->flag;
		}
		
		ei++;
	}
}

void make_editipo()
{
	EditIpo *ei;
	Object *ob;
	ID *from;
	rctf *rf;
	int a;

	if(G.sipo->editipo)
		MEM_freeN(G.sipo->editipo);
	G.sipo->editipo= 0;
	G.sipo->totipo= 0;
	ob= OBACT;

	G.sipo->ipo= get_ipo_to_edit(&from);
	G.sipo->from= from;

	if(G.sipo->ipo) G.sipo->showkey= G.sipo->ipo->showkey;

	if(G.sipo->blocktype==ID_SEQ) {
		make_seq_editipo(G.sipo);
	}
	else if(G.sipo->blocktype==ID_WO) {
		make_world_editipo(G.sipo);
	} 
	else if(G.sipo->blocktype==ID_OB) {
		if (ob) {
			ob->ipowin= ID_OB;
			make_ob_editipo(ob, G.sipo);
		}
	}
	else if(G.sipo->blocktype==ID_MA) {
		if (ob) {
			ob->ipowin= ID_MA;
			make_mat_editipo(G.sipo);
		}
	}
	else if(G.sipo->blocktype==ID_CU) {
		if (ob) {
			ob->ipowin= ID_CU;
			make_cu_editipo(G.sipo);
		}
	}
	else if(G.sipo->blocktype==ID_KE) {
		if (ob) {
			ob->ipowin= ID_KE;
			make_key_editipo(G.sipo);
		}
	}
	else if(G.sipo->blocktype==ID_LA) {
		if (ob) {
			ob->ipowin= ID_LA;
			make_lamp_editipo(G.sipo);
		}
	}
	else if(G.sipo->blocktype==ID_TE) {
		if (ob) {
			ob->ipowin= ID_TE;
			make_texture_editipo(G.sipo);
		}
	}
	else if(G.sipo->blocktype==ID_CA) {
		if (ob) {
			ob->ipowin= ID_CA;
			make_camera_editipo(G.sipo);
		}
	}
	else if(G.sipo->blocktype==ID_SO) {
		if (ob) {
			ob->ipowin= ID_SO;
			make_sound_editipo(G.sipo);
		}
	}
	else if(G.sipo->blocktype==IPO_CO){
		G.sipo->totipo = make_constraint_editipo(G.sipo->ipo, (EditIpo**)&G.sipo->editipo);
		if (ob) {
			ob->ipowin= IPO_CO;
		}
	}
	else if(G.sipo->blocktype==ID_AC) {

		G.sipo->totipo = make_action_editipo(G.sipo->ipo, (EditIpo**)&G.sipo->editipo);
		if (ob) {
			ob->ipowin= ID_AC;
		}
	}

	if(G.sipo->editipo==0) return;
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		
		if(ei->flag & IPO_VISIBLE) ei->flag |= (1<<IPO_IS_SELECTED);
		
		if(ei->icu) ei->icu->flag= ei->flag;
	}
	editipo_changed(G.sipo, 0);
	
	if(G.sipo->ipo) {

		if (G.sipo->pin)
			rf= &(G.sipo->v2d.cur);
		else
			rf= &(G.sipo->ipo->cur);
		
		if(rf->xmin<rf->xmax && rf->ymin<rf->ymax) G.v2d->cur= *rf;
		
	}
	else {
		if(G.sipo->blocktype==ID_OB) {
			G.v2d->cur.xmin= 0.0;
			G.v2d->cur.xmax= EFRA;
			G.v2d->cur.ymin= -5.0;
			G.v2d->cur.ymax= +5.0;
		}
		else if(G.sipo->blocktype==ID_CA) {
			G.v2d->cur.xmin= 0.0;
			G.v2d->cur.xmax= EFRA;
			G.v2d->cur.ymin= 0.0;
			G.v2d->cur.ymax= 100.0;
		}
		else if ELEM5(G.sipo->blocktype, ID_MA, ID_CU, ID_WO, ID_LA, IPO_CO) {
			G.v2d->cur.xmin= (float)-0.1;
			G.v2d->cur.xmax= EFRA;
			G.v2d->cur.ymin= (float)-0.1;
			G.v2d->cur.ymax= (float)+1.1;
		}
		else if(G.sipo->blocktype==ID_TE) {
			G.v2d->cur.xmin= (float)-0.1;
			G.v2d->cur.xmax= EFRA;
			G.v2d->cur.ymin= (float)-0.1;
			G.v2d->cur.ymax= (float)+1.1;
		}
		else if(G.sipo->blocktype==ID_SEQ) {
			G.v2d->cur.xmin= -5.0;
			G.v2d->cur.xmax= 105.0;
			G.v2d->cur.ymin= (float)-0.1;
			G.v2d->cur.ymax= (float)+1.1;
		}
		else if(G.sipo->blocktype==ID_KE) {
			G.v2d->cur.xmin= (float)-0.1;
			G.v2d->cur.xmax= EFRA;
			G.v2d->cur.ymin= (float)-0.1;
			G.v2d->cur.ymax= (float)+2.1;
		}

	}
}


void test_editipo()
{
	Ipo *ipo;
	ID *from;
	
	if(G.sipo->editipo==0){
		make_editipo();
	}
	else {
		ipo= get_ipo_to_edit(&from);

		if(G.sipo->ipo != ipo || G.sipo->from!=from)
			make_editipo();
		
	}

	if (G.sipo->pin)
		return;


	if(G.sipo->ipo)
		G.sipo->ipo->cur = G.v2d->cur;

}

/* ****************************************** */

int totipo_edit, totipo_sel, totipo_vis, totipo_vert, totipo_vertsel, totipo_key, totipo_keysel;

void get_status_editipo()
{
	EditIpo *ei;
	IpoKey *ik;
	BezTriple *bezt;
	int a, b;
	
	totipo_vis= 0;
	totipo_sel= 0;
	totipo_edit= 0;
	totipo_vert= 0;
	totipo_vertsel= 0;
	totipo_key= 0;
	totipo_keysel= 0;
	
	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;
	
	ei= G.sipo->editipo;
	if(ei==0) return;
	for(a=0; a<G.sipo->totipo; a++) {
		if( ei->flag & IPO_VISIBLE ) {
			totipo_vis++;
			if(ei->flag & IPO_SELECT) totipo_sel++;
			if(G.sipo->showkey || (ei->flag & IPO_EDIT)) {
				
				/* if showkey: do count the vertices (for grab) */
				if(G.sipo->showkey==0) totipo_edit++;
				
				if(ei->icu) {
					if(ei->icu->bezt) {
						bezt= ei->icu->bezt;
						b= ei->icu->totvert;
						while(b--) {
							if(ei->icu->ipo==IPO_BEZ) {
								if(bezt->f1 & 1) totipo_vertsel++;
								if(bezt->f3 & 1) totipo_vertsel++;
								totipo_vert+= 2;
							}
							if(bezt->f2 & 1) totipo_vertsel++;
							
							totipo_vert++;
							bezt++;
						}
					}
				}
			}
		}
		ei++;
	}
	
	if(G.sipo->showkey) {
		ik= G.sipo->ipokey.first;
		while(ik) {
			totipo_key++;
			if(ik->flag & 1) totipo_keysel++;
			ik= ik->next;
		}
	}
}



void update_editipo_flags()
{
	EditIpo *ei;
	IpoKey *ik;
	short flag;/*unsigned int flag;*/
	int a;
	
	ei= G.sipo->editipo;
	if(ei) {
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			ei->flag &= ~IPO_VISIBLE;
			flag= (1<<IPO_IS_SELECTED);
			if( ei->flag & flag ) ei->flag |= IPO_VISIBLE;
			
			if(ei->icu) ei->icu->flag= ei->flag;
			
		}
	}
	if(G.sipo->showkey) {
		ik= G.sipo->ipokey.first;
		while(ik) {
			for(a=0; a<G.sipo->totipo; a++) {
				if(ik->data[a]) {
					if(ik->flag & 1) {
						ik->data[a]->f1 |= 1;
						ik->data[a]->f2 |= 1;
						ik->data[a]->f3 |= 1;
					}
					else {
						ik->data[a]->f1 &= ~1;
						ik->data[a]->f2 &= ~1;
						ik->data[a]->f3 &= ~1;
					}
				}
			}
			ik= ik->next;
		}
	}
}

void set_editflag_editipo()
{
	EditIpo *ei;
	int a; /*  , tot= 0, ok= 0; */
	
	/* after showkey immediately go to editing of selected points */
	if(G.sipo->showkey) {
		G.sipo->showkey= 0;
		if(G.sipo->ipo) G.sipo->ipo->showkey= 0;
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) ei->flag |= IPO_SELECT;
		scrarea_queue_headredraw(curarea);
		allqueue(REDRAWVIEW3D, 0);
	}
	
	get_status_editipo();
	
	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {		
		if(ei->icu) {
			if(ei->flag & IPO_VISIBLE) {
				
				if(totipo_edit==0 && (ei->flag & IPO_SELECT)) {
					ei->flag |= IPO_EDIT;
					ei->icu->flag= ei->flag;
				}
				else if(totipo_edit && (ei->flag & IPO_EDIT)) {
					ei->flag -= IPO_EDIT;
					ei->icu->flag= ei->flag;
				}
				else if(totipo_vis==1) {
					if(ei->flag & IPO_EDIT) ei->flag -= IPO_EDIT;
					else ei->flag |= IPO_EDIT;
					ei->icu->flag= ei->flag;
				}
			}
		}
	}
	scrarea_queue_headredraw(curarea);
	scrarea_queue_winredraw(curarea);
}

void ipo_toggle_showkey(void) {
	if(G.sipo->showkey) {
		G.sipo->showkey= 0;
		swap_selectall_editipo();	/* sel all */
	}
	else G.sipo->showkey= 1;
	free_ipokey(&G.sipo->ipokey);
	if(G.sipo->ipo) G.sipo->ipo->showkey= G.sipo->showkey;
}

void swap_selectall_editipo()
{
	Object *ob;
	EditIpo *ei;
	IpoKey *ik;
	BezTriple *bezt;
	int a, b; /*  , sel=0; */
	
	
	deselectall_key();

	get_status_editipo();
	


	if(G.sipo->showkey) {
		ik= G.sipo->ipokey.first;
		while(ik) {
			if(totipo_vertsel) ik->flag &= ~1;
			else ik->flag |= 1;
			ik= ik->next;
		}
		update_editipo_flags();

		if(G.sipo->showkey && G.sipo->blocktype==ID_OB ) {
			ob= OBACT;
			if(ob && (ob->ipoflag & OB_DRAWKEY)) draw_object_ext(BASACT);
		}
	}
	else if(totipo_edit==0) {
		ei= G.sipo->editipo;
		if (ei){
			for(a=0; a<G.sipo->totipo; a++) {
				if( ei->flag & IPO_VISIBLE ) {
					if(totipo_sel) ei->flag &= ~IPO_SELECT;
					else ei->flag |= IPO_SELECT;
				}
				ei++;
			}
			update_editipo_flags();
		}
	}
	else {
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++) {
			if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_EDIT, icu ) {
				bezt= ei->icu->bezt;
				if(bezt) {
					b= ei->icu->totvert;
					while(b--) {
						if(totipo_vertsel) {
							bezt->f1= bezt->f2= bezt->f3= 0;
						}
						else {
							bezt->f1= bezt->f2= bezt->f3= 1;
						}
						bezt++;
					}
				}
			}
			ei++;
		}
		
	}
	
	scrarea_queue_winredraw(curarea);
	
}

void swap_visible_editipo()
{
	EditIpo *ei;
	Object *ob;
	int a; /*  , sel=0; */
	
	get_status_editipo();
	
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++) {
		if(totipo_vis==0) {
			if(ei->icu) {
				ei->flag |= IPO_VISIBLE;
				ei->flag |= (1<<IPO_IS_SELECTED);
			}
		}
		else ei->flag &= ~IPO_VISIBLE;
		ei++;
	}
	
	update_editipo_flags();
	
	if(G.sipo->showkey) {
		
		make_ipokey();
		
		ob= OBACT;
		if(ob && (ob->ipoflag & OB_DRAWKEY)) allqueue(REDRAWVIEW3D, 0);
	}

	scrarea_queue_winredraw(curarea);
	
}

void deselectall_editipo()
{
	EditIpo *ei;
	IpoKey *ik;
	BezTriple *bezt;
	int a, b; /*  , sel=0; */
	
	deselectall_key();

	get_status_editipo();
	
	if(G.sipo->showkey) {
		ik= G.sipo->ipokey.first;
		while(ik) {
			ik->flag &= ~1;
			ik= ik->next;
		}
		update_editipo_flags();

	}
	else if(totipo_edit==0) {
		
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++) {
			if( ei->flag & IPO_VISIBLE ) {
				ei->flag &= ~IPO_SELECT;
			}
			ei++;
		}
		update_editipo_flags();
	}
	else {
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++) {
			if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_EDIT, icu ) {
				if(ei->icu->bezt) {
					bezt= ei->icu->bezt;
					b= ei->icu->totvert;
					while(b--) {
						bezt->f1= bezt->f2= bezt->f3= 0;
						bezt++;
					}
				}
			}
			ei++;
		}
	}
	
	scrarea_queue_winredraw(curarea);
}

short findnearest_ipovert(IpoCurve **icu, BezTriple **bezt)
{
	/* selected verts get a disadvantage */
	/* in icu and (bezt or bp) the nearest is written */
	/* return 0 1 2: handlepunt */
	EditIpo *ei;
	BezTriple *bezt1;
	int dist= 100, temp, a, b;
	short mval[2], hpoint=0;

	*icu= 0;
	*bezt= 0;

	getmouseco_areawin(mval);

	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_EDIT, icu) {
			
			if(ei->icu->bezt) {
				bezt1= ei->icu->bezt;
				b= ei->icu->totvert;
				while(b--) {

					ipoco_to_areaco_noclip(G.v2d, bezt1->vec[0], bezt1->s[0]);
					ipoco_to_areaco_noclip(G.v2d, bezt1->vec[1], bezt1->s[1]);
					ipoco_to_areaco_noclip(G.v2d, bezt1->vec[2], bezt1->s[2]);
										
					if(ei->disptype==IPO_DISPBITS) {
						temp= abs(mval[0]- bezt1->s[1][0]);
					}
					else temp= abs(mval[0]- bezt1->s[1][0])+ abs(mval[1]- bezt1->s[1][1]);

					if( bezt1->f2 & 1) temp+=5;
					if(temp<dist) { 
						hpoint= 1; 
						*bezt= bezt1; 
						dist= temp; 
						*icu= ei->icu; 
					}
					
					if(ei->disptype!=IPO_DISPBITS && ei->icu->ipo==IPO_BEZ) {
						/* middle points get an advantage */
						temp= -3+abs(mval[0]- bezt1->s[0][0])+ abs(mval[1]- bezt1->s[0][1]);
						if( bezt1->f1 & 1) temp+=5;
						if(temp<dist) { 
							hpoint= 0; 
							*bezt= bezt1; 
							dist= temp; 
							*icu= ei->icu; 
						}
		
						temp= abs(mval[0]- bezt1->s[2][0])+ abs(mval[1]- bezt1->s[2][1]);
						if( bezt1->f3 & 1) temp+=5;
						if(temp<dist) { 
							hpoint= 2; 
							*bezt=bezt1; 
							dist= temp; 
							*icu= ei->icu; 
						}
					}
					bezt1++;
				}
			}
		}
	}

	return hpoint;
}


void move_to_frame()
{
	EditIpo *ei;
	BezTriple *bezt;
	ID *id;
	float cfra;
	int a, b;
	
	if(G.sipo->editipo==0) return;

	ei= G.sipo->editipo;

	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			if(G.sipo->showkey || (ei->flag & IPO_EDIT)) {
			
				if(ei->icu->bezt) {
					
					b= ei->icu->totvert;
					bezt= ei->icu->bezt;
					while(b--) {
						if(BEZSELECTED(bezt)) {
						
							cfra=  bezt->vec[1][0]/G.scene->r.framelen;

							id= G.sipo->from;
							if(id && GS(id->name)==ID_OB ) {
								Object *ob= (Object *)id;
								if(ob->sf!=0.0 && (ob->ipoflag & OB_OFFS_OB) ) {
									cfra+= ob->sf/G.scene->r.framelen;
								}
							}
							CFRA= (short)floor(cfra+0.5);
						
							if(CFRA < 1) CFRA= 1;
							update_for_newframe();
							
							break;
						}
						bezt++;
					}
				}
			}
		}
	}
}

/* *********************************** */

void do_ipowin_buts(short event)
{
	EditIpo *ei = 0;
	int a;
	if((G.qual & LR_SHIFTKEY)==0) {
		if(event>G.sipo->totipo) return;
		ei = G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++) {
			if(a==event) ei->flag |= (1<<IPO_IS_SELECTED);
			else ei->flag &= ~(1<<IPO_IS_SELECTED);
			ei++;
		}
	}
	scrarea_queue_winredraw(curarea);
	
	update_editipo_flags();

	if(G.sipo->showkey) {
		make_ipokey();
		if(G.sipo->blocktype==ID_OB) allqueue(REDRAWVIEW3D, 0);
	}

}

void do_ipo_selectbuttons()
{
	EditIpo *ei, *ei1;
	int a, nr;
	short mval[2];
	
	if(G.sipo->showkey) return;
	
	/* do not allow editipo here: convert editipos to selected */
	get_status_editipo();
	if(totipo_edit) {
		set_editflag_editipo();
	}
	
	/* which */
	getmouseco_areawin(mval);

	nr= -(mval[1]-curarea->winy+30-G.sipo->butofs-IPOBUTY)/IPOBUTY;
	if(nr>=0 && nr<G.sipo->totipo) {
		ei= G.sipo->editipo;
		ei+= nr;
		
		if(ei->icu) {
			if((ei->flag & IPO_VISIBLE)==0) {
				ei->flag |= IPO_VISIBLE;
				ei->flag |= (1<<IPO_IS_SELECTED);
			}
	
			if((G.qual & LR_SHIFTKEY)==0) {
				ei1= G.sipo->editipo;
				for(a=0; a<G.sipo->totipo; a++) {
					ei1->flag &= ~IPO_SELECT;
					ei1++;
				}
			}

			if(ei->flag & IPO_SELECT) {
				ei->flag &= ~IPO_SELECT;
			}
			else {
				ei->flag |= IPO_SELECT;
			}

			update_editipo_flags();
			scrarea_queue_winredraw(curarea);
		}
	}
}

/* ******************************************* */

EditIpo *get_editipo()
{
	EditIpo *ei;
	int a; /*  , sel=0; */
	
	get_status_editipo();
	
	if(totipo_edit>1) {
		return 0;
	}
	if(G.sipo->editipo==0) return 0;
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++) {
		if(ei->flag & IPO_VISIBLE) {
			if( ei->flag & IPO_EDIT ) return ei;
			else if(totipo_vis==1) return ei;
			
			if(ei->flag & IPO_SELECT) {
				if(totipo_sel==1) return ei;
			}
		}
		ei++;
	}
	return 0;
}


Ipo *get_ipo(ID *from, short type, int make)
{
	Object *ob;
	Material *ma;
	Tex *tex;
	Curve *cu;
	Sequence *seq;
	Key *key;
	World *wo;
	Lamp *la;
	Camera *ca;
	Ipo *ipo= 0;
	bAction *act;

	if( type==ID_OB) {
		ob= (Object *)from;
		if(ob->id.lib) return 0;
		
		ipo= ob->ipo;
		if(make && ipo==0) ipo= ob->ipo= add_ipo("ObIpo", ID_OB);	
	}
	else if( type==IPO_CO){
		ob= (Object *)from;
		if(ob->id.lib) return 0;

		if (ob->activecon){
			ipo= ob->activecon->ipo;
			if(make && ipo==0) ipo= ob->activecon->ipo= add_ipo("CoIpo", IPO_CO);	
		}
	}
	else if( type==ID_AC) {
		act= (bAction *)from;
		if (!act->achan) return 0;
		if (act->id.lib) return 0;
		ipo= act->achan->ipo;

		/* This should never happen */
		if(make && ipo==0) ipo= act->achan->ipo= add_ipo("AcIpo", ID_AC);
	}
	else if( type==ID_MA) {
		ma= (Material *)from;
		if(ma->id.lib) return 0;
		ipo= ma->ipo;
		
		if(make && ipo==0) ipo= ma->ipo= add_ipo("MatIpo", ID_MA);
	}
	else if( type==ID_TE) {
		tex= (Tex *)from;
		if(tex->id.lib) return 0;
		ipo= tex->ipo;
		
		if(make && ipo==0) ipo= tex->ipo= add_ipo("TexIpo", ID_TE);
	}
	else if( type==ID_SEQ) {
		seq= (Sequence *)from;

		if((seq->type & SEQ_EFFECT)||(seq->type == SEQ_SOUND)) {
			ipo= seq->ipo;
			if(make && ipo==0) ipo= seq->ipo= add_ipo("SeqIpo", ID_SEQ);
		}
		else return 0;
	}		
	else if( type==ID_CU) {
		cu= (Curve *)from;
		if(cu->id.lib) return 0;
		ipo= cu->ipo;
		
		if(make && ipo==0) ipo= cu->ipo= add_ipo("CuIpo", ID_CU);
	}
	else if( type==ID_KE) {
		key= (Key *)from;
		if(key->id.lib) return 0;
		ipo= key->ipo;
		
		if(make && ipo==0) ipo= key->ipo= add_ipo("KeyIpo", ID_KE);
	}
	else if( type==ID_WO) {
		wo= (World *)from;
		if(wo->id.lib) return 0;
		ipo= wo->ipo;
		
		if(make && ipo==0) ipo= wo->ipo= add_ipo("WoIpo", ID_WO);
	}
	else if( type==ID_LA) {
		la= (Lamp *)from;
		if(la->id.lib) return 0;
		ipo= la->ipo;
		
		if(make && ipo==0) ipo= la->ipo= add_ipo("LaIpo", ID_LA);
	}
	else if( type==ID_CA) {
		ca= (Camera *)from;
		if(ca->id.lib) return 0;
		ipo= ca->ipo;
		
		if(make && ipo==0) ipo= ca->ipo= add_ipo("CaIpo", ID_CA);
	}
	else if( type==ID_SO) {
		bSound *snd= (bSound *)from;
		if(snd->id.lib) return 0;
		ipo= snd->ipo;
		
		if(make && ipo==0) ipo= snd->ipo= add_ipo("SndIpo", ID_SO);
	}
	else return 0;
	
	return ipo;	
}


// this function should not have the G.sipo in it...

IpoCurve *get_ipocurve(ID *from, short type, int adrcode, Ipo *useipo)
{
	Ipo *ipo= 0;
	IpoCurve *icu=0;
	
	/* return 0 if lib */
	/* also test if ipo and ipocurve exist */

	if (useipo==NULL) {

		if (G.sipo==NULL || G.sipo->pin==0){
			ipo= get_ipo(from, type, 1);	/* 1= make */
		}
		else
			ipo = G.sipo->ipo;

		
		if(G.sipo) {
			if (G.sipo->pin==0) G.sipo->ipo= ipo;
		}
	}
	else
		ipo= useipo;

		
	if(ipo && ipo->id.lib==0) {
	
		icu= ipo->curve.first;
		while(icu) {
			if(icu->adrcode==adrcode) break;
			icu= icu->next;
		}
		if(icu==0) {
			icu= MEM_callocN(sizeof(IpoCurve), "ipocurve");
			
			icu->flag |= IPO_VISIBLE;

			if (!useipo && G.sipo && G.sipo->pin)
				icu->blocktype = G.sipo->blocktype;
			else
				icu->blocktype= type;
			icu->adrcode= adrcode;
			
			set_icu_vars(icu);
			
			BLI_addtail( &(ipo->curve), icu);
		}
	}
	return icu;
}

void insert_vert_ipo(IpoCurve *icu, float x, float y)
{
	BezTriple *bezt, beztr, *newbezt;
	int a = 0, h1, h2;
	
	memset(&beztr, 0, sizeof(BezTriple));
	beztr.vec[1][0]= x;
	beztr.vec[1][1]= y;
	beztr.hide= IPO_BEZ;
	beztr.f1= beztr.f2= beztr.f3= SELECT;
	beztr.h1= beztr.h2= HD_AUTO;
		
	bezt= icu->bezt;
		
	if(bezt==0) {
		icu->bezt= MEM_callocN( sizeof(BezTriple), "beztriple");
		*(icu->bezt)= beztr;
		icu->totvert= 1;
	}
	else {
		/* all vertices deselect */
		for(a=0; a<icu->totvert; a++, bezt++) {
			bezt->f1= bezt->f2= bezt->f3= 0;
		}
	
		bezt= icu->bezt;
		for(a=0; a<=icu->totvert; a++, bezt++) {
			
			/* no double points */
			if(a<icu->totvert && (bezt->vec[1][0]>x-IPOTHRESH && bezt->vec[1][0]<x+IPOTHRESH)) {
				*(bezt)= beztr;
				break;
			}
			if(a==icu->totvert || bezt->vec[1][0] > x) {
				newbezt= MEM_callocN( (icu->totvert+1)*sizeof(BezTriple), "beztriple");
				
				if(a>0) memcpy(newbezt, icu->bezt, a*sizeof(BezTriple));
				
				bezt= newbezt+a;
				*(bezt)= beztr;
				
				if(a<icu->totvert) memcpy(newbezt+a+1, icu->bezt+a, (icu->totvert-a)*sizeof(BezTriple));
				
				MEM_freeN(icu->bezt);
				icu->bezt= newbezt;
				
				icu->totvert++;
				break;
			}
		}
	}
	
	
	calchandles_ipocurve(icu);
	
	/* set handletype */
	if(icu->totvert>2) {
		h1= h2= HD_AUTO;
		if(a>0) h1= (bezt-1)->h2;
		if(a<icu->totvert-1) h2= (bezt+1)->h1;
		bezt->h1= h1;
		bezt->h2= h2;

		calchandles_ipocurve(icu);
	}
}

void add_vert_ipo()
{
	EditIpo *ei;
	float x, y;
	int val;
	short mval[2];

	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;
	if(G.sipo->showkey) {
		G.sipo->showkey= 0;
		free_ipokey(&G.sipo->ipokey);
	}
	
	getmouseco_areawin(mval);
	
	if(mval[0]>G.v2d->mask.xmax) return;
	
	ei= get_editipo();
	if(ei==0) {
		error("Too many EditIpos");
		return;
	}
	
	areamouseco_to_ipoco(G.v2d, mval, &x, &y);
	
	if(ei->icu==0) {
		if(G.sipo->from)
			ei->icu= get_ipocurve(G.sipo->from, G.sipo->blocktype, ei->adrcode, 0);
	}
	if(ei->icu==0) return;

	if(ei->disptype==IPO_DISPBITS) {
		ei->icu->vartype= IPO_BITS;
		val= (int)floor(y-0.5);
		if(val<0) val= 0;
		y= (float)(1 << val);
	}
	
	insert_vert_ipo(ei->icu, x, y);

	/* to be sure: if icu was 0, or only 1 curve visible */
	ei->flag |= IPO_SELECT;
	ei->icu->flag= ei->flag;

	editipo_changed(G.sipo, 1);
}

void add_duplicate_editipo()
{
	Object *ob;
	EditIpo *ei;
	IpoCurve *icu;
	BezTriple *bezt, *beztn, *newb;
	int tot, a, b;
	
	get_status_editipo();
	if(totipo_vertsel==0) return;
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN3(ei, flag & IPO_VISIBLE, icu, icu->bezt) {
			if(G.sipo->showkey || (ei->flag & IPO_EDIT)) {
				icu= ei->icu;
				
				/* how many points */
				tot= 0;
				b= icu->totvert;
				bezt= icu->bezt;
				while(b--) {
					if(bezt->f2 & 1) tot++;
					bezt++;
				}
				
				if(tot) {
					icu->totvert+= tot;
					newb= beztn= MEM_mallocN(icu->totvert*sizeof(BezTriple), "bezt");
					bezt= icu->bezt;
					b= icu->totvert-tot;
					while(b--) {
						*beztn= *bezt;
						if(bezt->f2 & 1) {
							beztn->f1= beztn->f2= beztn->f3= 0;
							beztn++;
							*beztn= *bezt;
						}
						beztn++;
						bezt++;
					}
					MEM_freeN(icu->bezt);
					icu->bezt= newb;
					
					calchandles_ipocurve(icu);
				}
			}
		}
	}
	
	if(G.sipo->showkey) {
		make_ipokey();
		if(G.sipo->blocktype==ID_OB) {
			ob= OBACT;
			if(ob && (ob->ipoflag & OB_DRAWKEY)) allqueue(REDRAWVIEW3D, 0);
		}
	}
	transform_ipo('g');
}

void remove_doubles_ipo()
{
	EditIpo *ei;
	IpoKey *ik, *ikn;
	BezTriple *bezt, *newb, *new1;
	float val;
	int mode, a, b;
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN3(ei, flag & IPO_VISIBLE, icu, icu->bezt) {
			
			/* OR the curve is selected OR in editmode OR in keymode */
			mode= 0;
			if(G.sipo->showkey || (ei->flag & IPO_EDIT)) mode= 1;
			else if(ei->flag & IPO_SELECT) mode= 2;
			
			if(mode) {
				bezt= ei->icu->bezt;
				newb= new1= MEM_mallocN(ei->icu->totvert*sizeof(BezTriple), "newbezt");
				*newb= *bezt;
				b= ei->icu->totvert-1;
				bezt++;
				while(b--) {
					
					/* can we remove? */
					if(mode==2 || (bezt->f2 & 1)) {
					
						/* are the points different? */
						if( fabs( bezt->vec[1][0]-newb->vec[1][0] ) > 0.9 ) {
							newb++;
							*newb= *bezt;
						}
						else {
							/* median */
							VecMidf(newb->vec[0], newb->vec[0], bezt->vec[0]);
							VecMidf(newb->vec[1], newb->vec[1], bezt->vec[1]);
							VecMidf(newb->vec[2], newb->vec[2], bezt->vec[2]);
							
							newb->h1= newb->h2= HD_FREE;
							
							ei->icu->totvert--;
						}
						
					}
					else {
						newb++;
						*newb= *bezt;
					}
					bezt++;
				}
				
				MEM_freeN(ei->icu->bezt);
				ei->icu->bezt= new1;
				
				calchandles_ipocurve(ei->icu);				
			}
		}
	}
	
	editipo_changed(G.sipo, 1);	/* makes ipokeys again! */

	/* remove double keys */
	if(G.sipo->showkey) {
		ik= G.sipo->ipokey.first;
		ikn= ik->next;
		
		while(ik && ikn) {
			if( (ik->flag & 1) && (ikn->flag & 1) ) {
				if( fabs(ik->val-ikn->val) < 0.9 ) {
					val= (float)((ik->val + ikn->val)/2.0);
					
					for(a=0; a<G.sipo->totipo; a++) {
						if(ik->data[a]) ik->data[a]->vec[1][0]= val;
						if(ikn->data[a]) ikn->data[a]->vec[1][0]= val;						
					}
				}
			}
			ik= ikn;
			ikn= ikn->next;

		}
		
		editipo_changed(G.sipo, 1);	/* makes ipokeys agian! */

	}
	deselectall_editipo();
}

void join_ipo_menu(void)
{
	int mode = 0;
	mode= pupmenu("Join %t|All Selected %x1|Selected Doubles %x2");
	
	if (mode == -1) return;
	
	join_ipo(mode);
}

void join_ipo(int mode)
{
	EditIpo *ei;
	IpoKey *ik;
	IpoCurve *icu;
	BezTriple *bezt, *beztn, *newb;
	float val;
	int tot, a, b;
	
	get_status_editipo();
	
	/* Mode events:
	 * All Selected: 1
	 * Selected Doubles: 2
	 */
	
	if( mode==2 ) {
		remove_doubles_ipo();
		return;
	}
	
	/* first: multiple selected verts in 1 curve */
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN3(ei, flag & IPO_VISIBLE, icu, icu->bezt) {
			if(G.sipo->showkey || (ei->flag & IPO_EDIT)) {
				icu= ei->icu;
				
				/* how many points */
				tot= 0;
				b= icu->totvert;
				bezt= icu->bezt;
				while(b--) {
					if(bezt->f2 & 1) tot++;
					bezt++;
				}
				
				if(tot>1) {
					tot--;
					icu->totvert-= tot;
					
					newb= MEM_mallocN(icu->totvert*sizeof(BezTriple), "bezt");
					/* the first point is the new one */
					beztn= newb+1;
					tot= 0;
					
					bezt= icu->bezt;
					b= icu->totvert+tot+1;
					while(b--) {
						
						if(bezt->f2 & 1) {
							if(tot==0) *newb= *bezt;
							else {
								VecAddf(newb->vec[0], newb->vec[0], bezt->vec[0]);
								VecAddf(newb->vec[1], newb->vec[1], bezt->vec[1]);
								VecAddf(newb->vec[2], newb->vec[2], bezt->vec[2]);
							}
							tot++;
						}
						else {
							*beztn= *bezt;
							beztn++;
						}
						bezt++;
					}
					
					VecMulf(newb->vec[0], (float)(1.0/((float)tot)));
					VecMulf(newb->vec[1], (float)(1.0/((float)tot)));
					VecMulf(newb->vec[2], (float)(1.0/((float)tot)));
					
					MEM_freeN(icu->bezt);
					icu->bezt= newb;
					
					sort_time_ipocurve(icu);
					calchandles_ipocurve(icu);
				}
			}
		}
	}
	
	/* next: in keymode: join multiple selected keys */
	
	editipo_changed(G.sipo, 1);	/* makes ipokeys again! */
	
	if(G.sipo->showkey) {
		ik= G.sipo->ipokey.first;
		val= 0.0;
		tot= 0;
		while(ik) {
			if(ik->flag & 1) {
				for(a=0; a<G.sipo->totipo; a++) {
					if(ik->data[a]) {
						val+= ik->data[a]->vec[1][0];
						break;
					}
				}
				tot++;
			}
			ik= ik->next;
		}
		if(tot>1) {
			val/= (float)tot;
			
			ik= G.sipo->ipokey.first;
			while(ik) {
				if(ik->flag & 1) {
					for(a=0; a<G.sipo->totipo; a++) {
						if(ik->data[a]) {
							ik->data[a]->vec[1][0]= val;
						}
					}
				}
				ik= ik->next;
			}
			editipo_changed(G.sipo, 0);
		}
	}
	deselectall_editipo();
}

void ipo_snap_menu(void)
{
	short event;
	
	event= pupmenu("Snap %t|Horizontal %x1|To Next %x2|To Frame %x3|To Current Frame%x4");
	if(event < 1) return;

	ipo_snap(event);
}

void ipo_snap(short event)
{
	EditIpo *ei;
	BezTriple *bezt;
	float dx = 0.0;
	int a, b;
	short ok, ok2;
	
	/* events:
	 * Horizontal : 1
	 * To Next: 2
	 * To Frame: 3
	 * To Current Frame: 4
	 */
	 
	get_status_editipo();

	ei= G.sipo->editipo;
	for(b=0; b<G.sipo->totipo; b++, ei++) {
		if ISPOIN3(ei, flag & IPO_VISIBLE, icu, icu->bezt) {
		
			ok2= 0;
			if(G.sipo->showkey) ok2= 1;
			else if(totipo_vert && (ei->flag & IPO_EDIT)) ok2= 2;
			else if(totipo_vert==0 && (ei->flag & IPO_SELECT)) ok2= 3;
			
			if(ok2) {
				bezt= ei->icu->bezt;
				a= ei->icu->totvert;
				while(a--) {
					ok= 0;
					if(totipo_vert) {
						 if(bezt->f2 & 1) ok= 1;
					}
					else ok= 1;
					
					if(ok) {
						if(event==1) {
							bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1];
							if(bezt->h1==HD_AUTO || bezt->h1==HD_VECT) bezt->h1= HD_ALIGN;
							if(bezt->h2==HD_AUTO || bezt->h2==HD_VECT) bezt->h2= HD_ALIGN;
						}
						else if(event==2) {
							if(a) {
								bezt->vec[0][1]= bezt->vec[1][1]= bezt->vec[2][1]= (bezt+1)->vec[1][1];
								if(bezt->h1==HD_AUTO || bezt->h1==HD_VECT) bezt->h1= HD_ALIGN;
								if(bezt->h2==HD_AUTO || bezt->h2==HD_VECT) bezt->h2= HD_ALIGN;
							}
						}
						else if(event==3) {
							bezt->vec[1][0]= (float)(floor(bezt->vec[1][0]+0.5));
						}
						else if(event==4) {	/* to current frame */
							
							if(ok2==1 || ok2==2) {
								
								if(G.sipo->blocktype==ID_SEQ) {
									Sequence *seq;
							
									seq= (Sequence *)G.sipo->from;
									if(seq) {
										dx= (float)(CFRA-seq->startdisp);
										dx= (float)(100.0*dx/((float)(seq->enddisp-seq->startdisp)));
										
										dx-= bezt->vec[1][0];
									}
								}
								else dx= G.scene->r.framelen*CFRA - bezt->vec[1][0];
								
								bezt->vec[0][0]+= dx;
								bezt->vec[1][0]+= dx;
								bezt->vec[2][0]+= dx;
							}
						}
					}
					
					bezt++;
				}
				calchandles_ipocurve(ei->icu);
			}
		}
	}
	editipo_changed(G.sipo, 1);
}



void mouse_select_ipo()
{
	Object *ob;
	EditIpo *ei, *actei= 0;
	IpoCurve *icu;
	IpoKey *ik, *actik;
	BezTriple *bezt;
	Key *key;
	KeyBlock *kb, *actkb=0;
	float x, y, dist, mindist;
	int a, oldflag = 0, hand, ok;
	short mval[2], xo, yo;
	
	if(G.sipo->editipo==0) return;
	
	get_status_editipo();
	
	if(G.sipo->showkey) {
		getmouseco_areawin(mval);
	
		areamouseco_to_ipoco(G.v2d, mval, &x, &y);
		actik= 0;
		mindist= 1000.0;
		ik= G.sipo->ipokey.first;
		while(ik) {
			dist= (float)(fabs(ik->val-x));
			if(ik->flag & 1) dist+= 1.0;
			if(dist < mindist) {
				actik= ik;
				mindist= dist;
			}
			ik= ik->next;
		}
		if(actik) {
			oldflag= actik->flag;
			
			if(G.qual & LR_SHIFTKEY);
			else deselectall_editipo();
			
			if(G.qual & LR_SHIFTKEY) {
				if(oldflag & 1) actik->flag &= ~1;
				else actik->flag |= 1;
			}
			else {
				actik->flag |= 1;
			}
		}
	}
	else if(totipo_edit) {
	
		hand= findnearest_ipovert(&icu, &bezt);

		if(G.qual & LR_SHIFTKEY) {
			if(bezt) {
				if(hand==1) {
					if(BEZSELECTED(bezt)) {
						bezt->f1= bezt->f2= bezt->f3= 0;
					}
					else {
						bezt->f1= bezt->f2= bezt->f3= 1;
					}
				}
				else if(hand==0) {
					if(bezt->f1 & 1) bezt->f1= 0;
					else bezt->f1= 1;
				}
				else {
					if(bezt->f3 & 1) bezt->f3= 0;
					else bezt->f3= 1;
				}
			}				
		}
		else {
			deselectall_editipo();
		
			if(bezt) {
				if(hand==1) {
					bezt->f1|= 1; bezt->f2|= 1; bezt->f3|= 1;
				}
				else if(hand==0) bezt->f1|= 1;
				else bezt->f3|= 1;
			}
		}
	}
	else {
		
		/* vertex keys ? */
		
		if(G.sipo->blocktype==ID_KE && G.sipo->from) {
			key= (Key *)G.sipo->from;
			
			ei= G.sipo->editipo;
			if(key->type==KEY_NORMAL || (ei->flag & IPO_VISIBLE)) {
				getmouseco_areawin(mval);
				
				areamouseco_to_ipoco(G.v2d, mval, &x, &y);
				/* how much is 20 pixels? */
				mindist= (float)(20.0*(G.v2d->cur.ymax-G.v2d->cur.ymin)/(float)curarea->winy);
				
				kb= key->block.first;
				while(kb) {
					dist= (float)(fabs(kb->pos-y));
					if(kb->flag & SELECT) dist+= (float)0.01;
					if(dist < mindist) {
						actkb= kb;
						mindist= dist;
					}
					kb= kb->next;
				}
				if(actkb) {
					ok= TRUE;
					if(G.obedit && (actkb->flag & 1)==0) {
						ok= okee("Copy key after leaving Edit Mode");
					}
					if(ok) {
						/* also does all keypos */
						deselectall_editipo();
						
						actkb->flag |= 1;
						
						/* calc keypos */
						showkeypos((Key *)G.sipo->from, actkb);
					}
				}
			}
		}
			
		/* select curve */
		if(actkb==0) {
			if(totipo_vis==1) {
				ei= G.sipo->editipo;
				for(a=0; a<G.sipo->totipo; a++, ei++) {
					if(ei->icu) {
						if(ei->flag & IPO_VISIBLE) actei= ei;
					}
				}
			}
			else if(totipo_vis>1) {
				actei= select_proj_ipo(0, 0);
			}
			
			if(actei) oldflag= actei->flag;
			
			if(G.qual & LR_SHIFTKEY);
			else deselectall_editipo();
			
			if(actei) {
				if(G.qual & LR_SHIFTKEY) {
					if(oldflag & IPO_SELECT) actei->flag &= ~IPO_SELECT;
					else actei->flag |= IPO_SELECT;
				}
				else {
					actei->flag |= IPO_SELECT;
				}
			}
		}
	}
	
	update_editipo_flags();
	
	force_draw();
	
	if(G.sipo->showkey && G.sipo->blocktype==ID_OB) {
		ob= OBACT;
		if(ob && (ob->ipoflag & OB_DRAWKEY)) allqueue(REDRAWVIEW3D, 0);
	}
	
	getmouseco_areawin(mval);
	xo= mval[0]; 
	yo= mval[1];

	while(get_mbut()&R_MOUSE) {		
		getmouseco_areawin(mval);
		if(abs(mval[0]-xo)+abs(mval[1]-yo) > 4) {
			
			if(actkb) move_keys();
			else transform_ipo('g');
			
			return;
		}
		BIF_wait_for_statechange();
	}
}

int icu_keys_bezier_loop(IpoCurve *icu,
                         int (*bezier_function)(BezTriple *),
                         void (ipocurve_function)(struct IpoCurve *icu)) 
{
    /*  This loops through the beziers in the Ipocurve, and executes 
     *  the generic user provided 'bezier_function' on each one. 
     *  Optionally executes the generic function ipocurve_function on the 
     *  IPO curve after looping (eg. calchandles_ipocurve)
     */

    int b;
    BezTriple *bezt;

    b    = icu->totvert;
    bezt = icu->bezt;

    /* if bezier_function has been specified
     * then loop through each bezier executing
     * it.
     */

    if (bezier_function != NULL) {
        while(b--) {
            /* exit with return code 1 if the bezier function 
             * returns 1 (good for when you are only interested
             * in finding the first bezier that
             * satisfies a condition).
             */
            if (bezier_function(bezt)) return 1;
            bezt++;
        }
    }

    /* if ipocurve_function has been specified 
     * then execute it
     */
    if (ipocurve_function != NULL)
        ipocurve_function(icu);

    return 0;

}

int ipo_keys_bezier_loop(Ipo *ipo,
                         int (*bezier_function)(BezTriple *),
                         void (ipocurve_function)(struct IpoCurve *icu))
{
    /*  This loops through the beziers that are attached to
     *  the selected keys on the Ipocurves of the Ipo, and executes 
     *  the generic user provided 'bezier_function' on each one. 
     *  Optionally executes the generic function ipocurve_function on a 
     *  IPO curve after looping (eg. calchandles_ipocurve)
     */

    IpoCurve *icu;

    /* Loop through each curve in the Ipo
     */
    for (icu=ipo->curve.first; icu; icu=icu->next){
        if (icu_keys_bezier_loop(icu,bezier_function, ipocurve_function))
            return 1;
    }

    return 0;
}

int selected_bezier_loop(int (*looptest)(EditIpo *),
                         int (*bezier_function)(BezTriple *),
                         void (ipocurve_function)(struct IpoCurve *icu))
{
	/*  This loops through the beziers that are attached to
	 *  selected keys in editmode in the IPO window, and executes 
	 *  the generic user-provided 'bezier_function' on each one 
	 *  that satisfies the 'looptest' function. Optionally executes
	 *  the generic function ipocurve_function on a IPO curve
	 *  after looping (eg. calchandles_ipocurve)
	 */

	EditIpo *ei;
	BezTriple *bezt;
	int a, b;

	/* Get the first Edit Ipo from the selected Ipos
	 */
	ei= G.sipo->editipo;

	/* Loop throught all of the selected Ipo's
	 */
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		/* Do a user provided test on the Edit Ipo
		 * to determine whether we want to process it
		 */
		if (looptest(ei)) {
			/* Loop through the selected
			 * beziers on the Edit Ipo
			 */
			bezt = ei->icu->bezt;
			b    = ei->icu->totvert;
			
			/* if bezier_function has been specified
			 * then loop through each bezier executing
			 * it.
			 */
			if (bezier_function != NULL) {
				while(b--) {
					/* exit with return code 1 if the bezier function 
					 * returns 1 (good for when you are only interested
					 * in finding the first bezier that
					 * satisfies a condition).
					 */
					if (bezier_function(bezt)) return 1;
					bezt++;
				}
			}

			/* if ipocurve_function has been specified 
			 * then execute it
			 */
			if (ipocurve_function != NULL)
				ipocurve_function(ei->icu);
		}
		/* nufte flourdje zim ploopydu <-- random dutch looking comment ;) */
		/* looks more like russian to me! (ton) */
	}

	return 0;
}

int select_bezier_add(BezTriple *bezt) {
  /* Select the bezier triple */
  bezt->f1 |= 1;
  bezt->f2 |= 1;
  bezt->f3 |= 1;
  return 0;
}

int select_bezier_subtract(BezTriple *bezt) {
  /* Deselect the bezier triple */
  bezt->f1 &= ~1;
  bezt->f2 &= ~1;
  bezt->f3 &= ~1;
  return 0;
}

int select_bezier_invert(BezTriple *bezt) {
  /* Invert the selection for the bezier triple */
  bezt->f2 ^= 1;
  if ( bezt->f2 & 1 ) {
    bezt->f1 |= 1;
    bezt->f3 |= 1;
  }
  else {
    bezt->f1 &= ~1;
    bezt->f3 &= ~1;
  }
  return 0;
}

int set_bezier_auto(BezTriple *bezt) 
{
	/* Sets the selected bezier handles to type 'auto' 
	 */

	/* is a handle selected? If so
	 * set it to type auto
	 */
	if(bezt->f1 || bezt->f3) {
		if(bezt->f1) bezt->h1= 1; /* the secret code for auto */
		if(bezt->f3) bezt->h2= 1;

		/* if the handles are not of the same type, set them
		 * to type free
		 */
		if(bezt->h1!=bezt->h2) {
			if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE;
			if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE;
		}
	}
	return 0;
}

int set_bezier_vector(BezTriple *bezt) 
{
	/* Sets the selected bezier handles to type 'vector' 
	 */

	/* is a handle selected? If so
	 * set it to type vector
	 */
	if(bezt->f1 || bezt->f3) {
		if(bezt->f1) bezt->h1= 2; /* the code for vector */
		if(bezt->f3) bezt->h2= 2;
    
		/* if the handles are not of the same type, set them
		 * to type free
		 */
		if(bezt->h1!=bezt->h2) {
			if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE;
			if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE;
		}
	}
	return 0;
}

int bezier_isfree(BezTriple *bezt) 
{
	/* queries whether the handle should be set
	 * to type 'free' (I think)
	 */
	if(bezt->f1 && bezt->h1) return 1;
	if(bezt->f3 && bezt->h2) return 1;
	return 0;
}

int set_bezier_free(BezTriple *bezt) 
{
	/* Sets selected bezier handles to type 'free' 
	 */
	if(bezt->f1) bezt->h1= HD_FREE;
	if(bezt->f3) bezt->h2= HD_FREE;
	return 0;
}

int set_bezier_align(BezTriple *bezt) 
{
	/* Sets selected bezier handles to type 'align' 
	 */
	if(bezt->f1) bezt->h1= HD_ALIGN;
	if(bezt->f3) bezt->h2= HD_ALIGN;
	return 0;
}

int vis_edit_icu_bez(EditIpo *ei) {
	/* A 4 part test for an EditIpo :
	 *   is it a) visible
	 *         b) in edit mode
	 *         c) does it contain an Ipo Curve
	 *         d) does that ipo curve have a bezier
	 *
	 * (The reason why I don't just use the macro
	 * is I need a pointer to a function.)
	 */
	return ISPOIN4(ei, flag & IPO_VISIBLE, flag & IPO_EDIT, icu, icu->bezt);
}

void select_ipo_bezier_keys(Ipo *ipo, int selectmode)
{
  /* Select all of the beziers in all
   * of the Ipo curves belonging to the
   * Ipo, using the selection mode.
   */
  switch (selectmode) {
  case SELECT_ADD:
    ipo_keys_bezier_loop(ipo, select_bezier_add, NULL);
    break;
  case SELECT_SUBTRACT:
    ipo_keys_bezier_loop(ipo, select_bezier_subtract, NULL);
    break;
  case SELECT_INVERT:
    ipo_keys_bezier_loop(ipo, select_bezier_invert, NULL);
    break;
  }
}

void sethandles_ipo_keys(Ipo *ipo, int code)
{
	/* this function lets you set bezier handles all to
	 * one type for some Ipo's (e.g. with hotkeys through
	 * the action window).
	 */ 

	/* code==1: set autohandle */
	/* code==2: set vectorhandle */
	/* als code==3 (HD_ALIGN) toggelt het, vectorhandles worden HD_FREE */
	
	switch(code) {
	case 1:
		/*** Set to auto ***/
		ipo_keys_bezier_loop(ipo, set_bezier_auto,
							 calchandles_ipocurve);
		break;
	case 2:
		/*** Set to vector ***/
		ipo_keys_bezier_loop(ipo, set_bezier_vector,
                         calchandles_ipocurve);
		break;
	default:
		if ( ipo_keys_bezier_loop(ipo, bezier_isfree, NULL) ) {
			/*** Set to free ***/
			ipo_keys_bezier_loop(ipo, set_bezier_free,
                           calchandles_ipocurve);
		}
		else {
			/*** Set to align ***/
			ipo_keys_bezier_loop(ipo, set_bezier_align,
                           calchandles_ipocurve);
		}
		break;
	}


}

void sethandles_ipo(int code)
{
	/* this function lets you set bezier handles all to
	 * one type for some selected keys in edit mode in the
	 * IPO window (e.g. with hotkeys)
	 */ 

	/* code==1: set autohandle */
	/* code==2: set vectorhandle */
	/* als code==3 (HD_ALIGN) toggelt het, vectorhandles worden HD_FREE */

	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;

	switch(code) {
	case 1:
		/*** Set to auto ***/
		selected_bezier_loop(vis_edit_icu_bez, set_bezier_auto,
                         calchandles_ipocurve);
		break;
	case 2:
		/*** Set to vector ***/
		selected_bezier_loop(vis_edit_icu_bez, set_bezier_vector,
                         calchandles_ipocurve);
		break;
	default:
		if (selected_bezier_loop(vis_edit_icu_bez, bezier_isfree, NULL) ) {
			/*** Set to free ***/
			selected_bezier_loop(vis_edit_icu_bez, set_bezier_free,
								 calchandles_ipocurve);
		}
		else {
			/*** Set to align ***/
			selected_bezier_loop(vis_edit_icu_bez, set_bezier_align,
								 calchandles_ipocurve);
		}
		break;
	}

	editipo_changed(G.sipo, 1);
}


void set_ipocurve_constant(struct IpoCurve *icu) {
	/* Sets the type of the IPO curve to constant
	 */
	icu->ipo= IPO_CONST;
}

void set_ipocurve_linear(struct IpoCurve *icu) {
	/* Sets the type of the IPO curve to linear
	 */
	icu->ipo= IPO_LIN;
}

void set_ipocurve_bezier(struct IpoCurve *icu) {
	/* Sets the type of the IPO curve to bezier
	 */
	icu->ipo= IPO_BEZ;
}


void setipotype_ipo(Ipo *ipo, int code)
{
	/* Sets the type of the each ipo curve in the
	 * Ipo to a value based on the code
	 */
	switch (code) {
	case 1:
		ipo_keys_bezier_loop(ipo, NULL, set_ipocurve_constant);
		break;
	case 2:
		ipo_keys_bezier_loop(ipo, NULL, set_ipocurve_linear);
		break;
	case 3:
		ipo_keys_bezier_loop(ipo, NULL, set_ipocurve_bezier);
		break;
	}
}

void set_ipotype()
{
	EditIpo *ei;
	Key *key;
	KeyBlock *kb;
	int a;
	short event;

	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;
	if(G.sipo->showkey) return;
	get_status_editipo();
	
	if(G.sipo->blocktype==ID_KE && totipo_edit==0 && totipo_sel==0) {
		key= (Key *)G.sipo->from;
		if(key==0) return;
		
		event= pupmenu("Key Type %t|Linear %x1|Cardinal %x2|B Spline %x3");
		if(event < 1) return;
		
		kb= key->block.first;
		while(kb) {
			if(kb->flag & SELECT) {
				kb->type= 0;
				if(event==1) kb->type= KEY_LINEAR;
				if(event==2) kb->type= KEY_CARDINAL;
				if(event==3) kb->type= KEY_BSPLINE;
			}
			kb= kb->next;
		}
	}
	else {
		event= pupmenu("Ipo Type %t|Constant %x1|Linear %x2|Bezier %x3");
		if(event < 1) return;
		
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_SELECT, icu) {
				if(event==1) ei->icu->ipo= IPO_CONST;
				else if(event==2) ei->icu->ipo= IPO_LIN;
				else ei->icu->ipo= IPO_BEZ;
			}
		}
	}
	scrarea_queue_winredraw(curarea);
}

void borderselect_ipo()
{
	EditIpo *ei;
	IpoKey *ik;
	BezTriple *bezt;
	rcti rect;
	rctf rectf;
	int a, b, val;
	short mval[2];

	get_status_editipo();
	
	val= get_border(&rect, 3);

	if(val) {
		mval[0]= rect.xmin;
		mval[1]= rect.ymin;
		areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
		mval[0]= rect.xmax;
		mval[1]= rect.ymax;
		areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);

		if(G.sipo->showkey) {
			ik= G.sipo->ipokey.first;
			while(ik) {
				if(rectf.xmin<ik->val && rectf.xmax>ik->val) {
					if(val==LEFTMOUSE) ik->flag |= 1;
					else ik->flag &= ~1;
				}
				ik= ik->next;
			}
			update_editipo_flags();
		}
		else if(totipo_edit==0) {
			if(rect.xmin<rect.xmax && rect.ymin<rect.ymax)
				select_proj_ipo(&rectf, val);
		}
		else {
			
			ei= G.sipo->editipo;
			for(a=0; a<G.sipo->totipo; a++, ei++) {
				if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_EDIT, icu) {
					if(ei->icu->bezt) {
						b= ei->icu->totvert;
						bezt= ei->icu->bezt;
						while(b--) {
							int bit= (val==LEFTMOUSE);
							
							if(BLI_in_rctf(&rectf, bezt->vec[0][0], bezt->vec[0][1]))
								bezt->f1 = (bezt->f1&~1) | bit;
							if(BLI_in_rctf(&rectf, bezt->vec[1][0], bezt->vec[1][1]))
								bezt->f2 = (bezt->f2&~1) | bit;
							if(BLI_in_rctf(&rectf, bezt->vec[2][0], bezt->vec[2][1]))
								bezt->f3 = (bezt->f3&~1) | bit;

							bezt++;
						}
					}
				}
			}
		}
		scrarea_queue_winredraw(curarea);
	}
}




void del_ipo()
{
	EditIpo *ei;
	BezTriple *bezt, *bezt1;
	int a, b;
	int del, event;

	get_status_editipo();
	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;
	
	if(totipo_edit==0 && totipo_sel==0 && totipo_vertsel==0) {
		delete_key();
		return;
	}
	
	if( okee("Erase selected")==0 ) return;

	// eerste doorloop, kunnen hele stukken weg? 
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
	
		del= 0;
		
		if(G.sipo->showkey==0 && totipo_edit==0) {
			if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_SELECT, icu) {
				del= 1;
			}
		}
		else {
			if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
				if(G.sipo->showkey || (ei->flag & IPO_EDIT)) {
					if(ei->icu->bezt) {
						bezt= ei->icu->bezt;
						b= ei->icu->totvert;
						if(b) {
							while(b) {
								if( BEZSELECTED(bezt) );
								else break;
								b--;
								bezt++;
							}
							if(b==0) del= 1;
						}
					}
				}
			}
		}
		
		if(del) {
			BLI_remlink( &(G.sipo->ipo->curve), ei->icu);
			if(ei->icu->bezt) MEM_freeN(ei->icu->bezt);
			MEM_freeN(ei->icu);
			ei->flag &= ~IPO_SELECT;
			ei->flag &= ~IPO_EDIT;
			ei->icu= 0;	
		}
	}
	
	// tweede doorloop, kleine stukken weg: alleen curves 
	ei= G.sipo->editipo;
	for(b=0; b<G.sipo->totipo; b++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			if(G.sipo->showkey || (ei->flag & IPO_EDIT)) {
			
				event= 0;
				if(ei->icu->bezt) {
				
					bezt= ei->icu->bezt;
					for(a=0; a<ei->icu->totvert; a++) {
						if( BEZSELECTED(bezt) ) {
							memcpy(bezt, bezt+1, (ei->icu->totvert-a-1)*sizeof(BezTriple));
							ei->icu->totvert--;
							a--;
							event= 1;
						}
						else bezt++;
					}
					if(event) {
						bezt1 = (BezTriple*) MEM_mallocN(ei->icu->totvert * sizeof(BezTriple), "delNurb");
						memcpy(bezt1, ei->icu->bezt, (ei->icu->totvert)*sizeof(BezTriple) );
						MEM_freeN(ei->icu->bezt);
						ei->icu->bezt= bezt1;
					}
				}
			}
		}
	}
	
	allqueue(REDRAWNLA, 0);
	allqueue (REDRAWACTION, 0);
	allqueue(REDRAWIPO, 0);
	allspace(REMAKEIPO, 0);
}

ListBase ipocopybuf={0, 0};
int totipocopybuf=0;

void free_ipocopybuf()
{
	IpoCurve *icu;
	
	while( (icu= ipocopybuf.first) ) {
		if(icu->bezt) MEM_freeN(icu->bezt);
		BLI_remlink(&ipocopybuf, icu);
		MEM_freeN(icu);
	}
	totipocopybuf= 0;
}

void copy_editipo()
{
	EditIpo *ei;
	IpoCurve *icu;
	int a;
	
	if(G.sipo->showkey) {
		error("cannot copy\n");
		return;
	}
	
	free_ipocopybuf();
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			if( (ei->flag & IPO_EDIT) || (ei->flag & IPO_SELECT) ) {
				icu= MEM_callocN(sizeof(IpoCurve), "ipocopybuf");
				*icu= *(ei->icu);
				BLI_addtail(&ipocopybuf, icu);
				if(icu->bezt) {
					icu->bezt= MEM_mallocN(icu->totvert*sizeof(BezTriple), "ipocopybuf");
					memcpy(icu->bezt, ei->icu->bezt, icu->totvert*sizeof(BezTriple));
				}
				totipocopybuf++;
			}
		}
	}
	
	if(totipocopybuf==0) error("Copy buffer is empty");
}

void paste_editipo()
{
	EditIpo *ei;
	IpoCurve *icu;
	int a, ok;
	
	if(G.sipo->showkey) return;
	
	if(totipocopybuf==0) return;
	if(G.sipo->ipo==0) return;
	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;

	get_status_editipo();
	
	if(totipo_vis==0) {
		error("No visible splines");
	}
	else if(totipo_vis!=totipocopybuf && totipo_sel!=totipocopybuf) {
		error("Incompatible paste");
	}
	else {
		/* prevent problems: splines visible that are not selected */
		if(totipo_vis==totipo_sel) totipo_vis= 0;
		
		icu= ipocopybuf.first;
		if(icu==0) return;

		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			if(ei->flag & IPO_VISIBLE) {
				ok= 0;
				if(totipo_vis==totipocopybuf) ok= 1;
				if(totipo_sel==totipocopybuf && (ei->flag & IPO_SELECT)) ok= 1;
	
				if(ok) {
			
					ei->icu= get_ipocurve(G.sipo->from, G.sipo->blocktype, ei->adrcode, 0);
					if(ei->icu==0) return;
					
					if(ei->icu->bezt) MEM_freeN(ei->icu->bezt);
					ei->icu->bezt= 0;
					
					ei->icu->totvert= icu->totvert;
					ei->icu->flag= ei->flag= icu->flag;
					ei->icu->extrap= icu->extrap;
					ei->icu->ipo= icu->ipo;
					
					if(icu->bezt) {
						ei->icu->bezt= MEM_mallocN(icu->totvert*sizeof(BezTriple), "ipocopybuf");
						memcpy(ei->icu->bezt, icu->bezt, icu->totvert*sizeof(BezTriple));
					}
					
					icu= icu->next;
					
				}
			}
		}
		editipo_changed(G.sipo, 1);
	}
}

void set_exprap_ipo(int mode)
{
	EditIpo *ei;
	int a;
		
	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;
	/* in case of keys: always ok */

	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			if( (ei->flag & IPO_EDIT) || (ei->flag & IPO_SELECT) || (G.sipo->showkey) ) {
				ei->icu->extrap= mode;
			}
		}
	}
	editipo_changed(G.sipo, 1);
}

int find_other_handles(EditIpo *eicur, float ctime, BezTriple **beztar)
{
	EditIpo *ei;
	BezTriple *bezt;
	int a, b, c= 1, totvert;
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if(ei!=eicur && ei->icu && (ei->flag & IPO_VISIBLE)) {
			
			bezt= ei->icu->bezt;
			totvert= ei->icu->totvert;
			
			for(b=0; b<totvert; b++, bezt++) {
				if( bezt->vec[1][0] < ctime+IPOTHRESH &&  bezt->vec[1][0] > ctime-IPOTHRESH) {
					if(c>2) return 0;
					beztar[c]= bezt;
					c++;
				}
			}
		}
	}
	
	if(c==3) return 1;
	return 0;
}

void set_speed_editipo(float speed)
{
	EditIpo *ei;
	BezTriple *bezt, *beztar[3];
	float vec1[3], vec2[3];
	int a, b, totvert, didit=0;
		
	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;

	/* starting with 1 visible curve, selected point, associated points: do lencorr! */

	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			bezt= ei->icu->bezt;
			totvert= ei->icu->totvert;
			
			for(b=0; b<totvert; b++, bezt++) {
				if(BEZSELECTED(bezt)) {
					
					beztar[0]= bezt;
					
					if( find_other_handles(ei, bezt->vec[1][0], beztar) ) {
						beztar[0]->h1= beztar[0]->h2= HD_ALIGN;
						beztar[1]->h1= beztar[1]->h2= HD_ALIGN;
						beztar[2]->h1= beztar[2]->h2= HD_ALIGN;
						
						vec1[0]= (beztar[0]->vec[1][1] - beztar[0]->vec[0][1]) / (beztar[0]->vec[1][0] - beztar[0]->vec[0][0]) ;
						vec2[0]= (beztar[0]->vec[1][1] - beztar[0]->vec[2][1]) / (beztar[0]->vec[2][0] - beztar[0]->vec[1][0]) ;
						
						vec1[1]= (beztar[1]->vec[1][1] - beztar[1]->vec[0][1]) / (beztar[1]->vec[1][0] - beztar[1]->vec[0][0]) ;
						vec2[1]= (beztar[1]->vec[1][1] - beztar[1]->vec[2][1]) / (beztar[1]->vec[2][0] - beztar[1]->vec[1][0]) ;
						
						vec1[2]= (beztar[2]->vec[1][1] - beztar[2]->vec[0][1]) / (beztar[2]->vec[1][0] - beztar[2]->vec[0][0]) ;
						vec2[2]= (beztar[2]->vec[1][1] - beztar[2]->vec[2][1]) / (beztar[2]->vec[2][0] - beztar[2]->vec[1][0]) ;
						
						Normalise(vec1);
						Normalise(vec2);
						
						VecMulf(vec1, speed);
						VecMulf(vec2, speed);
						
						beztar[0]->vec[0][1]= beztar[0]->vec[1][1] - vec1[0]*(beztar[0]->vec[1][0] - beztar[0]->vec[0][0]) ;
						beztar[0]->vec[2][1]= beztar[0]->vec[1][1] - vec2[0]*(beztar[0]->vec[2][0] - beztar[0]->vec[1][0]) ;
						
						beztar[1]->vec[0][1]= beztar[1]->vec[1][1] - vec1[1]*(beztar[1]->vec[1][0] - beztar[1]->vec[0][0]) ;
						beztar[1]->vec[2][1]= beztar[1]->vec[1][1] - vec2[1]*(beztar[1]->vec[2][0] - beztar[1]->vec[1][0]) ;
						
						beztar[2]->vec[0][1]= beztar[2]->vec[1][1] - vec1[2]*(beztar[2]->vec[1][0] - beztar[2]->vec[0][0]) ;
						beztar[2]->vec[2][1]= beztar[2]->vec[1][1] - vec2[2]*(beztar[2]->vec[2][0] - beztar[2]->vec[1][0]) ;
						
						didit= 1;
					}
					else {
						error("Only works for 3 visible curves with handles");
					}
				}
			}
			break;	
		}
	}
	
	if(didit==0) error("Did not set speed");
	
	editipo_changed(G.sipo, 1);
	allqueue(REDRAWNLA, 0);
	allqueue (REDRAWACTION, 0);
	allqueue(REDRAWIPO, 0);

}


void insertkey(ID *id, int adrcode)
{
	IpoCurve *icu;
	Ipo *ipo;
	Object *ob;
	void *poin;
	float curval, cfra;
	int type;
	
	if(id) {

		// this call here, otherwise get_ipo_curve gives it from the pinned ipo
		ipo= get_ipo(id, GS(id->name), 1);	// 1=make

		icu= get_ipocurve(id, GS(id->name), adrcode, ipo);
		
		if(icu) {
			poin= get_ipo_poin(id, icu, &type);
			if(poin) {
				curval= read_ipo_poin(poin, type);
				
				cfra= frame_to_float(CFRA);
				
				if( GS(id->name)==ID_OB ) {
					ob= (Object *)id;
					if(ob->sf!=0.0 && (ob->ipoflag & OB_OFFS_OB) ) {
						/* actually frametofloat calc again! */
						cfra-= ob->sf*G.scene->r.framelen;
					}
				}
				
				insert_vert_ipo(icu, cfra, curval);
			}
		}
	}
}

void insertkey_editipo()
{
	EditIpo *ei;
	IpoKey *ik;
	ID *id;
	float *fp, cfra, *insertvals;
	int a, nr, ok, tot;
	short event;
	
	if(G.sipo->showkey)
		event= pupmenu("Insert Key Vertices %t|Current Frame %x1|Selected Keys %x2");
	else 
		event= pupmenu("Insert Key Vertices %t|Current Frame %x1");
	
	if(event<1) return;
	
	ei= G.sipo->editipo;
	for(nr=0; nr<G.sipo->totipo; nr++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
		
			ok= 0;
			if(G.sipo->showkey) ok= 1;
			else if(ei->flag & IPO_SELECT) ok= 1;

			if(ok) {
				/* count amount */
				if(event==1) tot= 1;
				else {
					ik= G.sipo->ipokey.first;
					tot= 0;
					while(ik) {
						if(ik->flag & 1) tot++;
						ik= ik->next;
					}
				}
				if(tot) {
				
					/* correction for ob timeoffs */
					cfra= frame_to_float(CFRA);
					id= G.sipo->from;	
					if(id && GS(id->name)==ID_OB ) {
						Object *ob= (Object *)id;
						if(ob->sf!=0.0 && (ob->ipoflag & OB_OFFS_OB) ) {
							cfra-= ob->sf*G.scene->r.framelen;
						}
					}
					else if(id && GS(id->name)==ID_SEQ) {
						extern Sequence *last_seq;	/* editsequence.c */
						
						if(last_seq) {
							cfra= (float)(100.0*(cfra-last_seq->startdisp)/((float)(last_seq->enddisp-last_seq->startdisp)));
						}
					}
			
					insertvals= MEM_mallocN(sizeof(float)*2*tot, "insertkey_editipo");
					/* make sure icu->curval is correct */
					calc_ipo(G.sipo->ipo, cfra);
					
					if(event==1) {
						insertvals[0]= cfra;
						
						insertvals[1]= ei->icu->curval;
					}
					else {
						fp= insertvals;
						ik= G.sipo->ipokey.first;
						while(ik) {
							if(ik->flag & 1) {
								calc_ipo(G.sipo->ipo, ik->val);

								fp[0]= ik->val;
								fp[1]= ei->icu->curval;
								fp+= 2;
							}
							ik= ik->next;
						}
					}
					fp= insertvals;
					for(a=0; a<tot; a++, fp+=2) {
						insert_vert_ipo(ei->icu, fp[0], fp[1]);
					}
					
					MEM_freeN(insertvals);
					calc_ipo(G.sipo->ipo, (float)CFRA);
				}
			}
		}
	}
	allqueue (REDRAWACTION, 0);
	allqueue(REDRAWNLA, 0);
	allqueue(REDRAWIPO, 0);
	allspace(REMAKEIPO, 0);
}


void common_insertkey()
{
	Base *base;
	Object *ob;
	Material *ma;
	ID *id;
	IpoCurve *icu;
	World *wo;
	Lamp *la;
	int tlay, map, event;
	char menustr[256];
	
	if(curarea->spacetype==SPACE_IPO) {
		insertkey_editipo();
	}
	else if(curarea->spacetype==SPACE_BUTS) {
		if(G.buts->mainb==CONTEXT_SHADING) {
			int tab= G.buts->tab[CONTEXT_SHADING];
			
			if(tab==TAB_SHADING_MAT) {
				id= G.buts->lockpoin;
				ma= G.buts->lockpoin;
				if(id) {
					event= pupmenu("Insert Key %t|RGB%x0|Alpha%x1|Halo Size%x2|Mode %x3|All Color%x10|All Mirror%x14|Ofs%x12|Size%x13|All Mapping%x11");
					if(event== -1) return;
					
					map= texchannel_to_adrcode(ma->texact);
					
					if(event==0 || event==10) {
						insertkey(id, MA_COL_R);
						insertkey(id, MA_COL_G);
						insertkey(id, MA_COL_B);
					}
					if(event==1 || event==10) {
						insertkey(id, MA_ALPHA);
					}
					if(event==2 || event==10) {
						insertkey(id, MA_HASIZE);
					}
					if(event==3 || event==10) {
						insertkey(id, MA_MODE);
					}
					if(event==10) {
						insertkey(id, MA_SPEC_R);
						insertkey(id, MA_SPEC_G);
						insertkey(id, MA_SPEC_B);
						insertkey(id, MA_REF);
						insertkey(id, MA_EMIT);
						insertkey(id, MA_AMB);
						insertkey(id, MA_SPEC);
						insertkey(id, MA_HARD);
						insertkey(id, MA_MODE);
						insertkey(id, MA_TRANSLU);
						insertkey(id, MA_ADD);
					}
					if(event==14) {
						insertkey(id, MA_RAYM);
						insertkey(id, MA_FRESMIR);
						insertkey(id, MA_FRESMIRI);
						insertkey(id, MA_FRESTRA);
						insertkey(id, MA_FRESTRAI);
					}
					if(event==12 || event==11) {
						insertkey(id, map+MAP_OFS_X);
						insertkey(id, map+MAP_OFS_Y);
						insertkey(id, map+MAP_OFS_Z);
					}
					if(event==13 || event==11) {
						insertkey(id, map+MAP_SIZE_X);
						insertkey(id, map+MAP_SIZE_Y);
						insertkey(id, map+MAP_SIZE_Z);
					}
					if(event==11) {
						insertkey(id, map+MAP_R);
						insertkey(id, map+MAP_G);
						insertkey(id, map+MAP_B);
						insertkey(id, map+MAP_DVAR);
						insertkey(id, map+MAP_COLF);
						insertkey(id, map+MAP_NORF);
						insertkey(id, map+MAP_VARF);
						insertkey(id, map+MAP_DISP);
					}
				}
			}
			else if(tab==TAB_SHADING_WORLD) {
				id= G.buts->lockpoin;
				wo= G.buts->lockpoin;
				if(id) {
					event= pupmenu("Insert Key %t|Zenith RGB%x0|Horizon RGB%x1|Mist%x2|Stars %x3|Offset%x12|Size%x13");
					if(event== -1) return;
					
					map= texchannel_to_adrcode(wo->texact);
					
					if(event==0) {
						insertkey(id, WO_ZEN_R);
						insertkey(id, WO_ZEN_G);
						insertkey(id, WO_ZEN_B);
					}
					if(event==1) {
						insertkey(id, WO_HOR_R);
						insertkey(id, WO_HOR_G);
						insertkey(id, WO_HOR_B);
					}
					if(event==2) {
						insertkey(id, WO_MISI);
						insertkey(id, WO_MISTDI);
						insertkey(id, WO_MISTSTA);
						insertkey(id, WO_MISTHI);
					}
					if(event==3) {
						insertkey(id, WO_STAR_R);
						insertkey(id, WO_STAR_G);
						insertkey(id, WO_STAR_B);
						insertkey(id, WO_STARDIST);
						insertkey(id, WO_STARSIZE);
					}
					if(event==12) {
						insertkey(id, map+MAP_OFS_X);
						insertkey(id, map+MAP_OFS_Y);
						insertkey(id, map+MAP_OFS_Z);
					}
					if(event==13) {
						insertkey(id, map+MAP_SIZE_X);
						insertkey(id, map+MAP_SIZE_Y);
						insertkey(id, map+MAP_SIZE_Z);
					}
				}
			}
			else if(tab==TAB_SHADING_LAMP) {
				id= G.buts->lockpoin;
				la= G.buts->lockpoin;
				if(id) {
					event= pupmenu("Insert Key %t|RGB%x0|Energy%x1|Spot Size%x2|Offset%x12|Size%x13");
					if(event== -1) return;
					
					map= texchannel_to_adrcode(la->texact);
					
					if(event==0) {
						insertkey(id, LA_COL_R);
						insertkey(id, LA_COL_G);
						insertkey(id, LA_COL_B);
					}
					if(event==1) {
						insertkey(id, LA_ENERGY);
					}
					if(event==2) {
						insertkey(id, LA_SPOTSI);
					}
					if(event==12) {
						insertkey(id, map+MAP_OFS_X);
						insertkey(id, map+MAP_OFS_Y);
						insertkey(id, map+MAP_OFS_Z);
					}
					if(event==13) {
						insertkey(id, map+MAP_SIZE_X);
						insertkey(id, map+MAP_SIZE_Y);
						insertkey(id, map+MAP_SIZE_Z);
					}

				}
			}
		}
		else if(G.buts->mainb==CONTEXT_OBJECT) {
			ob= OBACT;
			if(ob && ob->type==OB_MESH) {
				id= (ID *) (ob);
				if(id) {
					event= pupmenu("Insert Key %t|Surface Damping%x0|Random Damping%x1|Permeability%x2|Force Strength%x3|Force Falloff%x4");
					if(event== -1) return;

					if(event==0) {
						insertkey(id, OB_PD_SDAMP);
					}
					if(event==1) {
						insertkey(id, OB_PD_RDAMP);
					}
					if(event==2) {
						insertkey(id, OB_PD_PERM);
					}
					if(event==3) {
						insertkey(id, OB_PD_FSTR);
					}
					if(event==4) {
						insertkey(id, OB_PD_FFALL);
					}

				}
			}
		}
		else if(G.buts->mainb==CONTEXT_EDITING) {
			ob= OBACT;
			if(ob && ob->type==OB_CAMERA) {
				id= G.buts->lockpoin;
				if(id) {
					event= pupmenu("Insert Key %t|Lens%x0|Clipping%x1");
					if(event== -1) return;

					if(event==0) {
						insertkey(id, CAM_LENS);
					}
					if(event==1) {
						insertkey(id, CAM_STA);
						insertkey(id, CAM_END);
					}
				}
			}
		}
		else if(FALSE /* && G.buts->mainb==BUTS_SOUND */) {
			if(G.ssound) {
				id= G.buts->lockpoin;
				if(id) {
					event= pupmenu("Insert Key %t|Volume%x0|Pitch%x1|Panning%x2|Attennuation%x3");
					if(event== -1) return;

					if(event==0) {
						insertkey(id, SND_VOLUME);
					}
					if(event==1) {
						insertkey(id, SND_PITCH);
					}
					if(event==2) {
						insertkey(id, SND_PANNING);
					}
					if(event==3) {
						insertkey(id, SND_ATTEN);
					}
				}
			}
		}
		
		allqueue(REDRAWACTION, 0);
		allqueue(REDRAWNLA, 0);
		allqueue(REDRAWIPO, 0);
		allspace(REMAKEIPO, 0);

	}
	else if(curarea->spacetype==SPACE_VIEW3D) {
		

		base= FIRSTBASE;
		while(base) {
			if TESTBASELIB(base) break;
			base= base->next;
		}
		if(base==0) return;
		
		if (G.obpose)
			strcpy(menustr, "Insert Key%t|Loc%x0|Rot%x1|Size%x2|LocRot%x3|LocRotSize%x4|Avail%x9");
		else
			strcpy(menustr, "Insert Key%t|Loc%x0|Rot%x1|Size%x2|LocRot%x3|LocRotSize%x4|Layer%x5|Avail%x9");

		
		if( (ob = OBACT)) {
			if(ob->type==OB_MESH) strcat(menustr, "| %x6|Mesh%x7");
			else if(ob->type==OB_LATTICE) strcat(menustr, "| %x6|Lattice%x7");
			else if(ob->type==OB_CURVE) strcat(menustr, "| %x6|Curve%x7");
			else if(ob->type==OB_SURF) strcat(menustr, "| %x6|Surface%x7");
			else if(ob->type==OB_IKA) strcat(menustr, "| %x6|Effector%x8");
			if(ob->flag & OB_FROMGROUP)	strcat(menustr, "| %x6|Entire Group%x10");
		}
		
		event= pupmenu(menustr);
		if(event== -1) return;
		
		if(event==7) {
			if(ob->type==OB_MESH) insert_meshkey(ob->data, 0);
			else if ELEM(ob->type, OB_CURVE, OB_SURF) insert_curvekey(ob->data);
			else if(ob->type==OB_LATTICE) insert_lattkey(ob->data);
			
			allqueue(REDRAWIPO, 0);
			allqueue(REDRAWACTION, 0);
			allqueue(REDRAWNLA, 0);
			allqueue(REDRAWBUTSOBJECT, 0);
			return;
		}
		
		if(event==10) {
			Group *group= find_group(ob);
			if(group) {
				add_group_key(group);
				allqueue(REDRAWBUTSOBJECT, 0);
			}
		}
		
		base= FIRSTBASE;
		if (G.obpose){
			bAction	*act;
			bPose	*pose;
			bPoseChannel *chan;
			bActionChannel *achan;

			ob = G.obpose;

			/* Get action & pose from object */
			act=ob->action;
			pose=ob->pose;

			collect_pose_garbage(ob);

			if (!act){
				act= G.obpose->action=add_empty_action();
				/* this sets the non-pinned open ipowindow(s) to show the action curve */
				ob->ipowin= ID_AC;
				allqueue(REDRAWIPO, ob->ipowin);
				
				allqueue(REDRAWACTION, 0);
				allqueue(REDRAWNLA, 0);
			}
			if (!pose){
				error ("No pose!"); /* Should never happen */
			}

			if (act->id.lib)
			{
				error ("Can't key libactions");
				return;
			}
			filter_pose_keys ();
			for (chan=pose->chanbase.first; chan; chan=chan->next)
			{
				if (chan->flag & POSE_KEY){
					//			set_action_key(act, chan);
					if(event==0 || event==3 ||event==4) {
						set_action_key(act, chan, AC_LOC_X, 1);
						set_action_key(act, chan, AC_LOC_Y, 1);
						set_action_key(act, chan, AC_LOC_Z, 1);
					}
					if(event==1 || event==3 ||event==4) {
						set_action_key(act, chan, AC_QUAT_X, 1);
						set_action_key(act, chan, AC_QUAT_Y, 1);
						set_action_key(act, chan, AC_QUAT_Z, 1);
						set_action_key(act, chan, AC_QUAT_W, 1);
					}
					if(event==2 || event==4) {
						set_action_key(act, chan, AC_SIZE_X, 1);
						set_action_key(act, chan, AC_SIZE_Y, 1);
						set_action_key(act, chan, AC_SIZE_Z, 1);
					}
					if (event==9){
						for (achan = act->chanbase.first; achan; achan=achan->next){
							if (achan->ipo && !strcmp (achan->name, chan->name)){
								for (icu = achan->ipo->curve.first; icu; icu=icu->next){
									set_action_key(act, chan, icu->adrcode, 0);
								}
								break;
							}
						}
					}
				}

				remake_action_ipos(act);
			}
			allqueue(REDRAWIPO, 0);
			allqueue(REDRAWACTION, 0);
			allqueue(REDRAWNLA, 0);
			
		}
		else
		{
			while(base) {
				if TESTBASELIB(base) {
					id= (ID *)(base->object);
					
					/* all curves in ipo deselect */
					if(base->object->ipo) {
						icu= base->object->ipo->curve.first;
						while(icu) {
							icu->flag &= ~IPO_SELECT;
							if(event==9) insertkey(id, icu->adrcode);
							icu= icu->next;
						}
					}
					
					if(event==0 || event==3 ||event==4) {
						insertkey(id, OB_LOC_X);
						insertkey(id, OB_LOC_Y);
						insertkey(id, OB_LOC_Z);
					}
					if(event==1 || event==3 ||event==4) {
						insertkey(id, OB_ROT_X);
						insertkey(id, OB_ROT_Y);
						insertkey(id, OB_ROT_Z);
					}
					if(event==2 || event==4) {
						insertkey(id, OB_SIZE_X);
						insertkey(id, OB_SIZE_Y);
						insertkey(id, OB_SIZE_Z);
					}
					if(event==5) {
						/* remove localview  */
						tlay= base->object->lay;
						base->object->lay &= 0xFFFFFF;
						insertkey(id, OB_LAY);
						base->object->lay= tlay;
					}
					if(event==8) {
						/* a patch, can be removed (old ika) */
						Ika *ika= ob->data;
						VecMat4MulVecfl(ika->effg, ob->obmat, ika->effn);
						
						insertkey(id, OB_EFF_X);
						insertkey(id, OB_EFF_Y);
						insertkey(id, OB_EFF_Z);
					}
				}
				base= base->next;
			}
		}
		allspace(REMAKEIPO, 0);
		allqueue(REDRAWIPO, 0);
		allqueue(REDRAWVIEW3D, 0);
		allqueue(REDRAWACTION, 0);
		allqueue(REDRAWNLA, 0);
	}
	
}


/* **************************************************** */

/* IPOKEY:
 * 
 *   there are three ways to use this system:
 *   1. below: for drawing and editing in Ipo window
 *   2. for drawing key positions in View3D (see ipo.c and drawobject.c)
 *   3. editing keys in View3D (below and in editobject.c)
 * 
 */


void free_ipokey(ListBase *lb)
{
	IpoKey *ik;
	
	ik= lb->first;
	while(ik) {
		if(ik->data) MEM_freeN(ik->data);
		ik= ik->next;
	}
	BLI_freelistN(lb);
}


void add_to_ipokey(ListBase *lb, BezTriple *bezt, int nr, int len)
{
	IpoKey *ik, *ikn;
	
	ik= lb->first;
	while(ik) {
		
		if( ik->val==bezt->vec[1][0] ) {
			if(ik->data[nr]==0) {	/* double points! */
				ik->data[nr]= bezt;
				if(bezt->f2 & 1) ik->flag= 1;
				return;
			}
		}
		else if(ik->val > bezt->vec[1][0]) break;
		
		ik= ik->next;
	}	
	
	ikn= MEM_callocN(sizeof(IpoKey), "add_to_ipokey");	
	if(ik) BLI_insertlinkbefore(lb, ik, ikn);
	else BLI_addtail(lb, ikn);

	ikn->data= MEM_callocN(sizeof(float *)*len, "add_to_ipokey");
	ikn->data[nr]= bezt;
	ikn->val= bezt->vec[1][0];

	if(bezt->f2 & 1) ikn->flag= 1;
}

void make_ipokey(void)
{
	EditIpo *ei;
	IpoKey *ik;
	ListBase *lb;
	BezTriple *bezt;
	int a, b, sel, desel, totvert;
	
	lb= &G.sipo->ipokey;
	free_ipokey(lb);
	
	ei= G.sipo->editipo;
	if(ei==0) return;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			bezt= ei->icu->bezt;
			totvert= ei->icu->totvert;
			
			for(b=0; b<totvert; b++, bezt++) {
				add_to_ipokey(lb, bezt, a, G.sipo->totipo);
			}
			
			ei->flag &= ~IPO_SELECT;
			ei->flag &= ~IPO_EDIT;
			ei->icu->flag= ei->flag;
		}
	}
	
	/* test selectflags */
	ik= lb->first;
	while(ik) {
		sel= desel= 0;
		for(a=0; a<G.sipo->totipo; a++) {
			if(ik->data[a]) {
				bezt= ik->data[a];
				if(bezt->f2 & 1) sel++;
				else desel++;
			}
		}
		if(sel && desel) sel= 0;
		for(a=0; a<G.sipo->totipo; a++) {
			if(ik->data[a]) {
				bezt= ik->data[a];
				if(sel) {
					bezt->f1 |= 1;
					bezt->f2 |= 1;
					bezt->f3 |= 1;
				}
				else {
					bezt->f1 &= ~1;
					bezt->f2 &= ~1;
					bezt->f3 &= ~1;
				}
			}
		}
		if(sel) ik->flag = 1;
		else ik->flag= 0;
		
		ik= ik->next;
	}
	get_status_editipo();
}

void make_ipokey_transform(Object *ob, ListBase *lb, int sel)
{
	IpoCurve *icu;
	BezTriple *bezt;
	int a, adrcode = 0, ok, dloc=0, drot=0, dsize=0;
	
	if(ob->ipo==0) return;
	if(ob->ipo->showkey==0) return;
	
	/* test: are there delta curves? */
	icu= ob->ipo->curve.first;
	while(icu) {
		if(icu->flag & IPO_VISIBLE) {
			switch(icu->adrcode) {
			case OB_DLOC_X:
			case OB_DLOC_Y:
			case OB_DLOC_Z:
				dloc= 1;
				break;
			case OB_DROT_X:
			case OB_DROT_Y:
			case OB_DROT_Z:
				drot= 1;
				break;
			case OB_DSIZE_X:
			case OB_DSIZE_Y:
			case OB_DSIZE_Z:
				dsize= 1;
				break;
			}
		}
		icu= icu->next;
	}

	icu= ob->ipo->curve.first;
	while(icu) {
		if(icu->flag & IPO_VISIBLE) {
			ok= 0;
			
			switch(icu->adrcode) {
			case OB_DLOC_X:
			case OB_DLOC_Y:
			case OB_DLOC_Z:
			case OB_DROT_X:
			case OB_DROT_Y:
			case OB_DROT_Z:
			case OB_DSIZE_X:
			case OB_DSIZE_Y:
			case OB_DSIZE_Z:
				ok= 1;
				break;
				
			case OB_LOC_X:
			case OB_LOC_Y:
			case OB_LOC_Z:
				if(dloc==0) ok= 1;
				break;
			case OB_ROT_X:
			case OB_ROT_Y:
			case OB_ROT_Z:
				if(drot==0) ok= 1;
				break;
			case OB_SIZE_X:
			case OB_SIZE_Y:
			case OB_SIZE_Z:
				if(dsize==0) ok= 1;
				break;
			}
			if(ok) {
				for(a=0; a<OB_TOTIPO; a++) {
					if(icu->adrcode==ob_ar[a]) {
						adrcode= a;
						break;
					}
				}
			
				bezt= icu->bezt;
				a= icu->totvert;
				while(a--) {
					if(sel==0 || (bezt->f2 & 1)) {
						add_to_ipokey(lb, bezt, adrcode, OB_TOTIPO);
					}
					bezt++;
				}
			}
		}
		icu= icu->next;
	}
}

void update_ipokey_val()	/* after moving vertices */
{
	IpoKey *ik;
	int a;
	
	ik= G.sipo->ipokey.first;
	while(ik) {
		for(a=0; a<G.sipo->totipo; a++) {
			if(ik->data[a]) {
				ik->val= ik->data[a]->vec[1][0];
				break;
			}
		}
		ik= ik->next;
	}
}

void set_tob_old(float *old, float *poin)
{
	old[0]= *(poin);
	old[3]= *(poin-3);
	old[6]= *(poin+3);
}

void set_ipo_pointers_transob(IpoKey *ik, TransOb *tob)
{
	BezTriple *bezt;
	int a, delta= 0;

	tob->locx= tob->locy= tob->locz= 0;
	tob->rotx= tob->roty= tob->rotz= 0;
	tob->sizex= tob->sizey= tob->sizez= 0;
	
	for(a=0; a<OB_TOTIPO; a++) {
		if(ik->data[a]) {
			bezt= ik->data[a];
		
			switch( ob_ar[a] ) {
			case OB_LOC_X:
			case OB_DLOC_X:
				tob->locx= &(bezt->vec[1][1]); break;
			case OB_LOC_Y:
			case OB_DLOC_Y:
				tob->locy= &(bezt->vec[1][1]); break;
			case OB_LOC_Z:
			case OB_DLOC_Z:
				tob->locz= &(bezt->vec[1][1]); break;
		
			case OB_DROT_X:
				delta= 1;
			case OB_ROT_X:
				tob->rotx= &(bezt->vec[1][1]); break;
			case OB_DROT_Y:
				delta= 1;
			case OB_ROT_Y:
				tob->roty= &(bezt->vec[1][1]); break;
			case OB_DROT_Z:
				delta= 1;
			case OB_ROT_Z:
				tob->rotz= &(bezt->vec[1][1]); break;
				
			case OB_SIZE_X:
			case OB_DSIZE_X:
				tob->sizex= &(bezt->vec[1][1]); break;
			case OB_SIZE_Y:
			case OB_DSIZE_Y:
				tob->sizey= &(bezt->vec[1][1]); break;
			case OB_SIZE_Z:
			case OB_DSIZE_Z:
				tob->sizez= &(bezt->vec[1][1]); break;		
			}	
		}
	}
	
	/* oldvals for e.g. undo */
	if(tob->locx) set_tob_old(tob->oldloc, tob->locx);
	if(tob->locy) set_tob_old(tob->oldloc+1, tob->locy);
	if(tob->locz) set_tob_old(tob->oldloc+2, tob->locz);
		
		/* store first oldrot, for mapping curves ('1'=10 degrees) and correct calculation */
	if(tob->rotx) set_tob_old(tob->oldrot+3, tob->rotx);
	if(tob->roty) set_tob_old(tob->oldrot+4, tob->roty);
	if(tob->rotz) set_tob_old(tob->oldrot+5, tob->rotz);
	
		/* store the first oldsize, this is not allowed to be dsize! */
	if(tob->sizex) set_tob_old(tob->oldsize+3, tob->sizex);
	if(tob->sizey) set_tob_old(tob->oldsize+4, tob->sizey);
	if(tob->sizez) set_tob_old(tob->oldsize+5, tob->sizez);

	tob->flag= TOB_IPO;
	if(delta) tob->flag |= TOB_IPODROT;
}



void nextkey(ListBase *elems, int dir)
{
	IpoKey *ik, *previk;
	int totsel;
	
	if(dir==1) ik= elems->last;
	else ik= elems->first;
	previk= 0;
	totsel= 0;
	
	while(ik) {
		
		if(ik->flag) totsel++;
		
		if(previk) {
			if(G.qual & LR_SHIFTKEY) {
				if(ik->flag) previk->flag= 1;
			}
			else previk->flag= ik->flag;
		}
		
		previk= ik;
		if(dir==1) ik= ik->prev;
		else ik= ik->next;
		
		if(G.qual & LR_SHIFTKEY);
		else if(ik==0) previk->flag= 0;
	}
	
	/* when no key select: */
	if(totsel==0) {
		if(dir==1) ik= elems->first;
		else ik= elems->last;
		
		if(ik) ik->flag= 1;
	}
}

static int float_to_frame (float frame) 
{
	int to= (int) frame;
	
	if (frame-to>0.5) to++;
	
	return to;	
}

void movekey_ipo(int dir)		/* only call external from view3d queue */
{
	IpoKey *ik;
	float toframe = 0.0;
	int a;
	
	if(G.sipo->showkey==0) return;

	ik= G.sipo->ipokey.first;
	if (dir==-1) {
		while (ik && float_to_frame(ik->val)<CFRA) {
			toframe= ik->val;
			ik= ik->next;
		}
	} else {
		while (ik && float_to_frame(ik->val)<=CFRA) {
			ik= ik->next;
		}
		if (ik) toframe= ik->val;
	}
	
	a= float_to_frame(toframe);
	
	if (a!=CFRA && a>0) {
		CFRA= a;
		
		update_for_newframe();
	}
	
	allqueue(REDRAWNLA, 0);
	allqueue(REDRAWACTION, 0);
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWIPO, 0);
	allspace(REMAKEIPO, 0);

}

void movekey_obipo(int dir)		/* only call external from view3d queue */
{
	Base *base;
	Object *ob;
	ListBase elems;
	IpoKey *ik;
	int a;
	float toframe= CFRA;
	
	base= FIRSTBASE;
	while(base) {
		if TESTBASE(base) {
			ob= base->object;
			if(ob->ipo && ob->ipo->showkey) {
				elems.first= elems.last= 0;
				make_ipokey_transform(ob, &elems, 0);

				if(elems.first) {
					ik= elems.first;
					if (dir==-1) {
						while (ik && float_to_frame(ik->val)<CFRA) {
							toframe= ik->val;
							ik= ik->next;
						}
					} else {
						while (ik && float_to_frame(ik->val)<=CFRA) {
							ik= ik->next;
						}
						if (ik) toframe= ik->val;
					}
										
					free_ipokey(&elems);
				}
			}
		}
		
		base= base->next;
	}
	
	a= float_to_frame(toframe);
	
	if (a!=CFRA && a>0) {
		CFRA= a;
		
		update_for_newframe();
	}
	
	allqueue(REDRAWNLA, 0);
	allqueue(REDRAWACTION, 0);
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWIPO, 0);
	allspace(REMAKEIPO, 0);

}

void nextkey_ipo(int dir)			/* call from ipo queue */
{
	IpoKey *ik;
	int a;
	
	if(G.sipo->showkey==0) return;
	
	nextkey(&G.sipo->ipokey, dir);
	
	/* copy to beziers */
	ik= G.sipo->ipokey.first;
	while(ik) {
		for(a=0; a<G.sipo->totipo; a++) {
			if(ik->data[a]) ik->data[a]->f1= ik->data[a]->f2= ik->data[a]->f3= ik->flag;
		}
		ik= ik->next;
	}		

	allqueue(REDRAWNLA, 0);
	allqueue(REDRAWACTION, 0);
	allqueue(REDRAWIPO, 0);
	if(G.sipo->blocktype == ID_OB) allqueue(REDRAWVIEW3D, 0);
}

void nextkey_obipo(int dir)		/* only call external from view3d queue */
{
	Base *base;
	Object *ob;
	ListBase elems;
	IpoKey *ik;
	int a;
	
	/* problem: this doesnt work when you mix dLoc keys with Loc keys */
	
	base= FIRSTBASE;
	while(base) {
		if TESTBASE(base) {
			ob= base->object;
			if( (ob->ipoflag & OB_DRAWKEY) && ob->ipo && ob->ipo->showkey) {
				elems.first= elems.last= 0;
				make_ipokey_transform(ob, &elems, 0);

				if(elems.first) {
					
					nextkey(&elems, dir);
					
					/* copy to beziers */
					ik= elems.first;
					while(ik) {
						for(a=0; a<OB_TOTIPO; a++) {
							if(ik->data[a]) ik->data[a]->f1= ik->data[a]->f2= ik->data[a]->f3= ik->flag;
						}
						ik= ik->next;
					}
					
					free_ipokey(&elems);
				}
			}
		}
		
		base= base->next;
	}
	allqueue(REDRAWNLA, 0);
	allqueue(REDRAWACTION, 0);
	allqueue(REDRAWVIEW3D, 0);
	allspace(REMAKEIPO, 0);
	allqueue(REDRAWIPO, 0);
}


/* **************************************************** */


void remake_ipo_transverts(TransVert *transmain, float *dvec, int tot)
{
	EditIpo *ei;
	TransVert *tv;
	BezTriple *bezt;
	int a, b;
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
				
			if(ei->icu->bezt) {
				sort_time_ipocurve(ei->icu);
			}
		}
	}

	ei= G.sipo->editipo;
	tv= transmain;
	for(a=0; a<G.sipo->totipo; a++, ei++) {
		
		if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
			if( (ei->flag & IPO_EDIT) || G.sipo->showkey) {
				if(ei->icu->bezt) {
					bezt= ei->icu->bezt;
					b= ei->icu->totvert;
					while(b--) {
						if(ei->icu->ipo==IPO_BEZ) {
							if(bezt->f1 & 1) {
								tv->loc= bezt->vec[0];
								tv++;
							}
							if(bezt->f3 & 1) {
								tv->loc= bezt->vec[2];
								tv++;
							}
						}
						if(bezt->f2 & 1) {
							tv->loc= bezt->vec[1];
							tv++;
						}
						
						bezt++;
					}
					testhandles_ipocurve(ei->icu);
				}
			}
		}
	}
	
	if(G.sipo->showkey) make_ipokey();
	
	if(dvec==0) return;
	
	tv= transmain;
	for(a=0; a<tot; a++, tv++) {
		tv->oldloc[0]= tv->loc[0]-dvec[0];
		tv->oldloc[1]= tv->loc[1]-dvec[1];
	}
}

void transform_ipo(int mode)
{
	EditIpo *ei;
	BezTriple *bezt;
	TransVert *transmain = NULL, *tv;
	float xref=1.0, yref=1.0, dx, dy, dvec[2], min[3], max[3], vec[2], div, cent[2], size[2], sizefac;
	int tot=0, a, b, firsttime=1, afbreek=0, midtog= 0, dosort, proj = 0;
	unsigned short event = 0;
	short mval[2], val, xo, yo, xn, yn, xc, yc;
	char str[32];
	
	if(G.sipo->ipo && G.sipo->ipo->id.lib) return;
	if(G.sipo->editipo==0) return;
	if(mode=='r') return;	/* from gesture */
	
	INIT_MINMAX(min, max);
	
	/* which vertices are involved */
	get_status_editipo();
	if(totipo_vertsel) {
		tot= totipo_vertsel;
		tv=transmain= MEM_callocN(tot*sizeof(TransVert), "transmain");
		
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			
			if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
				if( (ei->flag & IPO_EDIT) || G.sipo->showkey) {

				
					if(ei->icu->bezt) {
						bezt= ei->icu->bezt;
						b= ei->icu->totvert;
						while(b--) {
							if(ei->icu->ipo==IPO_BEZ) {
								if(bezt->f1 & 1) {
									tv->loc= bezt->vec[0];
									VECCOPY(tv->oldloc, tv->loc);
									if(ei->disptype==IPO_DISPBITS) tv->flag= 1;
									
									/* we take the middle vertex */
									DO_MINMAX2(bezt->vec[1], min, max);

									tv++;
								}
								if(bezt->f3 & 1) {
									tv->loc= bezt->vec[2];
									VECCOPY(tv->oldloc, tv->loc);
									if(ei->disptype==IPO_DISPBITS) tv->flag= 1;
									
									/* we take the middle vertex */
									DO_MINMAX2(bezt->vec[1], min, max);

									tv++;
								}
							}
							if(bezt->f2 & 1) {
								tv->loc= bezt->vec[1];
								VECCOPY(tv->oldloc, tv->loc);
								if(ei->disptype==IPO_DISPBITS) tv->flag= 1;
								DO_MINMAX2(bezt->vec[1], min, max);
								tv++;
							}
							bezt++;
						}
					}
				}
			}
		}
		
	}
	else if(totipo_edit==0 && totipo_sel!=0) {
		
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_SELECT, icu) {
				if(ei->icu->bezt && ei->icu->ipo==IPO_BEZ) tot+= 3*ei->icu->totvert;
				else tot+= ei->icu->totvert;
			}
		}
		if(tot==0) return;
		
		tv=transmain= MEM_callocN(tot*sizeof(TransVert), "transmain");

		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			if ISPOIN3(ei, flag & IPO_VISIBLE, flag & IPO_SELECT, icu) {
				if(ei->icu->bezt) {
					
					bezt= ei->icu->bezt;
					b= ei->icu->totvert;
					while(b--) {
						if(ei->icu->ipo==IPO_BEZ) {
							tv->loc= bezt->vec[0];
							VECCOPY(tv->oldloc, tv->loc);
							if(ei->disptype==IPO_DISPBITS) tv->flag= 1;
							tv++;
						
							tv->loc= bezt->vec[2];
							VECCOPY(tv->oldloc, tv->loc);
							if(ei->disptype==IPO_DISPBITS) tv->flag= 1;
							tv++;
						}
						tv->loc= bezt->vec[1];
						VECCOPY(tv->oldloc, tv->loc);
						if(ei->disptype==IPO_DISPBITS) tv->flag= 1;
						
						DO_MINMAX2(bezt->vec[1], min, max);
						
						tv++;
						
						bezt++;
					}
				}
			}
		}

	}

	if(tot==0) {
		if(totipo_edit==0) move_keys();
		return;
	}

	cent[0]= (float)((min[0]+max[0])/2.0);
	cent[1]= (float)((min[1]+max[1])/2.0);

	if(G.sipo->showkey) {
		midtog= 1;
		proj= 1;
	}
	
	ipoco_to_areaco(G.v2d, cent, mval);
	xc= mval[0];
	yc= mval[1];
	
	getmouseco_areawin(mval);
	xo= xn= mval[0];
	yo= yn= mval[1];
	dvec[0]= dvec[1]= 0.0;
	
	sizefac= (float)(sqrt( (float)((yc-yn)*(yc-yn)+(xn-xc)*(xn-xc)) ));
	if(sizefac<2.0) sizefac= 2.0;

	while(afbreek==0) {
		getmouseco_areawin(mval);
		if(mval[0]!=xo || mval[1]!=yo || firsttime) {
			
			if(mode=='g') {
			
				dx= (float)(mval[0]- xo);
				dy= (float)(mval[1]- yo);
	
				div= (float)(G.v2d->mask.xmax-G.v2d->mask.xmin);
				dvec[0]+= (G.v2d->cur.xmax-G.v2d->cur.xmin)*(dx)/div;
	
				div= (float)(G.v2d->mask.ymax-G.v2d->mask.ymin);
				dvec[1]+= (G.v2d->cur.ymax-G.v2d->cur.ymin)*(dy)/div;
				
				if(midtog) dvec[proj]= 0.0;
				
				/* vec is reused below: remake_ipo_transverts */
				vec[0]= dvec[0];
				vec[1]= dvec[1];
				
				apply_keyb_grid(vec, 0.0, (float)1.0, (float)0.1, U.flag & USER_AUTOGRABGRID);
				apply_keyb_grid(vec+1, 0.0, (float)1.0, (float)0.1, 0);
				
				tv= transmain;
				for(a=0; a<tot; a++, tv++) {
					tv->loc[0]= tv->oldloc[0]+vec[0];

					if(tv->flag==0) tv->loc[1]= tv->oldloc[1]+vec[1];
				}
				
				sprintf(str, "X: %.3f   Y: %.3f  ", vec[0], vec[1]);
				headerprint(str);
			}
			else if(mode=='s') {
				
				size[0]=size[1]=(float)( (sqrt( (float)((yc-mval[1])*(yc-mval[1])+(mval[0]-xc)*(mval[0]-xc)) ))/sizefac);
				
				if(midtog) size[proj]= 1.0;
				size[0]*= xref;
				size[1]*= yref;
				
				apply_keyb_grid(size, 0.0, (float)0.2, (float)0.1, U.flag & USER_AUTOSIZEGRID);
				apply_keyb_grid(size+1, 0.0, (float)0.2, (float)0.1, U.flag & USER_AUTOSIZEGRID);

				tv= transmain;

				for(a=0; a<tot; a++, tv++) {
					tv->loc[0]= size[0]*(tv->oldloc[0]-cent[0])+ cent[0];
					if(tv->flag==0) tv->loc[1]= size[1]*(tv->oldloc[1]-cent[1])+ cent[1];
				}
				
				sprintf(str, "sizeX: %.3f   sizeY: %.3f  ", size[0], size[1]);
				headerprint(str);
				
			}
			
			xo= mval[0];
			yo= mval[1];
				
			dosort= 0;
			ei= G.sipo->editipo;
			for(a=0; a<G.sipo->totipo; a++, ei++) {
				if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
					
					/* watch it: if the time is wrong: do not correct handles */
					if (test_time_ipocurve(ei->icu) ) dosort++;
					else testhandles_ipocurve(ei->icu);
				}
			}
			
			if(dosort) {
				if(mode=='g') remake_ipo_transverts(transmain, vec, tot);
				else remake_ipo_transverts(transmain, 0, tot);
			}
			if(G.sipo->showkey) update_ipokey_val();
			
			calc_ipo(G.sipo->ipo, (float)CFRA);

			/* update realtime */
			if(G.sipo->lock) {
				if(G.sipo->blocktype==ID_MA) {
					force_draw_plus(SPACE_BUTS);
				}
				else if(G.sipo->blocktype==ID_KE) {
					do_ob_key(OBACT);
					makeDispList(OBACT);
					force_draw_plus(SPACE_VIEW3D);
				}
				else if(G.sipo->blocktype==ID_AC) {
					do_all_actions();
					force_draw_all();
				}
				else if(G.sipo->blocktype==ID_OB) {
					Base *base= FIRSTBASE;
					
					while(base) {
						if(base->object->ipo==G.sipo->ipo) do_ob_ipo(base->object);
						base= base->next;
					}
					force_draw_plus(SPACE_VIEW3D);
				}
				else force_draw();
			}
			else {
				force_draw();
			}
			firsttime= 0;
		}
		else BIF_wait_for_statechange();
		
		while(qtest()) {
			event= extern_qread(&val);
			if(val) {
				switch(event) {
				case ESCKEY:
				case LEFTMOUSE:
				case RIGHTMOUSE:
				case SPACEKEY:
				case RETKEY:
					afbreek= 1;
					break;
				case MIDDLEMOUSE:
					if(G.sipo->showkey==0) {
						midtog= ~midtog;
						if(midtog) {
							if( abs(mval[0]-xn) > abs(mval[1]-yn)) proj= 1;
							else proj= 0;
							firsttime= 1;
						}
					}
					break;
				case XKEY:
				case YKEY:
					if(event==XKEY) xref= -xref;
					else if(G.sipo->showkey==0) yref= -yref;
					firsttime= 1;
					break;
				case LEFTCTRLKEY:
				case RIGHTCTRLKEY:
					firsttime= 1;
					break;
				default:
					if(mode=='g') {
						if(G.qual & LR_CTRLKEY) {
							if(event==LEFTARROWKEY) {dvec[0]-= 1.0; firsttime= 1;}
							else if(event==RIGHTARROWKEY) {dvec[0]+= 1.0; firsttime= 1;}
							else if(event==UPARROWKEY) {dvec[1]+= 1.0; firsttime= 1;}
							else if(event==DOWNARROWKEY) {dvec[1]-= 1.0; firsttime= 1;}
						}
						else arrows_move_cursor(event);
					}
					else arrows_move_cursor(event);
				}
			}
			if(afbreek) break;
		}
	}
	
	if(event==ESCKEY || event==RIGHTMOUSE) {
		tv= transmain;
		for(a=0; a<tot; a++, tv++) {
			tv->loc[0]= tv->oldloc[0];
			tv->loc[1]= tv->oldloc[1];
		}
		
		dosort= 0;
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
				if( (ei->flag & IPO_EDIT) || G.sipo->showkey) {
					if( test_time_ipocurve(ei->icu)) {
						dosort= 1;
						break;
					}
				}
			}
		}
		
		if(dosort) remake_ipo_transverts(transmain, 0, tot);
		
		ei= G.sipo->editipo;
		for(a=0; a<G.sipo->totipo; a++, ei++) {
			if ISPOIN(ei, flag & IPO_VISIBLE, icu) {
				if( (ei->flag & IPO_EDIT) || G.sipo->showkey) {
					testhandles_ipocurve(ei->icu);
				}
			}
		}
		calc_ipo(G.sipo->ipo, (float)CFRA);
	}
	
	editipo_changed(G.sipo, 1);

	MEM_freeN(transmain);
}

void filter_sampledata(float *data, int sfra, int efra)
{
	float *da;
	int a;
	
	da= data+1;
	for(a=sfra+1; a<efra; a++, da++) {
		da[0]=(float)( 0.25*da[-1] + 0.5*da[0] + 0.25*da[1]);
	}
	
}

void sampledata_to_ipocurve(float *data, int sfra, int efra, IpoCurve *icu)
{
	BezTriple *bezt;
	float *da;
	int a, tot;
	
	filter_sampledata(data, sfra, efra);
	filter_sampledata(data, sfra, efra);
	
	icu->ipo= IPO_LIN;
	
	if(icu->bezt) MEM_freeN(icu->bezt);
	icu->bezt= 0;
	
	tot= 1;	/* first point */
	da= data+1;
	for(a=sfra+1; a<efra; a++, da++) {
		if( IS_EQ(da[0], da[1])==0 && IS_EQ(da[1], da[2])==0 ) tot++;
	}
	
	icu->totvert= tot;
	bezt= icu->bezt= MEM_callocN(tot*sizeof(BezTriple), "samplebezt");
	bezt->vec[1][0]= (float)sfra;
	bezt->vec[1][1]= data[0];
	bezt++;
	da= data+1;
	for(a=sfra+1; a<efra; a++, da++) {
		if( IS_EQ(da[0], da[1])==0 && IS_EQ(da[1], da[2])==0 ) {
			bezt->vec[1][0]= (float)a;
			bezt->vec[1][1]= da[0];
			bezt++;
		}
	}	
}

void ipo_record()
{
	/* only 1 or 2 active curves
	 * make a copy (ESC) 
	 *
	 * reference point is the current situation (or 0)
	 * dx (dy) is the height correction factor
	 * CTRL: start record
	 */
	extern double tottime;
	EditIpo *ei, *ei1=0, *ei2=0;
	ScrArea *sa, *oldarea;
	Ipo *ipo;
	void *poin;
	double swaptime;
	float or1, or2 = 0.0, fac, *data1, *data2;
	int type, a, afbreek=0, firsttime=1, cfrao, cfra, sfra, efra;
	unsigned short event = 0;
	short anim, val, xn, yn, mvalo[2], mval[2];
	char str[128];
	
	if(G.sipo->from==0) return;
	if(SFRA>=EFRA) return;
	
	anim= pupmenu("Record Mouse %t|Still %x1|Play Animation %x2");
	if(anim < 1) return;
	if(anim!=2) anim= 0;

	ipo= get_ipo(G.sipo->from, G.sipo->blocktype, 1);	/* 1= make */
	if(G.sipo) G.sipo->ipo= ipo;

	/* find the curves... */
	
	ei= G.sipo->editipo;
	for(a=0; a<G.sipo->totipo; a++) {
		if(ei->flag & IPO_VISIBLE) {
			
			if(ei1==0) ei1= ei;
			else if(ei2==0) ei2= ei;
			else {
				error("Maximum 2 visible curves");
				return;
			}
		}
		ei++;
	}

	if(ei1==0) {
		error("Select 1 or 2 channels");
		return;
	}
	
	/* make curves ready, start values */
	if(ei1->icu==0) ei1->icu= get_ipocurve(G.sipo->from, G.sipo->blocktype, ei1->adrcode, 0);
	if(ei1->icu==0) return;
	poin= get_ipo_poin(G.sipo->from, ei1->icu, &type);
	if(poin) ei1->icu->curval= read_ipo_poin(poin, type);
	or1= ei1->icu->curval;
	ei1->icu->flag |= IPO_LOCK;
	
	if(ei2) {
		if(ei2->icu==0)  ei2->icu= get_ipocurve(G.sipo->from, G.sipo->blocktype, ei2->adrcode, 0);
		if(ei2->icu==0) return;
		poin= get_ipo_poin(G.sipo->from, ei2->icu, &type);
		if(poin) ei2->icu->curval= read_ipo_poin(poin, type);
		or2= ei2->icu->curval;
		ei2->icu->flag |= IPO_LOCK;
	}

	fac= G.v2d->cur.ymax - G.v2d->cur.ymin;
	fac/= (float)curarea->winy;

	/* which area */
	oldarea= curarea;
	sa= G.curscreen->areabase.first;
	while(sa) {
		if(sa->win) {
			if(G.sipo->blocktype==ID_MA || G.sipo->blocktype==ID_LA) {
				if(sa->spacetype==SPACE_BUTS) break;
			}
			else {
				if(sa->spacetype==SPACE_VIEW3D) break;
			}
		}		
		sa= sa->next;	
	}
	if(sa) areawinset(sa->win);
	
	/* can we? */
	while(get_mbut()&L_MOUSE) BIF_wait_for_statechange();
	data1= MEM_callocN(sizeof(float)*(EFRA-SFRA+1), "data1");
	data2= MEM_callocN(sizeof(float)*(EFRA-SFRA+1), "data2");
	
	getmouseco_areawin(mvalo);
	xn= mvalo[0]; yn= mvalo[1];
	waitcursor(1);
	
	tottime= 0.0;
	swaptime= speed_to_swaptime(G.animspeed);
	cfrao= CFRA;
	cfra=efra= SFRA;
	sfra= EFRA;
	
	while(afbreek==0) {
		
		getmouseco_areawin(mval);
		
		if(mval[0]!= mvalo[0] || mval[1]!=mvalo[1] || firsttime || (G.qual & LR_CTRLKEY)) {
			if(anim) CFRA= cfra;
			else firsttime= 0;

			set_timecursor(cfra);
			
			/* do ipo: first all, then the specific ones */
			if(anim==2) {
				do_all_ipos();
				do_all_keys();
			}

			ei1->icu->curval= or1 + fac*(mval[0]-xn);
			if(ei2) ei2->icu->curval= or2 + fac*(mval[1]-yn);

			do_ipo_nocalc(G.sipo->ipo);
			do_all_visible_ikas();
			
			if(G.qual & LR_CTRLKEY) {
				sprintf(str, "Recording... %d\n", cfra);
				data1[ cfra-SFRA ]= ei1->icu->curval;
				if(ei2) data2[ cfra-SFRA ]= ei2->icu->curval;
				
				sfra= MIN2(sfra, cfra);
				efra= MAX2(efra, cfra);
			}
			else sprintf(str, "Mouse Recording. Use Ctrl to start. LeftMouse or Space to end");
			
			do_ob_key(OBACT);

			headerprint(str);

			if(sa) scrarea_do_windraw(sa);

			/* minimal wait swaptime */
			tottime -= swaptime;
			while (update_time()) PIL_sleep_ms(1);

			screen_swapbuffers();
			
			tottime= 0.0;
			
			mvalo[0]= mval[0];
			mvalo[1]= mval[1];
			
			if(anim || (G.qual & LR_CTRLKEY)) {
				cfra++;
				if(cfra>EFRA) cfra= SFRA;
			}
		}
		
		while(qtest()) {
			event= extern_qread(&val);
			if(val) {
				switch(event) {
				case LEFTMOUSE: case ESCKEY: case SPACEKEY: case RETKEY:
					afbreek= 1;
					break;
				}
			}
			if(afbreek) break;
		}
	}
	
	if(event!=ESCKEY) {
		sampledata_to_ipocurve(data1+sfra-SFRA, sfra, efra, ei1->icu);
		if(ei2) sampledata_to_ipocurve(data2+sfra-SFRA, sfra, efra, ei2->icu);

		/* not nice when this is on */
		if(G.sipo->showkey) {
			G.sipo->showkey= 0;
			free_ipokey(&G.sipo->ipokey);
		}
	}
	else {
		/* undo: start values */
		poin= get_ipo_poin(G.sipo->from, ei1->icu, &type);
		if(poin) write_ipo_poin(poin, type, or1);
		if(ei1->icu->bezt==0) {
			BLI_remlink( &(G.sipo->ipo->curve), ei1->icu);
			MEM_freeN(ei1->icu);
			ei1->icu= 0;
		}
		if(ei2) {
			poin= get_ipo_poin(G.sipo->from, ei2->icu, &type);
			if(poin) write_ipo_poin(poin, type, or2);
			if(ei2->icu->bezt==0) {
				BLI_remlink( &(G.sipo->ipo->curve), ei2->icu);
				MEM_freeN(ei2->icu);
				ei2->icu= 0;
			}
		}
	}
	
	if(ei1->icu) ei1->icu->flag &= ~IPO_LOCK;	
	if(ei2 && ei2->icu) ei2->icu->flag &= ~IPO_LOCK;	
	
	editipo_changed(G.sipo, 0);
	do_ipo(G.sipo->ipo);
	waitcursor(0);
	allqueue(REDRAWVIEW3D, 0);
	if(sa) scrarea_queue_headredraw(sa);	/* headerprint */
	scrarea_queue_redraw(oldarea);
	CFRA= cfrao;
	
	/* for the time being? */
	update_for_newframe();
	
	MEM_freeN(data1);
	MEM_freeN(data2);
}



void remake_object_ipos(Object *ob)
{
	IpoCurve		*icu;
	
	if (!ob)
		return;
	if (!ob->ipo)
		return;
	
	for (icu = ob->ipo->curve.first; icu; icu=icu->next){
		sort_time_ipocurve(icu);
		testhandles_ipocurve(icu);
	}
}


int is_ipo_key_selected(Ipo *ipo)
{
	int i;
	IpoCurve *icu;

	if (!ipo)
		return 0;

	for (icu=ipo->curve.first; icu; icu=icu->next){
		for (i=0; i<icu->totvert; i++)
			if (BEZSELECTED(&icu->bezt[i]))
				return 1;
	}

	return 0;
}


void set_ipo_key_selection(Ipo *ipo, int sel)
{
	int i;
	IpoCurve *icu;

	if (!ipo)
		return;

	for (icu=ipo->curve.first; icu; icu=icu->next){
		for (i=0; i<icu->totvert; i++){
			if (sel){
				icu->bezt[i].f1|=1;
				icu->bezt[i].f2|=1;
				icu->bezt[i].f3|=1;
			}
			else{
				icu->bezt[i].f1&=~1;
				icu->bezt[i].f2&=~1;
				icu->bezt[i].f3&=~1;
			}
		}
	}
}

void delete_ipo_keys(Ipo *ipo)
{
	IpoCurve *icu, *next;
	int i;
	
	if (!ipo)
		return;
	
	for (icu=ipo->curve.first; icu; icu=next){
		next = icu->next;
		for (i=0; i<icu->totvert; i++){
			if (icu->bezt[i].f2 & 1){
				//	Delete the item
				memcpy (&icu->bezt[i], &icu->bezt[i+1], sizeof (BezTriple)*(icu->totvert-i-1));
				icu->totvert--;
				i--;
			}
		}
		if (!icu->totvert){
			/* Delete the curve */
			BLI_remlink( &(ipo->curve), icu);
			if(icu->bezt) MEM_freeN(icu->bezt);
			MEM_freeN(icu);
		}
	}
}

int fullselect_ipo_keys(Ipo *ipo)
{
	int i;
	IpoCurve *icu;
	int tvtot = 0;

	if (!ipo)
		return tvtot;
	
	for (icu=ipo->curve.first; icu; icu=icu->next){
		for (i=0; i<icu->totvert; i++){
			if (icu->bezt[i].f2 & 1){
				tvtot+=3;
				icu->bezt[i].f1 |= 1;
				icu->bezt[i].f3 |= 1;
			}
		}
	}

	return tvtot;
}

int add_trans_ipo_keys(Ipo *ipo, TransVert *tv, int tvtot)
{
	int i;
	IpoCurve *icu;
	
	if (!ipo)
		return tvtot;
	
	for (icu=ipo->curve.first; icu; icu=icu->next){
		for (i=0; i<icu->totvert; i++){
			if (icu->bezt[i].f2 & 1){
				tv[tvtot+0].loc=icu->bezt[i].vec[0];
				tv[tvtot+1].loc=icu->bezt[i].vec[1];
				tv[tvtot+2].loc=icu->bezt[i].vec[2];
				
				memcpy (&tv[tvtot+0].oldloc, icu->bezt[i].vec[0], sizeof (float)*3);
				memcpy (&tv[tvtot+1].oldloc, icu->bezt[i].vec[1], sizeof (float)*3);
				memcpy (&tv[tvtot+2].oldloc, icu->bezt[i].vec[2], sizeof (float)*3);
				tvtot+=3;
			}
		}
	}
	
	return tvtot;
}

void duplicate_ipo_keys(Ipo *ipo)
{
	IpoCurve *icu;
	int i;
	BezTriple *newbezt;

	if (!ipo)
		return;

	for (icu=ipo->curve.first; icu; icu=icu->next){
		for (i=0; i<icu->totvert; i++){
			/* If a key is selected */
			if (icu->bezt[i].f2 & 1){
				/* Expand the list */
				newbezt = MEM_callocN(sizeof(BezTriple) * (icu->totvert+1), "beztriple");
				memcpy (newbezt, icu->bezt, sizeof(BezTriple) * (i+1));
				memcpy (newbezt+i+1, icu->bezt+i, sizeof(BezTriple));
				memcpy (newbezt+i+2, icu->bezt+i+1, sizeof (BezTriple) *(icu->totvert-(i+1)));
				icu->totvert++;
				MEM_freeN (icu->bezt);
				icu->bezt=newbezt;
				/* Unselect the current key*/
				icu->bezt[i].f1 &= ~ 1;
				icu->bezt[i].f2 &= ~ 1;
				icu->bezt[i].f3 &= ~ 1;
				i++;
				/* Select the copied key */
				icu->bezt[i].f1 |= 1;
				icu->bezt[i].f2 |= 1;
				icu->bezt[i].f3 |= 1;
				
			}
		}
	}
}

void borderselect_icu_key(IpoCurve *icu, float xmin, float xmax, 
						  int (*select_function)(BezTriple *))
{
	/* Selects all bezier triples in the Ipocurve 
	 * between times xmin and xmax, using the selection
	 * function.
	 */

	int i;

	/* loop through all of the bezier triples in
	 * the Ipocurve -- if the triple occurs between
	 * times xmin and xmax then select it using the selection
	 * function
	 */
	for (i=0; i<icu->totvert; i++){
		if (icu->bezt[i].vec[1][0] > xmin && icu->bezt[i].vec[1][0] < xmax ){
			select_function(&(icu->bezt[i]));
		}
	}
}

void borderselect_ipo_key(Ipo *ipo, float xmin, float xmax, int selectmode)
{
	/* Selects all bezier triples in each Ipocurve of the
	 * Ipo between times xmin and xmax, using the selection mode.
	 */

	IpoCurve *icu;
	int (*select_function)(BezTriple *);

	/* If the ipo is no good then return */
	if (!ipo)
		return;

	/* Set the selection function based on the
	 * selection mode.
	 */
	switch(selectmode) {
	case SELECT_ADD:
		select_function = select_bezier_add;
		break;
	case SELECT_SUBTRACT:
		select_function = select_bezier_subtract;
		break;
	case SELECT_INVERT:
		select_function = select_bezier_invert;
		break;
	default:
		return;
	}

	/* loop through all of the bezier triples in all
	 * of the Ipocurves -- if the triple occurs between
	 * times xmin and xmax then select it using the selection
	 * function
	 */
	for (icu=ipo->curve.first; icu; icu=icu->next){
		borderselect_icu_key(icu, xmin, xmax, select_function);
	}
}

void select_ipo_key(Ipo *ipo, float selx, int selectmode)
{
	/* Selects all bezier triples in each Ipocurve of the
	 * Ipo at time selx, using the selection mode.
	 */
	int i;
	IpoCurve *icu;
	int (*select_function)(BezTriple *);

	/* If the ipo is no good then return */
	if (!ipo)
		return;

	/* Set the selection function based on the
	 * selection mode.
	 */
	switch(selectmode) {
	case SELECT_ADD:
		select_function = select_bezier_add;
		break;
	case SELECT_SUBTRACT:
		select_function = select_bezier_subtract;
		break;
	case SELECT_INVERT:
		select_function = select_bezier_invert;
		break;
	default:
		return;
	}

	/* loop through all of the bezier triples in all
	 * of the Ipocurves -- if the triple occurs at
	 * time selx then select it using the selection
	 * function
	 */
	for (icu=ipo->curve.first; icu; icu=icu->next){
		for (i=0; i<icu->totvert; i++){
			if (icu->bezt[i].vec[1][0]==selx){
				select_function(&(icu->bezt[i]));
			}
		}
	}
}

void select_icu_key(IpoCurve *icu, float selx, int selectmode)
{
    /* Selects all bezier triples in the Ipocurve
     * at time selx, using the selection mode.
     * This is kind of sloppy the obvious similarities
     * with the above function, forgive me ...
     */
    int i;
    int (*select_function)(BezTriple *);

    /* If the icu is no good then return */
    if (!icu)
        return;

    /* Set the selection function based on the
     * selection mode.
     */
    switch(selectmode) {
    case SELECT_ADD:
        select_function = select_bezier_add;
        break;
    case SELECT_SUBTRACT:
        select_function = select_bezier_subtract;
        break;
    case SELECT_INVERT:
        select_function = select_bezier_invert;
        break;
    default:
        return;
    }

    /* loop through all of the bezier triples in
     * the Ipocurve -- if the triple occurs at
     * time selx then select it using the selection
     * function
     */
    for (i=0; i<icu->totvert; i++){
        if (icu->bezt[i].vec[1][0]==selx){
            select_function(&(icu->bezt[i]));
        }
    }

}
