/* Copyright 1990-1992 Laboratoire de Recherche en Informatique (LRI)Permission to use, copy, and modify this software and its documentationfor your own purposes is hereby granted without fee, provided thatthe above copyright notice appear in all copies and that both thatcopyright notice and this permission notice appear in supportingdocumentation, and use acknowledge that the software was developedby Laboratoire de Recherche en Informatique, Universite de Paris-Sud,Orsay, France. LRI makes no representations about the suitability ofthis software for any purpose.  It is provided "as is" without expressor implied warranty.This software or modified versions of this software cannot bedistributed in source or binary form, nor included into productswithout prior written permission of the author.LRI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALLIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL LRIBE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGESWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTIONOF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.Author:   ___    0  Thomas Baudel                 e-mail: thomas@lri.fr  /   \  /   LRI - Bat 490 /  __/ /    Universite de Paris-Sud       voice : +33 (1) 69 41 69 10/__   \/     91 405 ORSAY Cedex - FRANCE   fax   : +33 (1) 69 41 65 86*//* INCLUDE  */# include <Types.h> 				/* Nearly always required */# include <QuickDraw.h> 			/* To access the qd globals */# include <ToolUtils.h> 			/* CursHandle and iBeamCursor */# include <Fonts.h> # include <Events.h>#include <CursorCtl.h># include <OSEvents.h># include <Errors.h># include <Files.h># include <String.h># include <Values.h># include <Printing.h># include <memory.h>			# include <Packages.h># include <Resources.h>	# include <windows.h># include <dialogs.h>				/* InitDialogs() and GetNewDialog() */# include <menus.h># include <math.h># include <SegLoad.h>	# include <OSUtils.h>	# include <Values.h>#include "main.h"#include "Gesture.h"extern "C" {#include <stdio.h>#include <string.h>#include <stdlib.h>#include "util.h"#include "bitvector.h"#include "matrix.h"#include "fv.h"#include "sc.h"}#define	AjoutChampId 	1#define		EnleveChampId	2#define ChampsId	4#define LargeurFen	700#define InfosDialogue	134#pragma segment Gestureinline int abs(int i) { return i>0 ? i : (-i) ; }FENETRE_SET :: FENETRE_SET (Str255 Titre, int ref): FENETRE (Titre, BaseId, false, false), NbChamps(0), CurClass  ((GEST_CLASS*) 0), CurItem (0){ 			short Test; 		LeFichier = ref; Test = GetVol(NULL, &LeVolume);		if (! LeFichier) {				FInfo finfo;				finfo.fdType='GEST'; finfo.fdCreator = 'GEST'; finfo.fdFlags =0;				CreateResFile (Titre); 				Test=SetFInfo (Titre, LeVolume, &finfo);				LeFichier = OpenResFile (Titre);if (ResError()) Debugger();		} else {			UseResFile (LeFichier); 				NbChamps = Count1Resources ('GEST');		}		ShowWindow((WindowPtr) &Fen);}FENETRE_SET :: ~FENETRE_SET (){	if (CurItem) CheckItem (LeMenu, CurItem, false);	for (short i  = 0; i < NbChamps; i++ ) DelMenuItem (LeMenu, ChampsId);	if (CurClass) delete CurClass; 	CloseResFile(LeFichier);	LeFichier =0;	if (ResError()) Debugger();}int FENETRE_SET :: Enregistre (int what) {	short f, id, Test;  	Str255 ch; 	SFTypeList tl; 	SFReply rep;	Point p;  	FInfo finfo; 	int i, n;	Handle h;	ResType tp;		if (CurClass) CurClass->Enregistre ();	if (Change) {				UpdateResFile(LeFichier);				Change = 0; 	} 	switch (what) {	case 0:  break;	case 1 :  tl[0] = 'STAK'; tl[1]='    '; tl[2]='    '; tl[3]='    ';								ch[0]=0; 								f = GetDocument (ch, (SFTypeList*) &tl, 1);								if (f <=0 && ch[0]) {									CreateResFile (ch);									f = OpenResFile (ch);								}								if (f) {										Analyse (f);										CloseResFile(f);								} 								break;		case 2 :  p.h = 60; p.v = 60;								SFPutFile (p, "\pNew gesture file name", Name(), NULL, &rep);								if (rep.good) {											if (! IUCompString (Name(), "\pUntitled")) {												SetVol (NULL, LeVolume);												Rename (Name(), LeVolume, rep.fName);												SetWTitle ((WindowPtr) &Fen, rep.fName);											} else {												if (CurClass) {														delete CurClass;														CurClass = 0;														if (CurItem) CheckItem (LeMenu, CurItem, false);														CurItem = 0;														SetPort ((GrafPtr) &Fen);														EraseRect (&Fen.port.portRect);												}												SetVol (NULL, rep.vRefNum);												LeVolume = rep.vRefNum;												finfo.fdType='GEST'; finfo.fdCreator = 'GEST'; finfo.fdFlags =0;												CreateResFile (rep.fName); 												Test=SetFInfo (rep.fName, LeVolume, &finfo);												UseResFile (LeFichier);												n = Count1Resources ('GEST');												f = OpenResFile (rep.fName);												for (i = 1 ; i <= n; i++) {													UseResFile (LeFichier);													h = Get1IndResource ('GEST', i);													GetResInfo (h, &id, &tp, ch);													DetachResource (h);													UseResFile (f);													AddResource (h, 'GEST', id, ch);													UpdateResFile (f);												}												SetWTitle ((WindowPtr) &Fen, rep.fName);												CloseResFile (LeFichier);												LeFichier = f;											}								}								break;		}	return 1; }int FENETRE_SET :: Dessine (RgnHandle r){ 	PenNormal();	if(! ImpressionCur) EraseRgn(r);	if (CurClass) return CurClass->Dessine (this);	else return MAXSHORT;}void FENETRE_SET :: ChangeChamp (Str255 Current, short  theItem){   		if (CurClass) { 				CheckItem (LeMenu, CurItem, false);				SetPort ((GrafPtr) &Fen);				EraseRect (&Fen.port.portRect);				delete CurClass;				UpdateResFile(LeFichier);	if (ResError()) Debugger();				CurClass = NULL;		}		if (Current) {			CurClass = new GEST_CLASS (this, GetNamedResource ('GEST', Current), Current);			CheckItem (LeMenu, theItem, true);			CurClass->Dessine (this);			CurItem = theItem;		} else {			Str255 buf;			buf[0] =0;			if (Demande ("\pEnter new Gesture Class Name", buf)) {				CurClass  = new GEST_CLASS (this, (Handle) 0, buf);				NbChamps++;				AppendMenu (LeMenu, buf);				theItem = CountMItems (LeMenu);				CheckItem (LeMenu, theItem, true);				CurItem = theItem;			}		}}void FENETRE_SET :: Active ( int Flag ){			Handle h; int j; ResType t; Str255 ch;			if (Flag) {						UseResFile(LeFichier);						while (CountMItems (LeMenu) >= ChampsId) DelMenuItem (LeMenu, ChampsId);						for (short i  = 1; i <= NbChamps; i++ ) {								h = Get1IndResource ('GEST', i);								GetResInfo (h, (short*) &j, &t, ch);								AppendMenu (LeMenu, ch);						} 						if (CurItem) CheckItem (LeMenu, CurItem, true);						InsertMenu (LeMenu, 0); DrawMenuBar();				} else {						if (CurItem) CheckItem (LeMenu, CurItem, false);						DeleteMenu (TypeFenetre); 						DrawMenuBar();				} }void FENETRE_SET :: Oisif (Boolean front) { 	// if (front && CurClass) CurClass->Oisif(this); }void FENETRE_SET :: Touche (EventRecord* e) { 		if (CurClass) { CurClass->Touche (this, e); }		Change = 1;}void FENETRE_SET :: Clique (EventRecord* e) { 		if (CurClass) { CurClass->Clique (this, e); }		Change = 1;}void FENETRE_SET :: MenuEdition (short) { 		Alerte ("Not implemented");}void FENETRE_SET :: Infos () { 	sClassifier sc;	Handle h;	UseResFile(LeFichier);	h = Get1IndResource ('GEsT', 1);	if (h) {		HLock (h);		sc = sLoad (h);		ReleaseResource (h);		if (sc) sFreeClassifier (sc);		Alerte("Everything'sOK");	} else Debugger();		/* 	GrafPtr 	savePort; DialogPtr	theDialog; short itemHit, Test; Handle h; Rect r;	Str255 chaine; FInfo finfo; 		GetPort(&savePort);	theDialog = GetNewDialog(InfosDialogue, nil, (WindowPtr) -1); 	ShowWindow(theDialog); SetPort(theDialog);		do { ModalDialog(nil, &itemHit); } while (itemHit > 2) ;	if (itemHit==1) {	}	CloseDialog(theDialog); SetPort(savePort);	*/}void FENETRE_SET :: Analyse (int f) { 		sClassifier sc = sNewClassifier ();		FV fv = FvAlloc();		Handle res, h;		char curname[255];		Str255 ch; int j; ResType t;		Vector curvect;				UseResFile (LeFichier);		InitCursorCtl(NULL); 		for (int i = 0; i < NbChamps; i++) {					h = Get1IndResource ('GEST',  i+1);				if (! h) Debugger();					GetResInfo (h, (short*) &j, &t, ch);					ChangeChamp (ch, (short) i + 4); // *** i + 4 not clean 					GEST_EXAMPLE* ge;					for (j =0; j < ch[0]; j++) curname[j] = ch[j+1]; curname[j]  = 0;					for (ge= CurClass->Examples ; ge; ge=ge->Suivant) {							POINT_LIST* curp;							SpinCursor(16);							FvInit (fv);							for (curp = ge->Points; curp ; curp = curp->Suivant)									FvAddPoint (fv, curp->p.h, curp->p.v, curp->tc);							SpinCursor(16);							curvect = FvCalc (fv);							sAddExample (sc, curname, curvect);					}		}		sDoneAdding (sc);		SpinCursor (16);		res = sCompile (sc);		SpinCursor (16);		if (res) {				UseResFile (f);				h= GetNamedResource('GEsT', Name ());				if (h) RmveResource (h);				AddResource (res, 'GEsT', UniqueID ('GEsT'), Name ());		}		Show_Cursor (ARROW_CURSOR); CurCursor=ARROW_CURSOR;		FvFree(fv);		sFreeClassifier (sc);}void FENETRE_SET :: RemoveCurrent (){	if (CurClass) {		CheckItem (LeMenu, CurItem, false);		DelMenuItem (LeMenu, CurItem);		SetPort ((GrafPtr) &Fen);		EraseRect (&Fen.port.portRect);		RmveResource (CurClass->Res);		CurClass->Res = NULL;		Change = 1;		delete CurClass;		CurClass = NULL;		NbChamps--;	}}		void FENETRE_SET :: Menu (int theItem) {		Str255 ch; 		if (LeFichier) UseResFile (LeFichier);		switch (theItem) {			case 0: return;			case AjoutChampId : ChangeChamp ((Str255) NULL, 0); break;			case EnleveChampId : RemoveCurrent (); break;			default : 				GetItem (LeMenu, theItem, ch);				ChangeChamp (ch, theItem);		}} GEST_CLASS :: GEST_CLASS (FENETRE_SET* Doc, Handle h, Str255 name) : Res(h), Examples(NULL), Current(NULL), nExamples(0){		for (int i  =0; i <= name[0] ; i++) Nom[i] = name[i];		if (Res) {			int offset=1;			nExamples = **((short**) Res);			for (int i = 0; i < nExamples; i++) {					Examples = new GEST_EXAMPLE (this, h, &offset);			}		} else {				UseResFile (Doc->LeFichier);				Res = NewHandle (sizeof(short));				**((short**) Res) = 0;				AddResource (Res, 'GEST', UniqueID ('GEST'), name);				UpdateResFile(Doc->LeFichier);		}}			GEST_CLASS :: ~GEST_CLASS () { 			if (Nom[0]) Enregistre ();			GEST_EXAMPLE* tamp = Examples;			while (tamp) {						tamp = Examples->Suivant;						delete Examples;if (MemError()) Debugger();						Examples = tamp;				}}void  GEST_CLASS :: Name (Str255 name){		if (Res) {				short i; ResType t;				GetResInfo (Res, &i, &t, name);		}}int GEST_CLASS :: Dessine (FENETRE_SET* Doc) { 					GEST_EXAMPLE* tamp = Examples;					while (tamp) {						tamp->Dessine (Doc, (tamp == Current));						tamp=tamp->Suivant;					}					return MAXSHORT;}void GEST_CLASS :: Touche (FENETRE_SET* Doc, EventRecord* e) { 		if (Current && ((e->message & 0x000000FF) == 8)) { 			delete Current;			nExamples--;			SetPort ((GrafPtr) &(Doc->Fen));			EraseRect (&(Doc->Fen.port.portRect));			Dessine (Doc);			Current = 0;		}}void GEST_CLASS :: Clique (FENETRE_SET* Doc, EventRecord* e)  { 		GEST_EXAMPLE* tamp;		if (tamp = TrouveExemple(Doc, e->where)) {		 	if (Current) Current->Dessine(Doc, false);			Current = tamp;			Current->Dessine(Doc, true);		} else {			 if (Current) Current->Dessine(Doc, false);			nExamples++;			Current = new GEST_EXAMPLE (this, NULL, 0);		}}void GEST_CLASS :: MenuEdition (FENETRE_SET*, short) { Alerte("Not Implemented"); }void GEST_CLASS :: Enregistre () { 	if (Res) {			**((short**) Res) = nExamples;			HUnlock(Res);			SetHandleSize (Res, sizeof (short));			GEST_EXAMPLE* tamp = Examples;			while (tamp) {						tamp->Enregistre (Res);if (MemError()) Debugger();						tamp=tamp->Suivant;			}			ChangedResource (Res);if (ResError()) Debugger();		}}GEST_EXAMPLE*GEST_CLASS :: TrouveExemple (FENETRE_SET*, Point p) {					GEST_EXAMPLE* tamp = Examples;					while (tamp) {						if (tamp->OnLine (p)) return tamp;						tamp=tamp->Suivant;					}					return NULL;}GEST_EXAMPLE :: GEST_EXAMPLE (GEST_CLASS* c, Handle h , int* offset){	POINT_LIST* tamp;	Point p, oldp;	short tc;	long tc2;	Parent = c; Length = 0; Points = (POINT_LIST*) NULL;	Parent->Current = this;	Suivant = Parent->Examples;	Parent->Examples = this;	if (h) {			// read from handle at offset			tamp = (POINT_LIST*) NULL;			Length = (*((short**) h))[*offset];			for (int i = 0; i < Length; i++) {				p.h = (*((short**) h))[*offset + 3*i +1];				p.v = (*((short**) h))[*offset + 3*i +2];				tc = (*((short**) h))[*offset + 3*i +3];				tamp = new POINT_LIST (tamp, (POINT_LIST*) NULL, p, tc);				if (i == 0) Points = tamp;			}			*offset += 3 * Length + 1;	} else {				oldp.h = 0; oldp.v = 0;				GetMouse(&p);				MoveTo (p.h, p.v);				if (Environnement.hasColorQD) {						RGBColor rgb;						rgb.red = 0xDD6B; rgb.green = 0x08C2; rgb.blue = 0x06A2;						RGBForeColor (&rgb);				} else PenPat(qd.gray);				tc2 = TickCount();				Points = new POINT_LIST ((POINT_LIST*) NULL, (POINT_LIST*) NULL, p, 0);				tamp = Points;				Length = 1;				while (StillDown ()) {					GetMouse(&p);					if ( (abs(p.v - oldp.v) > 2) || (abs(p.h - oldp.h) > 2)) {							tamp = new POINT_LIST (tamp, (POINT_LIST*) NULL, p, (short) (TickCount() - tc2));							LineTo (p.h, p.v);							Length++;							oldp =p;					}			}	}}GEST_EXAMPLE :: ~GEST_EXAMPLE (){			POINT_LIST* tamp = Points;			while (tamp) {					tamp = tamp->Suivant;					delete Points;					Points = tamp;			}			if (Parent->Examples == this) Parent->Examples = Suivant;			else {				GEST_EXAMPLE* ex = Parent->Examples;				while (ex  && ex->Suivant != this) ex=ex->Suivant;				if (ex) ex->Suivant = Suivant;			}}Boolean isonline (const Point& p, const Point& p1, const Point& p2){const int eps = 2;	register long dx  = p2.h - p1.h;	register long dy  = p2.v - p1.v;	register long N = dx*dx + dy*dy;	register long dxC = p.h - p1.h;	register long dyC = p.v - p1.v;	register long d = dy*dxC - dx*dyC;		if (d < 0) d = -d;		/* avoid overflow : this is clearly a hack ! */	/* works if N*eps*eps does not overflow */	if (d > (1 << 15)) return 0;		/* check in circle centered in p1+p2 / 2 */	dxC -= dx/2;	dyC -= dy/2;	if (dxC*dxC + dyC*dyC >= N/4) return 0;		/* check if distance less than eps */	return (d*d <= N * ((long) (eps*eps))) ? 1 : 0;}Boolean GEST_EXAMPLE :: OnLine (Point p){		if (Points) {			POINT_LIST* p1 = Points;			POINT_LIST* p2 = Points->Suivant;			while (p1 && p2) {				if (isonline (p, p1->p, p2->p))  return true;				p2=p2->Suivant;				p1=p1->Suivant;			}		}		return false; }void GEST_EXAMPLE :: Dessine (FENETRE_SET*, Boolean hilite){		if (Points) {				POINT_LIST* tamp = Points->Suivant;				if (hilite) {					if (Environnement.hasColorQD) {						RGBColor rgb;						rgb.red = 0xDD6B; rgb.green = 0x08C2; rgb.blue = 0x06A2;						RGBForeColor (&rgb);					} else PenPat(qd.gray);				} else {					if (Environnement.hasColorQD) {						RGBColor rgb;						rgb.red = 0; rgb.green = 0; rgb.blue = 0;						RGBForeColor (&rgb);					}					PenNormal ();				}				MoveTo (Points->p.h, Points->p.v);				while (tamp) {					LineTo (tamp->p.h, tamp->p.v);					tamp = tamp->Suivant;					}		}}void GEST_EXAMPLE :: Enregistre (Handle h){	POINT_LIST* tamp = Points;	long offset = GetHandleSize (h);if (MemError()) Debugger();	SetHandleSize (h, offset + (Length * 3 * sizeof (short) + sizeof (short)));if (MemError()) Debugger();	offset /= sizeof(short);	(*((short**) h))[offset] = Length;	for (int i = 0; i < Length && tamp; i++) {			(*((short**) h))[offset + 3*i +1] = tamp->p.h;			(*((short**) h))[offset + 3*i +2] = tamp->p.v;			(*((short**) h))[offset + 3*i +3] = tamp->tc;			tamp = tamp->Suivant;	}}POINT_LIST ::	POINT_LIST (POINT_LIST* prec, POINT_LIST* sui, Point pl, short i): p(pl), Precedent(prec), Suivant (sui), tc (i){	if (Precedent) Precedent->Suivant = this;	if (Suivant) Suivant->Precedent = this;}POINT_LIST :: 	~POINT_LIST (){		if (Precedent) Precedent->Suivant = Suivant;		if (Suivant) Suivant->Precedent = Precedent;}
