// Version 1.00 22/05/97  First release.


int speech_handle;
int speech_asyoutype;
int speech_carettracking;
int speech_speaking;
int speech_file;
int speech_docclosed;
string speech_choicespath;


// write 'speech-as-you-type' choices

void speech_writechoices(void)
{
 int h;

 h = fileopen(speech_choicespath, "wb");
 if(h)
 {
  fileputc(speech_asyoutype, h);
  fileclose(h);
 }
}


// register/de-register Speak module

void speech_register(int user)
{
 swi13(0x4ad82, user, 0, 0);
 if(user == 2)
  speech_writechoices();
}


// return true if key is a terminating character

int speech_term(int key)
{
 if ((key>=' ' && key<='&') || (key>='(' && key<='/') || (key>=':' && key<='@') || (key>='[' && key<='`') || (key>='{' && key<='&') || (key>='~' && key<='&') || (key>=128 && key<=144) || (key>=146 && key<=157) || (key>=160 && key<=191))
  return(1);
 return(0);
}


// if character typed is a word terminator, speak previous word 

int speech_fix(int user, int view, int key)
{
 int b, l;
 string s;

 if(activetype(TEXTFRAME) <= 1)
  return(key);

 if(speech_speaking)
 {
  speech_carettracking = 0;
  return(key);
 }

 // check if key is a terminator
 if (speech_term(key) || key == 13 || key == 394 || key == 2061 || key == 6669)
 {
  b = bmcreate("speech_b");
  setbmtocaret(b);

  while(bmprevchar(b) > 32 && bmprevchar(b) < 256 && !speech_term(bmprevchar(b)))
  {
   bmmove(b, 0, 0);
   ++l;
  }

  for(; l > 0; --l)
  {
   s += chars(bmchar(b));
   bmmove(b, 1, 0);
  }
  bmdelete(b);
  s = s + chars(key);
  if(slen(s) > 1)
   osclis("say " + s);
 }
 return(key);
}


// set/unset 'speech-as-you-type'

void speech_settype(int i)
{
 if(i)  
  addeventhandler(0x300, 0, "speech_fix");
 else
  remeventhandler(0x300, 0, "speech_fix");

 speech_asyoutype = i;
}


// read and 'speech-as-you-type' choices

void speech_readchoices(void)
{
 int h;

 h = fileopen(speech_choicespath, "rb");
 if(h)
 {
  speech_settype(filegetc(h));
  fileclose(h);
 }
 else
  speech_settype(1);
}


// return TRUE if next character is alphanumeric

int speech_isalnum(int b)
{
 int c;

 c = bmchar(b);
 if(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9')
  return(1);
 return(0);
}


// stop speech

void speech_stop(void)
{
 swi13(0x4ad85, 0, 0, 0);
 speech_speaking = 0;
}


// speak entire story, or story from caret

void speech_story(int start)
{
 int b1, b2, c, i, p;
 string s;
 string m = ",.?!:;";

 if(activetype(TEXTFRAME) <= 1)
  return;

 speech_file = currentfile();

 speech_carettracking = 1;
 speech_speaking = 1;
 speech_docclosed = 0;

 b1 = bmcreate("speech_b1");
 b2 = bmcreate("speech_b2");
 setbmtocaret(b1);  
 if (start == 1)
  bmmove(b1, 0, 4);

 while(bmchar(b1))
 {
  s = "";

  // strip leading spaces and hard spaces
  while(bmchar(b1) == ' ' || bmchar(b1) == 160)
   bmmove(b1, 1, 0);

  // position caret at start of clause
  setbmtobm(b2, b1);
  if(speech_carettracking)
   setcarettobm(b2);

  // read a clause, then speak it
  while(c = bmchar(b1))
  {
   bmmove(b1, 1, 0);
   s += chars(c);
   if(m / chars(c) >= 0 && !speech_isalnum(b1) && c != bmchar(b1))
    break;
   if(c == ' ' && slen(s) > 200)
    break;
   if(c == '\n' || c == 13 || slen(s) == 250)
    break;
  }
  osclis("say " + s);

  // while clause is being spoken, update caret to start of each word
  p = 0;
  while(swin4(0, 0x4ad80, 0, 0, 0, 0) == 0 && speech_speaking)
  {
   c = swin4(1, 0x4ad80, 0, 0, 0, 0);
   if(c != p)
   {
    for(i = 0; i < c - p; ++i)
     bmmove(b2, 1, 0);
    if(speech_carettracking)
     setcarettobm(b2);
    p = c;
   }
   pause(1);
  }
  if(!speech_speaking)
   break;
 }
 if(!speech_docclosed)
 {
  bmdelete(b1);
  bmdelete(b2);
 }
 speech_stop();
}



// switch off 'caret tracking' when the mouse is clicked in document

int speech_click(int user, int view, int mousex, int mousey, int mousebuttons)
{
 int b3, b4;
 string s;

 if(mousebuttons != 2 && speech_speaking && speech_file == filefromview(view))
  speech_carettracking = 0;
/* else
 if(mousebuttons != 2 && !speech_speaking && speech_asyoutype)
 {
  b3 = bmcreate("speech_b3");
  b4 = bmcreate("speech_b4");
  setbmtocaret(b3);
  bmmove(b3, 0, 1);
  setbmtocaret(b4);
  bmmove(b4, 1, 1);
  s = "";
  while(bmcmp(b3, b4))
  {
   s = s + chars(bmchar(b3));
   bmmove(b3, 1, 0);
  }
 osclis("say " + s);
 bmdelete(b3);
 bmdelete(b4);
 }*/
 return(mousebuttons);
}


// stop speech if user clicks in another file

void speech_gaincaret(int user, int view, int gain)
{
 if(gain && speech_speaking && speech_file != filefromview(view))
  speech_stop(); 
}


// stop speech if current view is closed

void speech_viewclose(int user, int view)
{
 if(speech_speaking && speech_file == filefromview(view))
 {
  speech_stop();
  speech_docclosed = 1;
 }
}


// deal with 'Speech' menu entries

int speech_entry(int entry, int subcode)
{
 switch(entry)
 {
  case 0:
         speech_story(1);
         break;
  case 1:
         speech_story(0);
         break;
  case 2:
         swi13(0x4ad85, 0, 0, 0);
         speech_speaking = 0;
         break;
  case 3:
         speech_carettracking = !speech_carettracking;
         break;
  case 4:
         speech_settype(!speech_asyoutype);
         break;
 }
 return(0);
}


// tick/un-tick 'Speech' menu entries

int speech_flags(int entry, string &text)
{
 switch(entry)
 {
  case 0:
         return(speech_speaking ? SHADED : 0);
         break;
  case 1:
         return(speech_speaking ? SHADED : 0);
         break;
  case 2:
         return(speech_speaking ? 0 : SHADED);         
         break;
  case 3:
         return(speech_speaking ? speech_carettracking : SHADED);
         break;
  case 4:
         return(speech_asyoutype);
         break;
 }
 return(0);
}


int speech_menu(int open)
{
 return(speech_handle);
}


// add 'Speech' entry to 'Applets' menu

void main(void)
{
 string s;
 string help1 = "{SPEECH_06}";
 string help2 = "{SPEECH_07}";

 s = "RMEnsure Speak 1.20";
 if(osclis(s))
  osclis("RMLoad <Speak$Dir>.speakmod");
 s = "RMEnsure Speak 1.20";
 if(osclis(s))
  errorbox("Ovation Pro !Speech applet needs Speak module.");

 speech_register(1);
 script_menu_initialise();

 speech_choicespath = "OvationPro$AppletsDir";
 getenvs(speech_choicespath);
 speech_choicespath += ".!Speech.Choices";

 speech_handle = create_menu("{SPEECH_00}");
 addentry_menu(speech_handle,"speech_entry","speech_flags","","","{SPEECH_01}");
 addentry_menu(speech_handle,"speech_entry","speech_flags","","","{SPEECH_02}");
 addentry_menu(speech_handle,"speech_entry","speech_flags","","","{SPEECH_03}");
 addentry_menu(speech_handle,"speech_entry","speech_flags","","","{SPEECH_04}");
 addentry_menu(speech_handle,"speech_entry","speech_flags","","","{SPEECH_05}");

 addentry_menu(script_handle,"","","speech_menu","","{SPEECH_00}");
 speech_readchoices();

 addeventhandler(0x004, 2, "speech_register");
 addeventhandler(0x213, 0, "speech_click");
 addeventhandler(0x006, 0, "speech_viewclose");
 addeventhandler(0x215, 0, "speech_gaincaret");
 
 if(isdefmacro("startspeech") == -1)
 {
  translate(help1);
  defmacro2(0,"startspeech","{if(activetype(TEXTFRAME)>1) speech_story(1);else bbc_vdu(7);}","startspeech",help1);
 }

 if(isdefmacro("stopspeech") == -1)
 {
  translate(help2);
  defmacro2(0,"stopspeech","{if(speech_speaking) speech_stop();else bbc_vdu(7);}","stopspeech",help2);
 }
}
