/* I'm changing this so that only the actual SEARCH routines are in here. * widget creation has been moved to "searchwidgets.c" */ #include #include #include #include #include #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 highestkanji)){ setstatus("input out of dict. range"); Beep(); printsearch(lastsearch); return; } if(number > highestkanji){ number = highestkanji; } else 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;ndxbyte1 <<8) | target->byte2; for(ndx=lowestkanji;ndxkanji->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(kanalengthbyte1 != 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"); } }