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

radsearch.c

/* This file implements kanji 'radical' search widgets.
 * At one point, I intended to make my own radical list, with
 * my own definition of radicals
 * But I have wimped out and decided to go with the "standard" ones.
 *
 * This stuff takes advantage of the 'radkfile' file, included with
 * Jim Breen's xjdic program, and copied here with permission.
 * 
 */


#include <stdio.h>

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

#include <Shell.h>

#include <Xaw/Command.h>
#include <Xaw/Label.h>
#include <Xaw/Form.h>
#include <Xaw/Box.h>
#include <Xaw/AsciiText.h>
#include <cursorfont.h>

#include "defs.h"
#include "externs.h"
#include "game.h"    /* for GUESS_XXX defines */
#include "search.h"
#include "multikanji.h"
#include "searchwidgets.h"
#include "utils.h"
#include "init.h" /* for setup_deletewindow() */

Cursor pointercursor, busycursor;

Widget radical_popup;
Widget radicalinputform;

Widget radsearch_help; /* like a status bar */

int num_radicals=0; /* number of radicals read from file */

/*MAXRADICALS is defined in defs.h now */


XChar2b radicals[MAXRADICALS]; /* char for each radical */
XChar2b radical_array[MAXRADICALS][MAXKRADPOST]; /* kanji containing each rad*/
Widget radbuttons[MAXRADICALS];
CARD8 radtoggle[MAXRADICALS];   /* array to show if radical is selected*/

int numradsactive; /* number of radicals indicated by current select */
XChar2b activelist[MAXKRADPOST];
 /* activelist[] is the intersection of the kanji list from each
  * highlighted radical (eg: if its entry in radtoggle[] is set)
  */

static void setradstatus(char *s)
{
      XtVaSetValues(radsearch_help,XtNlabel,s,NULL);
}


/* Callback for 'clear' button
 * Note that we have to duplicate PART of functionality in here,
 * in subtractRadical()
 */
void ClearRadicals(Widget w, XtPointer data, XtPointer call_data)
{
      int rcount;
      for(rcount=0; rcount<num_radicals; rcount++){
            UnhighlightButton(radbuttons[rcount]);
            XtSetSensitive(radbuttons[rcount],True);
            radtoggle[rcount]=0;
      }
      numradsactive=0;
      printf("DEBUG: ClearRadicals clearing byte at %p\n",
             &activelist[0].byte1);

      activelist[0].byte1=0;
      setradstatus("No radicals selected");
      ClearAllMulti();
      
}

/* given a radical indexnumber, 
 * take all kanji referenced by that radical and
 * intersect them with the 'active' list.
 *
 * If do_merge==1, make that intersection, the NEW 'active' list.
 *
 * return 1 if meaningful merge possible, or 0 if intersection set ==empty set
 */
static int mergeRadical(int radnum, int do_merge){
      XChar2b newactivelist[MAXKRADPOST];
      XChar2b searchstr[2];
      XChar2b *kradptr;
      int newcount=0;

      if(radnum>=num_radicals){
            puts("ERROR: mergeRadical passed invalid radnum");
            return 0;
      }

      kradptr=radical_array[radnum];
      /* kradptr now points to list of all kanji that use this radical*/


      while(kradptr->byte1!=0){
            if(kstrchr(activelist,*kradptr) != NULL){
                  newactivelist[newcount++]=*kradptr;
            }
            kradptr++;
      }

      if(newcount>0){
            if(do_merge==0){
                  /* we were just checking to see if it was POSSIBLE*/
                  return 1;
            }
            newactivelist[newcount].byte1=0;
            newactivelist[newcount].byte2=0;
#ifdef DEBUG
            {
                  XChar2b *tmpptr=activelist;
                  int kcount=0;
                  printf("kanji in old active list:\n  ");
                  while(tmpptr->byte1!=0){
                        printf("0x%2x%2x ",
                               tmpptr->byte1,tmpptr->byte2);
                        tmpptr++;
                        kcount++;
                  }
                  printf(" (kcount=%d)\n",kcount);
                  
                  tmpptr=&radical_array[radnum][0];
                  kcount=0;
                  printf("kanji in radical%d list:\n  ",radnum);
                  while(tmpptr->byte1!=0){
                        printf("0x%2x%2x ",
                               tmpptr->byte1,tmpptr->byte2);
                        tmpptr++;
                        kcount++;
                  }
                  printf(" (kcount=%d)\n",kcount);
            }
#endif

            printf("DEBUG: copying %d bytes from %p to %p\n",
                   (newcount+1)*sizeof(XChar2b),newactivelist,activelist);

            bcopy(newactivelist,activelist,(newcount+1)*sizeof(XChar2b));
#ifdef DEBUG
            {
                  XChar2b *tmpptr=activelist;
                  int kcount=0;
                  printf("kanji in MERGED active list:\n  ");
                  while(tmpptr->byte1!=0){
                        printf("0x%2x%2x ",
                               tmpptr->byte1,tmpptr->byte2);
                        tmpptr++;
                        kcount++;
                  }
                  printf(" (kcount=%d)\n",kcount);
            }
#endif

            return 1;
      } else {
            return 0;
      }
}

/* Given a SINGLE kanji, look up all radicals that we know of, that
 * are used in it.
 * Length of radlist array should be MAXRADICALS+1,
 * although in reality, there probably wont be more than 5 radicals involved.
 */
void FindRadicals(XChar2b kanji, XChar2b *radlist){
      int rcount;
      XChar2b *radscan;
      for(rcount=0; rcount<MAXRADICALS;rcount++){
            radscan=radical_array[rcount];
            while(radscan->byte1 !=0){
                  if((radscan->byte1==kanji.byte1) &&
                     (radscan->byte2==kanji.byte2)) {
                        radlist->byte1=radicals[rcount].byte1;
                        radlist->byte2=radicals[rcount].byte2;
                        radlist++;
                  }
                  radscan++;
            }
      }
}


/* given a radical indexnumber, 
 * take all kanji referenced by that radical and add to 'active' list.
 * return 1 if okay, 0 if we tried to merge, and failed.
 */
static int addRadical(int radnum)
{
      int radcount;
      XChar2b *radptr;
      if((radnum>num_radicals)||(radnum<0)){
            puts("ERROR: addRadical passed invalid number");
            return 0;
      }

      if(numradsactive==0){
            bcopy(radical_array[radnum], activelist,
                   MAXKRADPOST*sizeof(XChar2b));
      } else {
            if(!mergeRadical(radnum, 1)){
                  setradstatus("no combination possible");
                  return 0;
            }
      }


      /* mask out buttons that no longer can be used */
      /* skip one that was just pressed, and any that are highlighted*/
      for(radcount=0; radcount <num_radicals; radcount++){
            if((radcount!=radnum)&&(radtoggle[radcount]==0)){
                  if(!mergeRadical(radcount,0)){
                        XtSetSensitive(radbuttons[radcount],False);
                  } else {
                        XtSetSensitive(radbuttons[radcount],True);
                  }
            }
      }

      /* Now just tally up active list, and we're done */
      radptr=activelist; radcount=0;
      while(radptr->byte1!=0){
            radcount++;
            radptr++;
      }
      
      {
            char countbuf[50];
            sprintf(countbuf,"%d kanji possible",radcount);
            setradstatus(countbuf);
            numradsactive=radcount;
      }

      return 1;
}


/* Need to just recalculate intersection of all currently active
 * radicals, except the one passed in.
 * Make sure to update numradsactive
 * Logic in here must match ClearRadicals()
 */
static void subtractRadical(int radnum)
{
      int rcount,totalactive;

      radtoggle[radnum]=0;
      numradsactive=0;
      activelist[0].byte1=0;

      /* Kinda brute-force, but cant think of "elegant" way to do this */

      for(rcount=0,totalactive=0; rcount<num_radicals; rcount++){
            if(radtoggle[rcount]){
                  addRadical(rcount);
                  totalactive++;
            }
      }

      /*
       * Note: addRadical() will have updated numradsactive for us
       */

      if(totalactive==0){
            /* Hmm. we must have UNclicked the only used radical.*/
            /* This will ensure all buttons are set to active again */
            ClearRadicals(NULL,NULL,NULL);
      }

}


/* callback for when user clicks on an individual radical button */
void SelectRadical(Widget w, XtPointer data, XtPointer call_data)
{
      TRANSLATION kanjiptr;
      int rcount=0;


      while(rcount<num_radicals){
            if(w==radbuttons[rcount]){
                  break;
            }
            rcount++;
      }
      if(rcount==num_radicals){
            puts("Error: unknown radicalbutton called SelectRadical?");
            return;
      }
#ifdef DEBUG
      printf("SelectRadical called by radical %d\n",rcount);
#endif

      /* Over slow links, or in debug mode, the operations
       * after this can be veeerrryyyy sslllloooowwww...
       */
      XtVaSetValues(radicalinputform, XtNcursor, busycursor,NULL);

      if(radtoggle[rcount]){
            UnhighlightButton(radbuttons[rcount]);
            subtractRadical(rcount);
      } else {
            if(!addRadical(rcount)){
                  return;
            }
            HighlightButton(radbuttons[rcount]);
            radtoggle[rcount]=1;
      }


      XtVaSetValues(radicalinputform, XtNcursor, pointercursor,NULL);

      /* potentially, we could have a really looong active list.
       * dont bother even trying to display, if longer than multilist
       * would display anyway
       */
      if(numradsactive>=getMultiMax()){
            return;
      }

      /* Now fill out the "multikanji" popup window, with an entry
       * for each "active" kanji that is potentially a match with
       * the radicals the user has selected so far.
       */
      
      ClearAllMulti();

      for(rcount=0;rcount<numradsactive;rcount++){
            int kindex=(activelist[rcount].byte1 <<8) +
                        activelist[rcount].byte2;

            if((kindex <MINKANJIALLOWED) ||
               (kindex >MAXKANJIALLOWED)){
                  printf("SelectRadical: ERROR: kanji 0x%x in activelist out of range\n",
                         kindex);
                  continue;
            } 

            kanjiptr=translations[kindex];
            if(kanjiptr==NULL){
                  printf("SelectRadical: ERROR: no entry for kanji 0x%x (position %d)\n",
                         kindex, rcount);
            } else {
                  AddMultiTranslation(kanjiptr);
            }
      }
      SetMultiMode(GUESS_KANJI);
      ShowMulti();
}



XChar2b fakelabel[2]={{0x24, 0x22}, {0x0, 0x0}};

/*
 * Internal routine, to create the buttons for the
 * kanji radical search popup.
 * Should have:
 *  -  Buttons for all 250 radicals
 *  -  a 'clear' button
 *  -  a 'search' button
 *  -  a status label.
 *
 */
void makeradicalinput(Widget parent)
{
      Widget radform,bottomform;
      Widget clearbutton;
      int radcount;
      XChar2b buttonlabel[2];
      buttonlabel[1].byte1=0;
      buttonlabel[1].byte2=0;

      radform=XtVaCreateManagedWidget("radinputform", boxWidgetClass,parent,
            XtNwidth,600,
                  XtNleft,XawChainLeft,
                  XtNright,XawChainRight,
                  XtNtop, XawChainTop,
                  XtNbottom, XawChainBottom,
            NULL);

      for(radcount=0;radcount<num_radicals;radcount++){
            char radbname[10];
            sprintf(radbname,"radb%d\n",radcount);
            buttonlabel[0].byte1=radicals[radcount].byte1;
            buttonlabel[0].byte2=radicals[radcount].byte2;
            radbuttons[radcount]=XtVaCreateManagedWidget(radbname,
                    commandWidgetClass, radform,
                  XtNlabel,buttonlabel,
                  XtNencoding, XawTextEncodingChar2b,
                  XtNfont, smallkfont,
                    NULL);

            XtAddCallback(radbuttons[radcount], XtNcallback,
                              SelectRadical, NULL);
      }

      bottomform=XtVaCreateManagedWidget("radcommandform", formWidgetClass,
            parent,
            XtNfromVert,radform,
            XtNborderWidth,0,
                  XtNleft,XawChainLeft,
                  XtNright,XawChainRight,
                  XtNtop, XawChainBottom,
                  XtNbottom, XawChainBottom,
            NULL);

      clearbutton=XtVaCreateManagedWidget("radclear",
              commandWidgetClass, bottomform,
            XtNlabel,"clear",
            XtNleft, XawChainLeft,
            XtNright, XawChainLeft,
              NULL);

      radsearch_help=XtVaCreateManagedWidget("radsearchhelp",
            labelWidgetClass, bottomform,
            XtNlabel,"                 Radical Search                   ",
            XtNfromHoriz,clearbutton,
            XtNleft, XawChainRight,
            XtNright, XawChainRight,
              NULL);
      
      XtAddCallback(clearbutton, XtNcallback,
                              ClearRadicals, NULL);

}

/* Exported routine */
/* Lets other functions know if radical search is available or not.
 * (just in case the radkfile is not present)
 */ 
int HaveRadicals()
{
      if(num_radicals>0) return 1;
      return 0;
}

/* exported routine */
/* Call HaveRadicals() if you want to know if it
 * succeeded or not
 */
void InitRadicals()
{
#define RADBUFLEN 8192

      /* I'm not particularly proud of the internals of this thing.
       * Unfortunately, the file format doesnt easily lend itself
       * to a better approach, that I can think of.
       * Oh well, memory is a lot cheaper these days. Sigh.
       */
      char radfile[MAXLINELEN]; /* path to "radkfile" */
      CARD8 linebuf[RADBUFLEN];
      int cur_radnum=-1;
      int kradpos=0;
      FILE *radfp;
      
      GetXtrmString("radkfile","Radkfile",radfile);

      numradsactive=0;
      bzero(radical_array,MAXRADICALS*MAXKRADPOST*sizeof(XChar2b));
      bzero(radtoggle,MAXRADICALS);

      radfp=fopen(radfile,"r");
      if(radfp==NULL){
            num_radicals=0;
            return;
      }
      printf("Opened radfile %s\n",radfile);

      while(cur_radnum<MAXRADICALS){
            int linepos=0;
            if(fgets((void*)linebuf, RADBUFLEN, radfp)==NULL){
                  break;
            }
            if(linebuf[0]=='#'){
                  continue;
            }
            if(linebuf[0]=='$'){
                  cur_radnum++;
                  kradpos=0;

                  /* save the kanji that we want to display,
                   * for the button representing radical #{cur_radnum}
                   */
                  radicals[cur_radnum].byte1=linebuf[2]&0x7f;
                  radicals[cur_radnum].byte2=linebuf[3]&0x7f;

                  continue;
            }
            /* otherwise, must be radical list line.
             * A list of kanji that match the current radical.
             * Except if file is corrupted, of course
             * Note that linepos gets incremented TWICE.
             * But, unless file gets corrupted, we should never get
             * anywhere near RADBUFLEN
             */

            linepos=0;
            for(; linepos<RADBUFLEN && linebuf[linepos]!='\n'; linepos+=2) {
                  radical_array[cur_radnum][kradpos].byte1=
                        linebuf[linepos]&0x7f;
                  radical_array[cur_radnum][kradpos].byte2=
                        linebuf[linepos+1]&0x7f;
                  kradpos++;
            }
            if(linepos >= RADBUFLEN){
                  fprintf(stderr,"ERROR: radkfile %s corrupted!\n",
                        radfile);
                  printf("linepos=%d,kradpos=%d\n",linepos,kradpos);
                  exit(1);
            }
      }

      if(cur_radnum==MAXRADICALS){
            fprintf(stderr,"ERROR:InitRadicals exceeded MAXRADICALS\n");
            /* Need to increase the #define */
            num_radicals=0;
            return;
      }
      num_radicals=cur_radnum;

      printf("%d radicals read from radkfile\n",num_radicals);

}


/* exported routine */
void MakeRadicalinputPopup()
{
      if(num_radicals==0) {
            return;
      }

      pointercursor=XCreateFontCursor(display, XC_X_cursor);
      busycursor=XCreateFontCursor(display, XC_clock);


      radical_popup = XtVaCreatePopupShell("kdrill_radicalsearch ",
            topLevelShellWidgetClass,
            search_popup,
            NULL);

      radicalinputform = XtVaCreateManagedWidget("radicalinputform",
                                   formWidgetClass,
                                   radical_popup,
                  XtNleft,XawChainLeft,
                  XtNright,XawChainRight,
                  XtNtop, XawChainTop,
                  XtNbottom, XawChainBottom,
                                   NULL);

      /* make all the substuff */
      makeradicalinput(radicalinputform);

}

/*
 * Exported routine:
 * 
 * It pops up the kanji radical search window
 */
void ShowRadicalinput(Widget w,XtPointer client_data, XtPointer call_data)
{
      static int radicals_up = -1;

      static Position rel_x,rel_y;
      Position x,y;

      if(num_radicals==0){
            setstatus("ERROR: Radical file radkfile not present");
            return;
      }

      if(radicals_up==-1){
            /* first time init.. */
            /*rel_x = GetXtNumber("radical_popupx","Search_popupx");*/
            /*rel_y = GetXtNumber("radical_popupy","Search_popupy");*/
            rel_x = 10;
            rel_y = 10;
            radicals_up=0;
      }
      if(isMapped(radical_popup)==False){
            XtTranslateCoords(search_popup,rel_x,rel_y,&x,&y);
            XtVaSetValues(radical_popup,
                  XtNx,x,
                  XtNy,y,
                  NULL);
            XtPopup(radical_popup,XtGrabNone);
            if(radicals_up==0){
                  setup_deletewindow(radical_popup);
                  radicals_up=1;
            }
            
            setstatus("Bringing up radical input window...");
      } else {
            XtPopdown(radical_popup);
      }
}



Generated by  Doxygen 1.6.0   Back to index