Logo Search packages:      
Sourcecode: kdrill version File versions  Download package

search.c

/* I'm changing this so that only the actual SEARCH routines are in here. 
 * widget creation has been moved to "searchwidgets.c"
 */

#include <stdio.h>

#include <Xos.h>
#include <Intrinsic.h>
#include <StringDefs.h>

#include <Shell.h>



#include "defs.h"
#include "externs.h"
#include "game.h"
#include "widgets.h"
#include "search.h"
#include "convert.h"
#include "multikanji.h"
#include "strokesearch.h"
#include "readfile.h"
#include "utils.h"
#include "log.h"

#include "searchwidgets.h"


TRANSLATION lastsearch=NULL;  /* holds index of last kanji searched */

/* called from DoStrokeSearch()
 * Go look for all kanji with this strokecount.
 * 
 */
void dostrokefind(BYTE strokecount, BITS16 skipval)
{
      short skipmask=skipfromthree(3,0,0);
      TRANSLATION searchtarget = NULL;
      TRANSLATION firstmatch = NULL;
      int searchndx=lowestkanji;

      if(strokecount==0)
            return;

#ifdef DEBUG
      printf("searching for strokecount %d\n",strokecount);
#endif

      setstatus("Searching...");

      ClearAllMulti();

      for(searchndx=lowestkanji; searchndx <=highestkanji; searchndx++){
            searchtarget=translations[searchndx];
            if(searchtarget==NULL) continue;

            if(searchtarget->Strokecount != strokecount) {
                  continue;
            }

            if(skipval!=0)
                  if((searchtarget->Sindex & skipmask) != skipval){
                        continue;
                  }

            if(match_onlyactive){
                  if(!UseThisKanji(searchndx)) {
                        continue;
                  }
            }
            
            AddMultiTranslation(searchtarget);
            if(firstmatch==NULL)
                  firstmatch=searchtarget;

      }

      if(firstmatch!=NULL){
            setstatus("Search complete");
            if(getMultiCount() >1){
                  ShowMulti();
            }
      } else{
            setstatus("No match found");
            Beep();
      }
      
}

/* called by doenglishfind, if we need to restrict search to
 * "beginning of word only"
 * return 1 on match, 0 on not.
 */
int matchengfirst(char *full, char *frag){
      int len=strlen(frag);
      while(1){
            if(strncmp(full,frag,len) ==0){
                  return 1;
            }
            full=strchr(full,':'); /* actually, " : " */
            if(full==NULL){
                  return 0;
            }
            full+=1;
      }
      return 0;
}

/* doenglishfind:
 *    Assume what we want to look for is in the search ENGLISH INPUT widget.
 *    First, read in that value from the widget
 *    (okay, this is not modular :-)
 *
 *    Then go through the whole dictionary looking for matches,
 *    and adding any matches to the multi window.
 *    We show the multiwindow, if appropriate
 *    We return the first match, if any.
 *    But we do not set the main search window, for some reason.
 */
TRANSLATION doenglishfind()
{
      String str;
      TRANSLATION searchtarget = NULL;
      TRANSLATION firstmatch = NULL;
      int searchndx=lowestkanji;

      setstatus("Searching...");

      XtVaGetValues(searchwidgets[SEARCH_ENG_W],XtNstring,&str,NULL);

      if((str[0] == '\0') || (str[0] == '\n')){
            return NULL;
      }

      ClearAllMulti();
      searchtarget=translations[lowestkanji];



      for(searchndx=lowestkanji;searchndx<=highestkanji;searchndx++){
            searchtarget=translations[searchndx];
            if(searchtarget==NULL) continue;

            if(match_onlyatstart){
                  if(matchengfirst(searchtarget->english,str) != 1){
                        continue;
                  }
            } else {
                  if(strstr(searchtarget->english,str) == NULL){
                        continue;
                  }
            }

            if(match_onlyactive){
                  if(!UseThisKanji(searchndx)){
                        continue;
                  }
            }
                  
            AddMultiTranslation(searchtarget);
            if(firstmatch==NULL){
                  firstmatch=searchtarget;
            }
      }



      if(firstmatch!=NULL){
            setstatus("Search complete");
            if(getMultiCount() >1){
                  ShowMulti();
            }
      } else{
            setstatus("No match found");
            Beep();
      }

      return firstmatch;
      
}

/********************************************************************
 * suite of really stupid-looking routines, so I can do INTELLIGENT *
 * generic-comparisons in DoFind, and posibly elsewhere             *
 ********************************************************************/

Boolean compareUnicode(TRANSLATION entry, int compvalue){
      if(entry->Uindex==compvalue) return True;
      return False;
}
Boolean compareHalpern(TRANSLATION entry, int compvalue){
      if(entry->Hindex==compvalue) return True;
      return False;
}
Boolean compareNelson(TRANSLATION entry, int compvalue){
      if(entry->Nindex==compvalue) return True;
      return False;
}
Boolean compareFreq(TRANSLATION entry, int compvalue){
      if(entry->frequency==compvalue) return True;
      return False;
}

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


/* DoFind
 *   (do-find)accelerator hook
 *   This handles callbacks for multiple widgets.
 *   Basically, any inputfield on the Search window, that uses
 *   [ENTER] as a trigger for a search.
 *   Except for the kanasearch
 */
void 
DoFind(Widget w,XEvent *event,String *params,Cardinal *num_parags){
      TRANSLATION findtarget = NULL;
      int targetval=0, retval=0, ndx=0;
      Boolean (* comparefunc)(TRANSLATION,int);

      setstatus("Searching...");

      if(w == searchwidgets[SEARCH_ENG_W]){
            findtarget = doenglishfind();

            if(findtarget == NULL){
                  setstatus("No match found");
                  Beep();
                  printsearch(lastsearch);
            } else {
                  printsearch(findtarget);
            }
            return;
      }

      /* Special case: not a "find this index", but a
       * "find me this index or greater"
       */
      if (w == searchnumbers[POUND_INPUT]){
            int number;
            /* want closest index match */
            number = GetWidgetHexval(w);

            if((number <lowestkanji) || (number> highestkanji)){
                  setstatus("input out of dict. range");
                  Beep();
                  printsearch(lastsearch);
                  return;
            } 

            if(number > highestkanji){
                  number = highestkanji;
            } else if (number <lowestkanji){
                  number = lowestkanji;
            } else {
                  /* find closest non-blank */
                  while(translations[number] == NULL){
                        number++;
                        /* this should never be triggered, but... */
                        if(number >highestkanji)
                              number = lowestkanji;
                  }
            }

            findtarget = translations[number];

            printsearch(findtarget);

            setstatus("Search complete");
            return;
      }


      if(w==searchnumbers[U_INPUT]) {
            targetval=GetWidgetHexval(w);
      } else {
            targetval=GetWidgetNumberval(w);
      }

      if(w==searchnumbers[U_INPUT]){
            comparefunc=compareUnicode;
      } else if (w == searchnumbers[F_INPUT]) {
            comparefunc=compareFreq;
      } else if (w == searchnumbers[H_INPUT]) {
            comparefunc=compareHalpern;
      } else if (w == searchnumbers[N_INPUT]) {
            comparefunc=compareNelson;
      } else {
            puts("DEBUG: ERROR: unrecognized compare type in DoFind");
            return ;
      }

      for(ndx=lowestkanji;ndx<highestkanji;ndx++){
            if(translations[ndx]==NULL)
                  continue;
            if(comparefunc(translations[ndx],targetval)){
                  retval=ndx;
                  break;
            }
      }


      if(retval!=0){
            findtarget = translations[retval];
            printsearch(findtarget);
            setstatus("Displaying match");
      } else {
            setstatus("No match found");
            Beep();
      }

      

}



/* Finds a match for single UNICODE-encoded widechar.
 * Returns pointer to translation struct, or NULL if no match.
 */
TRANSLATION dounicodefind(XChar2b *target)
{
      /* The guts of this are modeled after the special-case for xU,
       * in DoFind()
       */

      int ndx, retval=0;
      int targetval=(target->byte1 <<8) | target->byte2;

      for(ndx=lowestkanji;ndx<highestkanji;ndx++){
            if(translations[ndx]==NULL)
                  continue;
            if(compareUnicode(translations[ndx],targetval)){
                  retval=ndx;
                  break;
            }
      }
      if(retval!=0){
            return translations[retval];
      } else {
            return NULL;
      }
}


/* pass in unicode string.
 * look for match. display if found.
 * Fancy stuff;
 *   If single-char, just looks it up in kanjidic.
 *
 *   If multi-char, then attempts to look up each char in kanjidic,
 *   to convert unicode to JIS, THEN does search on JIS string. Wheee!
 */
void findunicodestring(XChar2b *target)
{
      XChar2b jisstring[5];
      TRANSLATION umatch;
      bzero(jisstring, sizeof(XChar2b) * 5);

      if(target==NULL) return;

      umatch=dounicodefind(target);

      if(umatch==NULL){
            setstatus("No match found");
            Beep();
            return;
      }

      /* only a single-char long */
      if(target[1].byte1==0){
            printsearch(umatch);
            setstatus("Displaying match");
            return;
      }

      jisstring[0].byte1=umatch->kanji->byte1;
      jisstring[0].byte2=umatch->kanji->byte2;

      umatch=dounicodefind(&target[1]);
      if(umatch==NULL){
            /* allow for a bit of mouse overswipe on a line */
            /* setstatus("Displaying fragment match"); */
            findkanjiall(jisstring);
            return;
      }
      jisstring[1].byte1=umatch->kanji->byte1;
      jisstring[1].byte2=umatch->kanji->byte2;

      if(target[2].byte1){
            umatch=dounicodefind(&target[2]);
            if(umatch!=NULL){
                  jisstring[2].byte1=umatch->kanji->byte1;
                  jisstring[2].byte2=umatch->kanji->byte2;
            }
      }

      findkanjiall(jisstring);
      
}


/* if fragment matches FIRST chars of fullstring, return 1.
 * Otherwise, return  0
 *   Allow hiragana/kanakana folding.
 */
int matchkanafirst(XChar2b *fullstring, XChar2b *fragment)
{
      while(1){
            if(fragment->byte1==0)
                  return 1;

            /* no need to test for fullstring->byte1==0,
             * it will just fail match :-)
             * Although if we were nice, we could return a value
             * saying the thing is too short
             */

            /* theres almost no point to this. sigh.*/
            if(fullstring->byte1 != fragment->byte1){
                  if((fullstring->byte1 != 0x24) &&
                     (fullstring->byte1 != 0x25))
                        return 0;
                  if((fragment->byte1 != 0x24) &&
                     (fragment->byte1 != 0x25))
                        return 0;
            }

            if(fullstring->byte2 != fragment->byte2)
                  return 0;

            fragment++;
            fullstring++;
      }
}

/* match XChar2b
 *    Attempt to find occurence of fragment in xchar2b string
 *    return 1 on match, 0 on fail.
 *
 *    Note that we try to allow match of kanagana, by hiragana.
 *    If match_onlyatstart!=0, only match if start of a kana phrase
 *    matches fragment
 */
int matchkana(XChar2b *fullstring, XChar2b *fragment)
{

      if(match_onlyatstart){
            while(fullstring->byte1 != 0){
                  if(matchkanafirst(fullstring,fragment))
                        return 1;

                  /* Oh well.... Find start of next "word".
                   * First skip over "current" word, then
                   * then look for valid kanji char.
                   * Remember, we need to skip space, AND things
                   * like parenthesis
                   */

                  /* Look for space, explicitly */
                  while(fullstring->byte1 != 0){
                        if((fullstring->byte1==0x21) &&
                           (fullstring->byte2==0x21)){
                              break;
                        }
                        fullstring++;
                  }
                  /* now have either space, or end-of-string */
                  /* so look for begining of word */
                  while(fullstring->byte1 < 0x24){
                        if(fullstring->byte1 ==0)
                              break;
                        fullstring++;
                  }
                  
                  if(fullstring->byte1 ==0){
                        /* oh well, end of string hit */
                        break;
                  }

                  /* We must be in the kana-and-above range now.
                   * just loop and search again!
                   */
            }

            return 0;
      }

      /* else */

      while(fullstring->byte1 != 0){
            if(matchkanafirst(fullstring,fragment))
                  return 1;
            fullstring++;

      }
      return 0;

}


/*
 *    Equivalent to matchkana!
 *    return 1 if match, 0 if no match
 *
 *    Note that this takes intoaccount 'match_onlyatstart'
 *    Which will presumably only match the kanji if at beginning
 *    of the phrase. Hmmm.
 */
int matchkanji(XChar2b *fullstring, XChar2b *fragment)
{
      return matchkana(fullstring,fragment);
}

/*
 * a kanji equivalent to strchr()
 * Returns pointer to first place the single widechar kchar is found in
 * kanji string, or NULL if no match
 */
XChar2b * kstrchr(XChar2b *string, XChar2b kchar)
{
      while(string->byte1 !=0){
            if((kchar.byte1==string->byte1) &&
               (kchar.byte2==string->byte2))
            {
                  return string;
            }
            string++;
      }

      return NULL;
}



/* dokanafind()
 *    find match to XChar2b string in kana translations
 */

void dokanafind(XChar2b *target)
{
      TRANSLATION firstmatch = NULL;
      TRANSLATION searchtarget = NULL;
      int searchndx=lowestkanji;

      setstatus("Searching...");

      searchtarget=translations[lowestkanji];
      ClearAllMulti();

      for(searchndx=lowestkanji; searchndx <=highestkanji; searchndx++){
      
            searchtarget=translations[searchndx];
            if(searchtarget==NULL) continue;
            if(match_onlyactive){
                  if(!UseThisKanji(searchndx)) {
                        continue;
                  }
            }

            if(matchkana(searchtarget->pronunciation, target)){
                  AddMultiTranslation(searchtarget);
                  if(firstmatch==NULL)
                        firstmatch=searchtarget;
            }
      }

      if(firstmatch!=NULL){
            printsearch(firstmatch);
            setstatus("Search complete");
            if(getMultiCount() >1){
                  ShowMulti();
            }
      } else{
            setstatus("No match found");
            Beep();
      }

      return;
}


/* This is here, because I want to preserve all "search" type
 * routines together in this file.
 * But it is called by Handle_showusefile
 *
 * The purpose is to let the user easily see which chars are in the
 * usefile, and easily remove them. They already have an easy way
 * to ADD them, via the search window :-)
 */
void dousefilefind()
{
      /*int kcount=getMultiMax();*/
      int kindex=lowestkanji;

      ClearAllMulti();
      for(kindex=lowestkanji; kindex<=highestkanji;kindex++){
            if(InUsefile(kindex)){
                  AddMultiTranslation(translations[kindex]);

                  /*
                   * let the multi-window complain about
                   * truncation, if appropriate
                  if(--kcount <=0){
                        return;
                  }
                  */
            }
      }

      ShowMulti();
}

/* dokanjifind()
 *    find match to "Four Corner" encoding in kanjidic translations
 */

void dokanjifind(int target)
{
      TRANSLATION firstmatch = NULL;
      TRANSLATION searchtarget = NULL;
      int searchndx=lowestkanji;

      setstatus("Searching...");

      ClearAllMulti();

      for(searchndx=lowestkanji; searchndx <=highestkanji; searchndx++){
            searchtarget=translations[searchndx];
            if(searchtarget==NULL) continue;

            if(searchtarget->Qindex == target){
                  if(match_onlyactive){
                        if(!UseThisKanji(searchndx)) {
                              continue;
                        }
                  }

                  AddMultiTranslation(searchtarget);
                  if(firstmatch==NULL)
                        firstmatch=searchtarget;
            }

      }

      if(firstmatch!=NULL){
            printsearch(firstmatch);
            setstatus("Search complete");
            if(getMultiCount() >1){
                  ShowMulti();
            }
      } else{
            setstatus("No match found");
            Beep();
      }

      return;
}

/* doskipfind()
 *    find match to "SKIP" encoding in kanjidic translations
 *    Indentical to dokanjifind, except look at Qindex instead of Sindex
 */

void doskipfind(int target)
{
      TRANSLATION firstmatch = NULL;
      TRANSLATION searchtarget = NULL;
      int searchndx=lowestkanji;

      setstatus("Searching...");

      ClearAllMulti();

      for(searchndx=lowestkanji; searchndx <=highestkanji; searchndx++){
            searchtarget=translations[searchndx];
            if(searchtarget==NULL) continue;

            if(searchtarget->Sindex == target){
                  if(match_onlyactive){
                        if(!UseThisKanji(searchndx)) {
                              continue;
                        }
                  }

                  AddMultiTranslation(searchtarget);
                  if(firstmatch==NULL)
                        firstmatch=searchtarget;
            }

      }

      if(firstmatch!=NULL){
            setstatus("Search complete");
            printsearch(firstmatch);
            if(getMultiCount() >1){
                  ShowMulti();
            }
      } else{
            setstatus("No match found");
            Beep();
      }

      return;
}


/* process_kinput
 *    Accepts a single 2-byte char as input.
 *    We assume this is hiragana, katakana, or special-directive char.
 *
 *    Adjusts the SEARCH_KANA_W widget labelstring appropriately
 *    (allowing backspacing)
 *    Also handles the mirrored labelwidget on the popup window itself
 *
 *    We call the convert routine "romajitokana" on our internal
 *    string, to handle romaji-to-kana stuffs.
 *    Note that we get called by both the point-n-click-kana window,
 *    AND by the kepress-event handler for the window.
 */
void process_kinput(XChar2b in_char)
{
      /* 'kanastring' is a GLOBAL BUFFER!! */
      XChar2b *kparse = kanastring;
      int kanalength;

      for(kanalength=0; kparse->byte1 != 0; kanalength++){
            kparse++;
      }

      /********************************************/
      /* Handle special embedded directives first */
      /********************************************/
      /* backspace */
      if((in_char.byte1 == 0x22) && (in_char.byte2 == 0x2b) ) {
            if(kanalength==0)
                  return;
            kanalength--;
            kanastring[kanalength].byte1 = 0;
            kanastring[kanalength].byte2 = 0;

            XtVaSetValues(searchwidgets[SEARCH_KANA_W],
                  XtNlabel, kanastring, NULL);
            XtVaSetValues(romajiinput,
                  XtNlabel, kanastring, NULL);

            return;
      
      }
      /* 'paragraph' means "do search now" */
      if((in_char.byte1 == paragraphglyph[0].byte1) &&
         (in_char.byte2 == paragraphglyph[0].byte2))
      {
            if(kanalength == 0)
                  return;
            /* accept.. search! */
            dokanafind(kanastring);
            return;
      }

      /* Done with special directives. Anything else is meant
       * to be displayed
       */

      if(kanalength<MAXKANALENGTH-1) {
            kanastring[kanalength].byte1 = in_char.byte1;
            kanastring[kanalength].byte2 = in_char.byte2;

            kanalength++;
            kanastring[kanalength].byte1 = 0;
            kanastring[kanalength].byte1 = 0;
      }

      /* See if we have enough romaji to convert to kana, before display */
      romajitokana(kanastring);

      XtVaSetValues(searchwidgets[SEARCH_KANA_W],
                  XtNlabel, kanastring, NULL);
      XtVaSetValues(romajiinput,
                  XtNlabel, kanastring, NULL);
      
}


void debugkprint(XChar2b* kstr){
      XChar2b *ptr=kstr;
      while(ptr->byte1 != 0){
            printf("%x%x",ptr[0].byte1,ptr[0].byte2);
            ptr++;
      }
      printf("\n");
}

/* Take the "current search" string, and split it up into separate
 * kanji, if it is longer than 1 kanji
 */
void split_kanji_search()
{
      XChar2b *kanjistring;
      if(lastsearch==NULL){
            return;
      }
      kanjistring=lastsearch->kanji;
      if(kanjistring[1].byte1==0){
            return;
      }
      /* Okay, we now have non-null string, of at least 2 chars in length*/
      ClearAllMulti();
      while(kanjistring->byte1!=0){
            /* kanji start at 0x3021 */
            if(kanjistring->byte1>=0x30){
                  int knum=(kanjistring->byte1 <<8) | kanjistring->byte2;
                  TRANSLATION tptr;
                  if(knum >highestkanji){
#ifdef DEBUG
                        puts("ERR: cant find kanji 0x%d\n",knum);
#endif
                        continue; /* error */
                  }
                  tptr=translations[knum];
                  AddMultiTranslation(translations[knum]);
            }
            kanjistring++;
      }

      ShowMulti();
}


/****************************************************************\
 * Find matches for a kanji 'string'                            *
 *   (used for cut-n-paste, and "match kanji" button)           *
 * If match found, will take care of calling ShowMulti()    *
\****************************************************************/
void findkanjiall(XChar2b *Schars)
{
      int foundone=0;
      TRANSLATION searchparse=NULL;
      int searchndx=0;
      int min_ndx=lowestkanji,max_ndx=highestkanji;

      if(Schars==NULL){
            puts("Paranoid coding is A Good Thing: findkanjiall passed NULL");
            return;
      }
      if(Schars->byte1==0){
            puts("Paranoid coding is A Good Thing: findkanjiall passed zerolen string");
            return;
      }

      /* if match_active on, and high levels not enabled, dont
       * bother searching through edict lines
       */
      if(match_onlyactive &&  ((gradelevelflags & ABOVESIXGRADE)==0)) {
            max_ndx=MAXKANJIALLOWED;
      }

      if(Schars[1].byte1!=0){
            /* We have multi-kanji string.
             * It is now impossible to match single-kanji range,
             * so start at edict range
             */
            min_ndx=MAXKANJIALLOWED;
      }

      for(searchndx=min_ndx; searchndx <=max_ndx; searchndx++){
            searchparse=translations[searchndx];
            if(searchparse==NULL) continue;

            if(match_onlyactive){
                  if(!UseThisKanji(searchndx)) {
                        continue;
                  }
            }
            
            if(matchkanji(searchparse->kanji,Schars)){
                  if(foundone==0) {
                        ClearAllMulti();
                        foundone=1;
                        printsearch(searchparse);
                  }
                  AddMultiTranslation(searchparse);
            }
            searchparse=searchparse->nextk;
      }

      if(foundone>0) {
            setstatus("Displaying search result");
            /* Search window already updated. So only show "multi"
             * window, if there are actual multiple matches
             */
            if(getMultiCount() >1){
                  ShowMulti();
            }
      } else {
            setstatus("no match found");
      }
      
}

Generated by  Doxygen 1.6.0   Back to index