/* 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");
}
}
syntax highlighted by Code2HTML, v. 0.9.1