/* Copyright (C) 1999-2002 Ricardo Ueda Karpischek This is free software; you can redistribute it and/or modify it under the terms of the version 2 of the GNU General Public License as published by the Free Software Foundation. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this software; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* event.c: GUI initialization and event handler */ #include #include #include #include #include #include #include #include "common.h" #include "gui.h" #ifndef PI #define PI M_PI #endif /* Event masks */ #define ev1 ButtonPressMask|ButtonReleaseMask|KeyPressMask #define ev2 ExposureMask|PointerMotionMask|PointerMotionMask #define ev3 StructureNotifyMask|FocusChangeMask /* #define ev3 StructureNotifyMask */ #define evmasks (ev1|ev2|ev3) /* #define evmasks ((1L<<25)-1) */ /* Indexes of the menus */ int CM_F; int CM_E; int CM_T; int CM_V; int CM_D; int CM_B; int CM_O; int CM_A; int CM_L; int CM_R; int CM_G; /* items on File menu */ int CM_F_LOAD; int CM_F_SAVE; int CM_F_SZ; int CM_F_REP; int CM_F_QUIT; /* items on Edit menu */ int CM_E_DOUBTS; int CM_E_RESCAN; int CM_E_FILL; int CM_E_FILL_C; int CM_E_PP; int CM_E_PP_C; int CM_E_SP; int CM_E_SM; int CM_E_ST; int CM_E_SW; int CM_E_SH; int CM_E_SN; int CM_E_DU; int CM_E_SETPT; int CM_E_SEARCHB; int CM_E_THRESH; int CM_E_AC; #ifdef PATT_SKEL int CM_E_RSKEL; #endif /* items on VIEW menu */ int CM_V_SMALL; int CM_V_MEDIUM; int CM_V_LARGE; int CM_V_DEF; int CM_V_HIDE; int CM_V_OF; int CM_V_VHS; int CM_V_WCLIP; int CM_V_MAP; int CM_V_CC; int CM_V_MAT; int CM_V_CMP; int CM_V_MAT_K; int CM_V_CMP_K; int CM_V_ST; int CM_V_PRES; /* items on Options menu */ int CM_O_CURR; int CM_O_ALL; int CM_O_DKEYS; int CM_O_AMENU; int CM_O_PGO; /* items on the Alphabet menu */ int CM_A_ARABIC; int CM_A_CYRILLIC; int CM_A_GREEK; int CM_A_HEBREW; int CM_A_LATIN; int CM_A_KANA; int CM_A_NUMBER; int CM_A_IDEOGRAM; /* items on the Languages menu */ int CM_L_BR; int CM_L_DE; int CM_L_EN; int CM_L_FR; int CM_L_GR; int CM_L_IT; int CM_L_PT; int CM_L_RU; int CM_L_UK; /* items on DEBUG menu */ int CM_G_ALIGN; int CM_G_LB; int CM_G_GLINES; int CM_G_CM; int CM_G_BO; int CM_G_IO; int CM_G_SUM; /* items on PAGE options menu */ int CM_D_TS; int CM_D_OS; int CM_D_HERE; int CM_D_ADD; int CM_D_SLINK; int CM_D_ALINK; int CM_D_DIS; int CM_D_SDIAG; int CM_D_WDIAG; int CM_D_GM; int CM_D_FOCUS; int CM_D_PIXELS; int CM_D_CLOSURES; int CM_D_SYMBOLS; int CM_D_WORDS; int CM_D_PTYPE; int CM_D_RS; int CM_D_BB; /* items on PAGE_FATBITS options menu */ int CM_B_HERE; int CM_B_CENTRE; int CM_B_SKEL; int CM_B_BORDER; int CM_B_HS; int CM_B_HB; int CM_B_BP; int CM_B_SLC; int CM_B_SLD; int CM_B_ISBAR; int CM_B_DX; /* items on OCR steps menu */ int CM_R_PREPROC; int CM_R_SEG; int CM_R_CONS; int CM_R_OPT; int CM_R_REV; int CM_R_BLOCK; int CM_R_CLASS; int CM_R_GEOMERGE; int CM_R_BUILD; int CM_R_SPELL; int CM_R_OUTP; int CM_R_DOUBTS; /* Menu availability flags. CMA_PAGE .. there exists a page loaded CMA_PATTERNS .. the bookfont is nonempty CMA_IDLE .. the OCR thread is idle CMA_CL .. requires closures computed CMA_ZONE .. at least one zone is defined CMA_ABSENT .. unimplemented feature CMA_NCL .. requires closures not computed */ #define CMA_PAGE 1 #define CMA_PATTERNS 2 #define CMA_IDLE 4 #define CMA_CL 8 #define CMA_ZONE 16 #define CMA_ABSENT 32 #define CMA_NCL 64 /* Menu help message and failure warning. */ char *mhelp; /* Do we have the focus? */ int focused = 1; /* (devel) Redraw flags ------------ The redraw flags inform the function redraw about which portions of the application window must be redraw. The precise meaning of each flag depends on the implementation of the redraw function, that can be analysed directly on the source code. redraw_button .. one specific button or all buttons redraw_bg .. redraw background redraw_grid .. the grid on fatbits windows redraw_stline .. the status line redraw_dw .. all visible windows redraw_inp .. all text input fields redraw_tab .. tabs and their labels redraw_zone .. rectangle that defines the zone redraw_menu .. menu bar and currently open menu redraw_j1 .. redraw junction 1 (page tab) redraw_j2 .. redraw junction 2 (page tab) redraw_pbar .. progress bar redraw_map .. alphabet map redraw_flea .. the flea An individual button may be redraw to reflect a status change (on/off). The junction 1 is the junction of the top and middle windows on the page tab, and the junction 2 is the junction of the middle and bottom window on the page tab. The correspondig flags are used when resizing some window on the page tab. If redraw_menu is 2, the menu is entirely redrawn. If redraw_menu is 1, then the draw_menu function will redraw only the last selected item and the newly selected item, except if the menu is being drawn by the first time. The progress bar is displayed on the bottom of the window to reflect the progress of some slow operation. By now, the progress bar is unused. */ int redraw_button, redraw_bg, redraw_grid, redraw_stline, redraw_dw, redraw_inp, redraw_tab, redraw_zone, redraw_menu, redraw_j1, redraw_j2, redraw_pbar=0, redraw_map, redraw_flea; /* cfont is the display buffer. It contains a matrix of pixels that will be displayed on the windows PAGE_FATBITS and PATTERN. */ char cfont[FS*FS]; /* buffer of messages */ char mb[MMB+1],mba[MMB+1]; /* font used to display text messages, its height and width */ XFontStruct *dfont = NULL; int DFH,DFW,DFA,DFD; /* vertical separation between text lines */ int VS = 2; /* The windows. doc .. the scanned page, reduced mode fb .. the scanned page, fat bits mode f .. font editor outp .. the OCR output sb .. the symbol browser dl .. the list of pages sl .. the list of patterns */ dwdesc dw[TOP_DW+1]; /* Restricted clip window for the PAGE window. */ int PHR,PVR,PDM,PDT; /* active window and active mode for each tab */ int CDW=-1; int PAGE_CM=PAGE,PATTERN_CM=PATTERN,TUNE_CM=TUNE; /* Xcolors */ XColor white,black,gray,darkgray,vdgray; int glevels=-1; /* Mode for display matches. This variable selects how the matches of symbols and patterns from the bookfont are displayed. When 0, the GUI will use black to the symbol pixels and gray to the pattern skeleton pixels. When 1, swap these colors. */ int dmmode=1; /* the event */ XEvent xev; XButtonEvent *bev; int have_s_ev; /* Context */ char *displayname = NULL; Display *xd=NULL; GC xgc; char *cschema; int mclip = 0; /* The window and its buffer. TODO: use_xb initial value must be 1 (as buffered I/O is still experimental, it's currently off by default). */ Window XW; XID xw; Pixmap pm; int pmw,pmh; int have_xw=0,have_pm=0,use_xb=0; /* default name for each tab */ char *tabl[] = {"page","patterns","tune","history"}; /* Currently active tab. Possible values are PAGE, PATTERN or TUNE. These names are just the names of some windows from these tabs (one for each) re-used to identify each tab. */ int tab; /* Zoomed pixel size. ZPS must be an odd integer, and may be changed through the -z command-line flag (please check the currently limits imposed on ZPS value at the getopt loop). */ int ZPS=1; /* current color */ int COLOR=BLACK; /* (devel) The zones --------- Clara OCR allows to create "zones". Zones are usually used to identify one text block in the page. For instance, a page containing two text columns should use one zone to limit each column. The zone limits are given by the "limits" array. The top left is (limits[0],limits[1]) as presented by the figure: +---------------------------+ | (0,1) (6,7) | | +-----------+ | | |this is a | | | |text block | | | |identifyed | | | |by a | | | |rectangular| | | |zone. | | | +-----------+ | | (2,3) (4,5) | | | +---------------------------+ Multiple zones are supported simultaneously, and each one is handled separately when building words and lines and generating the output. The limits of the second zone are limists[8..15], and so on. Also, non-rectangular zones are supported, in order to cover nonrectangular (skewed) text blocks. */ int zones=0; int limits[LIMITS_SIZE]; int curr_corner; /* To measure the duration of a mouse click. */ struct timeval tclick; /* These variables are documented below (search the service enter_wait). */ int waiting_key = 0,key_pressed; char inp_str[MMB+1]; int inp_str_ps; /* This is the flea path. */ unsigned short fp[2*sizeof(short)*FS*FS]; float fsl[3*FS*FS],fpp[FS*FS]; int topfp,fpsz,curr_fp=0,last_fp=-1; /* Spyhole stuff. */ int sh_tries; /* Create a new menu. The initial number of items is m and the menu title is tt. If a==1, the menu is added to the menu bar. If a==2, it must be activated by some special action, like clicking the mouse button 3 somewhere. */ cmdesc *addmenu(int a,char *tt,int m) { /* enlarge CM */ if (++TOP_CM >= CM_SZ) { int a=0; /* The pointer to the currently active menu (if any) must be recomputed accordingly to the new dynamic CM address. */ if (cmenu != NULL) a = cmenu-CM; CM = c_realloc(CM,sizeof(cmdesc)*(CM_SZ+=30),NULL); if (cmenu != NULL) cmenu = CM + a; } /* Create the menu */ CM[TOP_CM].a = a; strncpy(CM[TOP_CM].tt,tt,MAX_MT); CM[TOP_CM].tt[MAX_MT] = 0; if (m <= 0) m = 5; CM[TOP_CM].m = m; CM[TOP_CM].n = 0; CM[TOP_CM].l = c_realloc(NULL,m*(MAX_MT+1)+m,NULL); CM[TOP_CM].t = c_realloc(NULL,m,NULL); CM[TOP_CM].h = c_realloc(NULL,m*sizeof(char *),NULL); CM[TOP_CM].f = c_realloc(NULL,m*sizeof(int),NULL); CM[TOP_CM].c = -1; return(CM+TOP_CM); } /* Add item to menu m, and returns the item identifier. The item label is t, its type is p and the flag g ("group") informs if a separator (an horizontal line) must be drawn between this item and the one just above it. The flag d ("dismiss") informs if the menu must be dismissed when this item is selected. The parameter h is the short help message to be presented by the GUI. The parameter f are the activation flags. The types are: CM_TA (performs an action when selected), CM_TB (on/off) and CM_TR (radio item). */ int additem(cmdesc *m,char *t,int p,int g,int d,char *h,int f) { int id; /* maximum number of items reached: enlarge */ if (m->n >= m->m) { int o; o = m->m; m->l = c_realloc(m->l,(m->m+=5)*(MAX_MT+1),NULL); m->t = c_realloc(m->t,m->m,NULL); m->h = c_realloc(m->h,(m->m)*sizeof(char *),NULL); m->f = c_realloc(m->f,(m->m)*sizeof(int),NULL); } /* label */ strncpy(m->l+(MAX_MT+1)*m->n,t,MAX_MT); m->l[MAX_MT] = 0; /* type and group flag */ m->t[m->n] = p | (g ? CM_NG : 0); /* not hidden, by default */ if (m->n % 2) m->t[m->n] |= CM_HI; /* dismiss flag */ if (d == -1) d = (p == CM_B) ? 0 : 1; if (d) m->t[m->n] |= CM_DI; /* short help and flags */ m->h[m->n] = h; m->f[m->n] = f; /* identifier */ id = m->n + ((m-CM)<< CM_IT_WD); ++(m->n); return(id); } /* (devel) The function show_hint ---------------------- Messages are put on the status line (on the bottom of the application X window) using the show_hint service. The show_hint service receives two parameters: an integer f and a string (the message). If f is 0, then the message is "discardable". It won't be displayed if a permanent message is currently being displayed. If f is 1, then the message is "fixed". It won't be erased by a subsequent show_hint call informing as message the empty string (in practical terms, the pointer motion won't clear the message). If f is 2, then the message is "permanent" (the message will be cleared only by other fixed or permanent message). If f is 3, clear any current message. */ void show_hint(int f,char *s, ...) { va_list args; static int fixed = 0; /* enter_wait disables show_hint */ if (waiting_key) return; va_start(args,s); if (f == 3) { redraw_stline = 1; mb[0] = 0; fixed = 0; } /* fixed or permanent entry: redraw unconditionally */ else if (f) { if (vsnprintf(mb,MMB,s,args) < 0) mb[0] = 0; else if ((trace) && (mb[0]!=0)) tr(mb); redraw_stline = 1; fixed = f; } else { /* If the last hint was fixed, then refresh only if the new hint is nonempty. */ if (fixed == 1) { if (s[0] != 0) { if (vsnprintf(mb,MMB,s,args) < 0) mb[0] = 0; redraw_stline = 1; fixed = 0; } } /* If the last hint was not fixed, then refresh only if the hint changed. */ else if ((fixed == 0) && (strcmp(mb,s) != 0)) { if (vsnprintf(mb,MMB,s,args) < 0) mb[0] = 0; redraw_stline = 1; } } /* printf("request (priority %d, mclip=%d, redraw=%d) to draw the message \"%s\"\n",f,mclip,redraw_stline,s); */ } /* Enter one waiting state, to permit the OCR tasks to accept asynchronously a user input from the keyboard. The parameter m is the wait mode (the variable waiting_key stores the current wait mode): Mode 1: wait any key. Mode 2: wait ENTER or ESC. Mode 3: wait y, Y, n or N. Mode 4: wait a string. Mode 5: wait <, >, + or -. Note that all OCR tasks remain stopped while the variable waiting_key is nonzero. On cases 2 and 3, the return status will be informed through the variable key_pressed as follows: ENTER, y, or Y .. 1 ESC, n or N .. 0 On case 4, the string will return on inp_str. If f==1, appends the valid inputs to the message to be displayed on the status line. */ void enter_wait(char *s,int f,int m) { char b[MMB+1]; /* already waiting */ if (waiting_key) return; /* build the message to warn the user */ b[MMB] = 0; if (s == NULL) b[0] = 0; else { strncpy(b,s,MMB); if (f == 1) strncat(b," ",MMB-strlen(b)); } if (f == 1) { if (m == 1) strncat(b,"(PRESS ANY KEY)",MMB-strlen(mb)); else if (m == 2) strncat(b,"(ENTER to confirm, ESC to ignore)",MMB-strlen(mb)); else if (m == 3) strncat(b,"(y/n)",MMB-strlen(mb)); } /* display the message and enter wait mode */ show_hint(2,b); waiting_key = m; /* In mode 4, the informed message is preserved because the editing service will need to redisplay it. */ strncpy(inp_str,s,MMB); inp_str[MMB] = 0; inp_str_ps = strlen(inp_str); } /* Step right the current window. */ void step_right(void) { if (CDW == PAGE) { X0 += FS; check_dlimits(0); redraw_dw = 1; } else if (CDW == PAGE_FATBITS) { ++X0; check_dlimits(0); RG = 1; redraw_dw = 1; } else if (HTML) { X0 += DFW; check_dlimits(0); redraw_dw = 1; } } /* Step left the current window. */ void step_left(void) { if (CDW == PAGE) { X0 -= FS; check_dlimits(0); redraw_dw = 1; } else if (CDW == PAGE_FATBITS) { --X0; check_dlimits(0); RG = 1; redraw_dw = 1; } else if (HTML) { if (X0 > 0) { X0 -= DFW; if (X0 < 0) X0 = 0; redraw_dw = 1; } } } /* Step up the current window. */ void step_up(void) { if (CDW == PAGE) { Y0 -= FS; check_dlimits(0); redraw_dw = 1; } else if (CDW == PAGE_FATBITS) { --Y0; check_dlimits(0); RG = 1; redraw_dw = 1; } else if (HTML) { if (Y0 >= DFW) { Y0 -= DFW; redraw_dw = 1; } } } /* Step down the current window. */ void step_down(void) { if (CDW == PAGE) { Y0 += FS; check_dlimits(0); redraw_dw = 1; } else if (CDW == PAGE_FATBITS) { ++Y0; check_dlimits(0); RG = 1; redraw_dw = 1; } else if (HTML) { Y0 += DFW; check_dlimits(0); redraw_dw = 1; } } /* Context-dependent "right" action. */ void right(void) { if ((dw[PAGE_SYMBOL].v) || (dw[PATTERN].v)) { form_auto_submit(); } /* select next menu of the menu bar */ if (cmenu != NULL) { if (cmenu->a == 1) { if ((cmenu-CM >= TOP_CM) || ((++cmenu)->a!=1)) cmenu = CM; CY0_orig = -1; force_redraw(); set_mclip(0); } } /* go to the next pattern */ else if ((CDW == PATTERN) || (dw[TUNE_PATTERN].v)) { if (topp >= 0) { /* find the next untransliterated pattern */ if (*cm_e_od != ' ') { while ((++cdfc <= topp) && (pattern[cdfc].tr != NULL)); if (cdfc > topp) cdfc = 0; } /* or just go to the next one */ else if (++cdfc > topp) cdfc = 0; /* edit it.. */ edit_pattern(0); /* ..and recompute skeleton */ dw[TUNE_PATTERN].rg = 1; dw[TUNE_SKEL].rg = 1; redraw_dw = 1; } } /* move the graphic cursor to the next symbol */ else if (CDW == PAGE) { int l; if (curr_mc >= 0) { if (mc[curr_mc].E >= 0) curr_mc = mc[curr_mc].E; else if ((l=rsymb(curr_mc,1)) >= 0) curr_mc = l; check_dlimits(1); if (dw[PAGE_SYMBOL].v) dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } } /* go to the next pattern type */ else if (CDW == PATTERN_TYPES) { if (cpt < 1) cpt = 1; else if (cpt < toppt) ++cpt; else cpt = 1; dw[PATTERN_TYPES].rg = 1; redraw_dw = 1; } /* scroll right PAGE_FATBITS window */ else if (CDW == PAGE_FATBITS) { ++X0; check_dlimits(0); RG = 1; redraw_dw = 1; } /* next revision act */ else if (CDW == TUNE_ACTS) { if (curr_act+1 <= topa) { ++curr_act; redraw_dw = 1; dw[TUNE_ACTS].rg = 1; } } /* scroll right HTML documents */ else if (HTML) { step_right(); } } /* Context-dependent "left" action. */ void left(void) { if ((dw[PAGE_SYMBOL].v) || (dw[PATTERN].v)) { form_auto_submit(); } /* select previous menu of the menu bar */ if (cmenu != NULL) { int l; if (cmenu->a == 1) { l = cmenu-CM; for (l=cmenu-CM; (l > 0) && ((--cmenu)->a!=1); --l); if (l == 0) for (cmenu=CM+(l=TOP_CM); (l > 0) && ((--cmenu)->a!=1); --l); CY0_orig = -1; force_redraw(); set_mclip(0); } } /* go to the previous pattern */ else if ((CDW == PATTERN) || (dw[TUNE_PATTERN].v)) { if (topp >= 0) { /* find the previous untransliterated pattern */ if (*cm_e_od != ' ') { while ((--cdfc >= 0) && (pattern[cdfc].tr != NULL)); if (cdfc < 0) cdfc = topp; } /* or just go to the previous one */ else if (--cdfc < 0) cdfc = topp; /* edit it.. */ edit_pattern(0); /* ..and recompute skeleton */ dw[TUNE_PATTERN].rg = 1; dw[TUNE_SKEL].rg = 1; redraw_dw = 1; } } /* move the current symbol to the previous one */ else if (CDW == PAGE) { int l; if (curr_mc >= 0) { if (mc[curr_mc].W >= 0) curr_mc = mc[curr_mc].W; else if ((l=lsymb(curr_mc,1)) >= 0) curr_mc = l; check_dlimits(1); if (dw[PAGE_SYMBOL].v) dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } } /* go to the previous pattern type */ else if (CDW == PATTERN_TYPES) { if ((0 <= toppt) && (toppt < cpt)) cpt = toppt; else if (cpt > 1) --cpt; else cpt = toppt; dw[PATTERN_TYPES].rg = 1; redraw_dw = 1; } /* scroll right PAGE_FATBITS window */ else if (CDW == PAGE_FATBITS) { if (X0 > 0) { --X0; RG = 1; redraw_dw = 1; } } /* previous revision act */ else if (CDW == TUNE_ACTS) { if (curr_act > 0) { --curr_act; redraw_dw = 1; dw[TUNE_ACTS].rg = 1; } } /* scroll left HTML documents */ else if (HTML) { step_left(); } } /* Context-dependent "up" action. */ void up(void) { /* select previous item of the active menu */ if (cmenu != NULL) { if (cmenu->c > 0) --(cmenu->c); else cmenu->c = cmenu->n - 1; if (cmenu->c >= 0) { set_mclip(1); redraw_menu = 1; } else cmenu->c = -1; } /* move the graphic cursor to the symbol above the current one */ else if (CDW == PAGE) { int l; if (curr_mc >= 0) { if (mc[curr_mc].N >= 0) curr_mc = mc[curr_mc].N; else if ((l=tsymb(curr_mc,1)) >= 0) curr_mc = l; check_dlimits(1); if (dw[PAGE_SYMBOL].v) dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } } /* scroll down HTML documents by one line */ else if (HTML) { if (Y0 > 0) { Y0 -= (DFH+VS); if (Y0 < 0) Y0 = 0; redraw_dw = 1; } } /* scroll up PAGE_FATBITS window */ else if (CDW == PAGE_FATBITS) { step_up(); } } /* Context-dependent "down" action. */ void down(void) { /* select next item of the active menu */ if (cmenu != NULL) { if ((cmenu->c < 0) || (cmenu->c+1 >= cmenu->n)) cmenu->c = 0; else ++(cmenu->c); if (cmenu->c < cmenu->n) { set_mclip(1); redraw_menu = 1; } else cmenu->c = -1; } /* move the graphic cursor to the symbol below the current one */ else if (CDW == PAGE) { int l; if (curr_mc >= 0) { if (mc[curr_mc].S >= 0) curr_mc = mc[curr_mc].S; else if ((l=bsymb(curr_mc,1)) >= 0) curr_mc = l; check_dlimits(1); if (dw[PAGE_SYMBOL].v) dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } } /* scroll down HTML documents by one line */ else if (HTML) { Y0 += DFH+VS; check_dlimits(0); redraw_dw = 1; } /* scroll up PAGE_FATBITS window */ else if (CDW == PAGE_FATBITS) { step_down(); } } /* Context-dependent "prior" (pgup) action. */ void prior(void) { int d; if (CDW == PAGE) { Y0 -= VR; check_dlimits(0); redraw_dw = 1; } else if (HTML) { d = VR; d /= (DFH+VS); d *= (DFH+VS); Y0 -= d; check_dlimits(0); redraw_dw = 1; } else if (CDW == PAGE_FATBITS) { Y0 -= FS; check_dlimits(0); RG = 1; redraw_dw = 1; } } /* Context-dependent "next" (pgdw) action. */ void next(void) { int d; if (CDW == PAGE) { Y0 += VR; check_dlimits(0); redraw_dw = 1; } else if (HTML) { d = VR; d /= (DFH+VS); d *= (DFH+VS); Y0 += d; check_dlimits(0); redraw_dw = 1; } else if (CDW == PAGE_FATBITS) { Y0 += FS; check_dlimits(0); RG = 1; redraw_dw = 1; } } /* Check menu item availability. */ int mcheck(int flags) { /* requires one page loaded */ if (flags & CMA_PAGE) { if (cpage < 0) { mhelp = "must load a page to activate this item"; return(0); } } /* requires nonempty bookfont */ if (flags & CMA_PATTERNS) { if (topp < 0) { mhelp = "no patterns available"; return(0); } } /* requires OCR thread idle */ if (flags & CMA_IDLE) { if (ocring) { mhelp = "BUSY"; return(0); } } /* requires binarization */ if (flags & CMA_CL) { if (!cl_ready) { mhelp = "this operation requires previous binarization"; return(0); } } /* requires zone */ if (flags & CMA_ZONE) { if (zones <= 0) { mhelp = "no zone defined"; return(0); } } /* requires implementation */ if (flags & CMA_ABSENT) { mhelp = "this feature is under implementation"; return(0); } /* requires binarization */ if (flags & CMA_NCL) { if (cl_ready) { mhelp = "this operation was performed already"; return(0); } } return(1); } /* Menu actions. After catching the selection of one menu item (at mactions_b1), the associated action is performed here. If check is nonzero, instead of selecting the item, a test is performed to check if the item can be selected or not. Returns 1 if yes, 0 otherwise. If check is 2, a message is displayed (a short help if the item is available, or a diagnostic otherwise). */ int cma(int it,int check) { /* Actions that perform large changes on the screen must set new_mclip to 0 in order to the redraw routines redraw the screen completely. Otherwise, only the menu rectangular region will be redraw. */ int new_mclip = 1; /* menu-relative item index */ int a = it & CM_IT_M; /* availability */ int aval; /* other */ int cur_dw; /* invalid item */ if ((a < 0) || ((cmenu->n) <= a)) { return(0); } /* compute availability */ mhelp = cmenu->h[a]; aval = mcheck(cmenu->f[a]); /* just checking and/or unavailable */ if ((check) || (aval == 0)) { if (check == 2) { if (aval == 0) show_hint(0,"unavailable: %s",mhelp); else show_hint(0,mhelp); } return(aval); } /* Change selection on radio group. */ if ((!check) && ((cmenu->t[a] & CM_TM) == CM_TR)) { char *s,t; int i; /* status of current item */ s = cmenu->l + a*(MAX_MT+1)+1; t = *s; /* search group start */ for (i=a; (i>=0) && (((cmenu->t)[a] & CM_TM) == CM_TR) && (((cmenu->t)[i] & CM_NG) == 0); --i); if (i < 0) ++i; /* deactivate all within group */ do { cmenu->l[i*(MAX_MT+1)+1] = ' '; ++i; } while ((in) && (((cmenu->t)[a] & CM_TM) == CM_TR) && (((cmenu->t)[i] & CM_NG) == 0)); /* toggle current */ if (t == ' ') { *s = 'X'; } else { *s = ' '; } } /* Toggle binary item */ else if ((!check) && ((cmenu->t[a] & CM_TM) == CM_TB)) { char *s; /* toggle */ s = cmenu->l + a*(MAX_MT+1)+1; *s = (*s == ' ') ? 'X' : ' '; redraw_menu = 1; } /* Items on the "File" menu */ if (cmenu-CM == CM_F) { /* Load page */ if (it == CM_F_LOAD) { setview(PAGE_LIST); new_mclip = 0; } /* save session */ else if (it == CM_F_SAVE) { start_ocr(-2,OCR_SAVE,0); } /* save zone */ else if (it == CM_F_SZ) { if (pixmap != NULL) wrzone("zone.pgm",1); else wrzone("zone.pbm",1); new_mclip = 0; } /* write report */ else if (it == CM_F_REP) { int F; cur_dw = CDW; CDW = PAGE_LIST; ge2txt(); F = open("report.txt",O_WRONLY|O_CREAT|O_TRUNC,0644); write(F,text,strlen(text)); close(F); show_hint(2,"Report saved on file report.txt"); CDW = cur_dw; new_mclip = 0; } /* QUIT the program */ else if (it == CM_F_QUIT) { finish = 2; start_ocr(-2,OCR_SAVE,0); new_mclip = 0; } } /* Items on "Edit" menu */ else if (cmenu-CM == CM_E) { /* sort criteria */ if ((it == CM_E_SM) || (it == CM_E_SP) || (it == CM_E_ST) || (it == CM_E_SW) || (it == CM_E_SH) || (it == CM_E_SN)) { dw[PATTERN_LIST].rg = 1; new_mclip = 0; } /* remove untransliterated patterns */ else if (it == CM_E_DU) { rm_untrans(); dw[PATTERN_LIST].rg = 1; new_mclip = 0; } else if (it == CM_E_SETPT) { if (!ocring) { ocr_other = 1; start_ocr(-2,OCR_OTHER,0); } new_mclip = 0; } else if (it == CM_E_SEARCHB) { if (cl_ready) { if (!ocring) { ocr_other = 2; start_ocr(-2,OCR_OTHER,0); } } else if (cpage < 0) show_hint(2,"load a page before trying this feature"); else show_hint(2,"perform segmentation before using this feature"); new_mclip = 0; } else if (it == CM_E_THRESH) { if ((pixmap != NULL) && (cl_ready == 0) && (pm_t == 8)) { /* select the manual global binarizer */ bin_method = 1; dw[TUNE].rg = 1; /* proceed */ if (!ocring) { ocr_other = 3; start_ocr(-2,OCR_OTHER,0); } new_mclip = 0; } else if ((pixmap != NULL) && (pm_t == 8)) show_hint(2,"cannot threshold after binarization"); else show_hint(2,"no graymap loaded"); } #ifdef PATT_SKEL else if (it == CM_E_RSKEL) { reset_skel_parms(); dw[TUNE_PATTERN].rg = 1; dw[TUNE_SKEL].rg = 1; new_mclip = 0; } #endif /* others */ else if ((it == CM_E_DOUBTS) || (it == CM_E_RESCAN) || (it == CM_E_FILL) || (it == CM_E_PP) || (it == CM_E_FILL_C) || (it == CM_E_PP_C)) { } } /* Items on "View" menu */ else if (cmenu-CM == CM_V) { /* set FAT BITS mode */ /* if ((cpage >= 0) && (it == CM_V_FAT)) { if (CDW != PAGE_FATBITS) { if ((CX0_orig >= DM) && (CY0_orig >= DT)) { X0 = (CX0_orig-DM) * RF; Y0 = (CY0_orig-DT) * RF; } setview(PAGE_FATBITS); check_dlimits(1); new_mclip = 0; } } */ /* change font */ if ((it == CM_V_SMALL) || (it == CM_V_MEDIUM) || (it == CM_V_LARGE) || (it == CM_V_DEF)) { /* set new font */ set_xfont(); /* recompute window size and components placement */ comp_wnd_size(-1,-1); set_mclip(0); /* recompute the placement of the windows */ setview(CDW); /* must regenerate HTML documents */ { int i; for (i=0; i<=TOP_DW; ++i) if (dw[i].html) dw[i].rg = 1; new_mclip = 0; } new_mclip = 0; } /* Recompute the geometry and redisplay the window to reflect the new value of the flag *cm_hide. */ if (it == CM_V_HIDE) { setview(CDW); new_mclip = 0; } /* generate again the list of patterns */ else if (it == CM_V_OF) { dw[PATTERN_LIST].rg = 1; new_mclip = 0; } /* Toggle view HTML source flag: must regenerate HTML documents */ else if (it == CM_V_VHS) { int i; for (i=0; i<=TOP_DW; ++i) if (dw[i].html) dw[i].rg = 1; new_mclip = 0; } /* recompute form contents */ else if (it == CM_V_WCLIP) { if (curr_mc >= 0) { dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } if ((0<=cdfc) && (cdfc<=topp)) { dw[PATTERN].rg = 1; redraw_dw = 1; } new_mclip = 0; } /* toggle alphabet mapping display */ else if (it == CM_V_MAP) { /* recompute window size and the placement of the components */ comp_wnd_size(-1,-1); /* recompute the placement of the windows */ setview(CDW); new_mclip = 0; } /* these statuses must redraw the window */ else if (it == CM_V_CC) { new_mclip = 0; redraw_dw = 1; } /* toggle show skeleton tuning */ else if (it == CM_V_ST) { new_mclip = 0; } /* start presentation */ else if (it == CM_V_PRES) { #ifdef HAVE_SIGNAL if (pmode == 0) cpresentation(1); #else show_hint(1,"Presentation unsupported!"); #endif new_mclip = 0; } /* Clear status line to display progress warnings. */ else if ((it == CM_V_MAT) || (it == CM_V_CMP) || (it == CM_V_MAT_K) || (it == CM_V_CMP_K)) { show_hint(1,""); } } /* Items on "Options" menu */ else if (cmenu-CM == CM_O) { /* toggle PAGE only */ if (it == CM_O_PGO) { if ((0 <= cpage) && (cpage < npages)) { if ((pixmap != NULL) && (pm_t == 8) && (!cl_ready)) { *cm_o_pgo = 'X'; show_hint(1,"PAGE only is mandatory for graymaps"); } else { if (*cm_o_pgo == ' ') { opage_j1 = page_j1 = PT+TH+(PH-TH)/3; opage_j2 = page_j2 = PT+TH+2*(PH-TH)/3; } setview(PAGE); check_dlimits(0); } } new_mclip = 0; } /* others */ else if ((it == CM_O_CURR) || (it == CM_O_ALL) || (it == CM_O_DKEYS) || (it == CM_O_AMENU)) { } } /* Items on "DEBUG" menu */ else if (cmenu-CM == CM_G) { if ((it == CM_G_ALIGN) || (it == CM_G_GLINES) || (it == CM_G_LB) || (it == CM_G_BO) || (it == CM_G_IO) || (it == CM_G_SUM)) { new_mclip = 0; redraw_dw = 1; } /* clear matches */ else if (it == CM_G_CM) { clear_cm(); dw[PATTERN_LIST].rg = 1; new_mclip = 0; } } /* Items on "PAGE options" menu */ else if (cmenu-CM == CM_D) { int i,j,x,y,t; { int last_dw; last_dw = CDW; CDW = PAGE; i = (CX0_orig-DM) / GS; j = (CY0_orig-DT) / GS; x = X0 + i*RF; y = Y0 + j*RF; t = symbol_at(x,y); CDW = last_dw; } /* use as pattern */ if ((t>=0) && (it == CM_D_TS)) { new_mclip = 0; this_pattern = mc[t].bm; } /* OCR this symbol */ else if ((t>=0) && (it == CM_D_OS)) { if (ocring == 0) { new_mclip = 0; justone = 1; curr_mc = t; start_ocr(-2,OCR_CLASS,1); } } /* make (CX0_orig,CY0_orig) the bottom left */ else if (it == CM_D_HERE) { i = (CX0_orig-DM) / GS; j = (CY0_orig-DT) / GS; if ((i > 0) && (j > 0)) { X0 += i * RF; Y0 -= (VR - j - 1) * RF; check_dlimits(1); } new_mclip = 0; } /* focus symbol t on PAGE_FATBITS */ else if ((t >= 0) && (it == CM_D_FOCUS)) { setview(PAGE_FATBITS); i = (mc[t].l + mc[t].r - HR) / 2; j = (mc[t].t + mc[t].b - VR) / 2; if ((i > 0) && (j > 0)) { X0 = i * RF; Y0 = j * RF; RG = 1; check_dlimits(1); } new_mclip = 0; } /* merge fragment with the current symbol */ else if (it == CM_D_ADD) { if ((curr_mc >= 0) && (t >= 0)) { start_ocr(-2,OCR_REV,0); to_arg = t; to_rev = REV_MERGE; } new_mclip = 0; } /* create symbol link */ else if (it == CM_D_SLINK) { if ((t >= 0) && (mc[t].sw<0)) { start_ocr(-2,OCR_REV,0); to_arg = t; to_rev = REV_SLINK; } new_mclip = 0; } /* disassemble symbol */ else if (it == CM_D_DIS) { if ((curr_mc >= 0) && (t >= 0)) { start_ocr(-2,OCR_REV,0); to_rev = REV_DIS; } new_mclip = 0; } /* create accent link */ else if (it == CM_D_ALINK) { if ((t >= 0) && (mc[t].sw<0)) { start_ocr(-2,OCR_REV,0); to_arg = t; to_rev = REV_ALINK; } new_mclip = 0; } /* diagnose symbol pairing */ else if (it == CM_D_SDIAG) { if ((curr_mc >= 0) && (t >= 0)) diag_pairing(t); new_mclip = 0; } /* diagnose word pairing */ else if (it == CM_D_WDIAG) { if ((curr_mc >= 0) && (t >= 0)) diag_wpairing(t); new_mclip = 0; } /* diagnose merging */ else if (it == CM_D_GM) { if ((curr_mc >= 0) && (t >= 0)) diag_geomerge(t); new_mclip = 0; } /* these statuses must redraw the window */ else if ((it == CM_D_PIXELS) || (it == CM_D_CLOSURES) || (it == CM_D_SYMBOLS) || (it == CM_D_WORDS) || (it == CM_D_PTYPE)) { new_mclip = 0; redraw_dw = 1; } /* report scale */ else if (it == CM_D_RS) { new_mclip = 0; redraw_tab = 1; } /* display box instead of symbol */ else if (it == CM_D_BB) { new_mclip = 0; redraw_tab = 1; if (dw[PAGE].v) redraw_dw = 1; } } /* Items on "PAGE_FATBITS options" menu */ else if (cmenu-CM == CM_B) { int i,j,x,y,t; { int last_dw; last_dw = CDW; CDW = PAGE_FATBITS; i = (CX0_orig-DM) / GS; j = (CY0_orig-DT) / GS; x = X0 + i*RF; y = Y0 + j*RF; t = closure_at(x,y,0); CDW = last_dw; } /* make (CX0_orig,CY0_orig) the bottom left */ if (it == CM_B_HERE) { if ((i > 0) && (j > 0)) { X0 += i * RF; Y0 -= (VR - j - 1) * RF; dw[PAGE_FATBITS].rg = 1; check_dlimits(1); } new_mclip = 0; } /* centralize the specified closure */ else if (it == CM_B_CENTRE) { if (t >= 0) { i = (cl[t].l + cl[t].r) / 2; j = (cl[t].b + cl[t].t) / 2; X0 = i - FS/2; Y0 = j - FS/2; dw[PAGE_FATBITS].rg = 1; check_dlimits(1); } new_mclip = 0; } /* when changing these presentation flags, some windows must be regenerated. */ else if ((it == CM_B_SKEL) || (it == CM_B_BORDER) || (it == CM_B_HS) || (it == CM_B_HB)) { /* must copy pattern to cfont again */ p2cf(); dw[PAGE_FATBITS].rg = 1; new_mclip = 0; } /* compute border path */ else if (it == CM_B_BP) { if (t>=0) { closure_border_path(t); } new_mclip = 0; } /* compute straight lines on border using pixel distance */ else if (it == CM_B_SLD) { short r[256]; if (t>=0) { closure_border_slines(t,8,2,0.36,r,128,0); } new_mclip = 0; } /* compute straight lines on border using correlation */ else if (it == CM_B_SLC) { short r[256]; if (t>=0) { closure_border_slines(t,8,1,0.9,r,128,0); } new_mclip = 0; } /* apply isbar test */ else if (it == CM_B_ISBAR) { if (t >= 0) { int r; float sk,bl; if ((r=isbar(t,&sk,&bl)) > 0) { show_hint(2,"seems to be a bar, skew=%1.3f (rad), len=%3.1f mm",sk,bl); } else { char *d; if (r == -1) d = "too small"; else if (r == -2) d = "too large"; else if (r == -3) d = "too wide"; else if (r == -4) d = "too complex"; else if (r == -5) d = "nonparallel borders"; else if (r == -6) d = "too thin/non straight"; else d = "unknown"; show_hint(2,"does not seem to be a bar (%s)",d); } } new_mclip = 0; } /* detect extremities */ else if (it == CM_B_DX) { if (t >= 0) { int n; n = dx(t,-1,-1); show_hint(2,"%d extremities found",n); } new_mclip = 0; } } /* Items on "Pattern" menu */ else if (cmenu-CM == CM_R) { int pages; pages = (*cm_o_all != ' ') ? -1 : -2; /* cannot start new operation */ if (ocring) { } /* start preproc */ else if (it == CM_R_PREPROC) { start_ocr(pages,OCR_PREPROC,0); } /* start detection of blocks */ else if (it == CM_R_BLOCK) { start_ocr(pages,OCR_BLOCK,0); } /* start binarization and segmentation */ else if (it == CM_R_SEG) { start_ocr(pages,OCR_SEG,0); } /* start consisting structures */ else if (it == CM_R_CONS) { start_ocr(pages,OCR_CONS,0); } /* start preparing patterns */ else if (it == CM_R_OPT) { start_ocr(pages,OCR_OPT,0); } /* start reading revision data */ else if (it == CM_R_REV) { start_ocr(pages,OCR_REV,0); } /* start classification */ else if (it == CM_R_CLASS) { start_ocr(pages,OCR_CLASS,0); } /* start geometric merging */ else if (it == CM_R_GEOMERGE) { start_ocr(pages,OCR_GEOMERGE,0); } /* start */ else if (it == CM_R_BUILD) { start_ocr(pages,OCR_BUILD,0); } /* start spelling */ else if (it == CM_R_SPELL) { start_ocr(pages,OCR_SPELL,0); } /* start building words and lines */ else if (it == CM_R_OUTP) { start_ocr(pages,OCR_OUTP,0); } /* start generating doubts */ else if (it == CM_R_DOUBTS) { start_ocr(pages,OCR_DOUBTS,0); } new_mclip = 0; } /* Items on "Alphabets" menu */ else if (cmenu-CM == CM_A) { set_bl_alpha(); redraw_map = 1; } /* must dismiss the context menu */ if (cmenu->t[a] & CM_DI) { cmenu = NULL; CX0_orig = CY0_orig = -1; /* workaround for some PAGE redraw problems */ if (dw[PAGE].v) new_mclip = 0; set_mclip(new_mclip); force_redraw(); } else { redraw_menu = 1; } return(1); } /* Press key (for presentations). */ int press(char c,int *delay) { xev.type = KeyPress; switch (c) { case 'r': xev.xkey.keycode = 27; break; case 'h': xev.xkey.keycode = 43; break; } have_s_ev = 1; *delay = 500000; return(0); } /* Click menu item (for presentations). */ int mselect(int item,int *delay) { int m,x,y=MH/2,px,py; XButtonEvent *be; m = item >> CM_IT_WD; x = ((CM[m].p*DFW) + ((CM[m].p+strlen(CM[m].tt))*DFW)) / 2; /* get pointer coordinates */ get_pointer(&px,&py); /* go to menu */ if (((cmenu == NULL) || ((cmenu-CM) != m)) && ((px != x) || (py != y))) { XWarpPointer(xd,None,XW,0,0,0,0,x,y); *delay = 1000000; return(1); } /* open menu */ else if (cmenu == NULL) { xev.type = ButtonPress; xev.xbutton.x = px; xev.xbutton.y = py; be = (XButtonEvent *) &xev; be->button = Button1; have_s_ev = 1; *delay = 500000; return(1); } /* no item selected */ else if ((cmenu->c < 0) || ((cmenu->c + ((cmenu-CM) << CM_IT_WD)) != item)) { int i; i = item & CM_IT_M; x = CX0 + CW - 10; y = i * (DFH + VS) + 5 + CY0 + DFH; XWarpPointer(xd,None,XW,0,0,0,0,x,y); *delay = 2000000; return(1); } /* click it */ else { xev.type = ButtonPress; xev.xbutton.x = px; xev.xbutton.y = py; be = (XButtonEvent *) &xev; be->button = Button1; have_s_ev = 1; *delay = 100000; return(0); } } /* Click symbol (for presentations). */ int sselect(int k,int *delay) { int x,y,px,py; XButtonEvent *be; /* get pointer coordinates */ get_pointer(&px,&py); /* go to tab */ if (!dw[PAGE].v) { y = PT + TH/2; x = PM + 10 + (PAGE) * (20+TW) + 10 + TW/2; /* move pointer */ if ((abs(px-x) > (TW/4)) || (abs(py-y) > TH/4)) { XWarpPointer(xd,None,XW,0,0,0,0,x,y); *delay = 1000000; return(1); } /* click the tab */ else { xev.type = ButtonPress; xev.xbutton.x = px; xev.xbutton.y = py; be = (XButtonEvent *) &xev; be->button = Button1; have_s_ev = 1; *delay = 500000; return(1); } } /* make sure that symbol k is visible */ else { int w,h; curr_mc = k; CDW = PAGE; x = DM + (((mc[k].l + mc[k].r) / 2) - X0) / RF; y = DT + (((mc[k].t + mc[k].b) / 2) - Y0) / RF; w = (mc[k].r - mc[k].l + 1) / RF; h = (mc[k].b - mc[k].t + 1) / RF; /* make the symbol visible */ if ((mc[curr_mc].l < X0) || (mc[curr_mc].r >= X0 + HR) || (mc[curr_mc].b < Y0) || (mc[curr_mc].t >= Y0 + VR)) { check_dlimits(1); force_redraw(); *delay = 1000000; return(1); } /* move to symbol */ else if ((abs(px-x) > w/4) || (abs(py-y) > w/4)) { XWarpPointer(xd,None,XW,0,0,0,0,x,y); *delay = 1000000; return(1); } /* click it */ else { xev.type = ButtonPress; xev.xbutton.x = px; xev.xbutton.y = py; be = (XButtonEvent *) &xev; be->button = Button1; have_s_ev = 1; *delay = 500000; return(0); } } } /* Click button (for presentations). */ int bselect(int k,int *delay) { int x,y,px,py; XButtonEvent *be; /* get pointer coordinates */ get_pointer(&px,&py); { int w,h; curr_mc = k; CDW = PAGE; x = 10 + BW/2; y = PT + k * (BH+VS) + BH/2; w = BW; h = BH; /* warp to button */ if ((abs(px-x) > w/4) || (abs(py-y) > w/4)) { XWarpPointer(xd,None,XW,0,0,0,0,x,y); *delay = 1000000; return(1); } /* click it */ else { xev.type = ButtonPress; xev.xbutton.x = px; xev.xbutton.y = py; be = (XButtonEvent *) &xev; be->button = Button1; have_s_ev = 1; *delay = 30000000; return(0); } } } /* Load file (for presentations). */ int fselect(char *p,int *delay) { int x,y,px,py; XButtonEvent *be; /* get pointer coordinates */ get_pointer(&px,&py); /* go to tab */ if (!dw[PAGE_LIST].v) { y = PT + TH/2; x = PM + 10 + (PAGE) * (20+TW) + 10 + TW/2; /* move pointer */ if ((abs(px-x) > (TW/4)) || (abs(py-y) > TH/4)) { XWarpPointer(xd,None,XW,0,0,0,0,x,y); *delay = 1000000; return(1); } /* click the tab */ else { xev.type = ButtonPress; xev.xbutton.x = px; xev.xbutton.y = py; be = (XButtonEvent *) &xev; be->button = Button1; have_s_ev = 1; *delay = 500000; return(1); } } /* make sure that file imre.pbm is visible */ else { int i,w,h; for (i=0; (i<=topge) && ((ge[i].type != GE_TEXT) || (strcmp(p,ge[i].txt) != 0)); ++i); if (i > topge) { printf("nao achei %s\n",p); return(0); } curr_hti = i; x = DM + (((ge[i].l + ge[i].r) / 2) - X0) / RF; y = DT + (((ge[i].t + ge[i].b) / 2) - Y0) / RF; w = (ge[i].r - ge[i].l + 1) / RF; h = (ge[i].b - ge[i].t + 1) / RF; /* make the link visible */ if ((ge[curr_hti].l < X0) || (ge[curr_hti].r >= X0 + HR) || (ge[curr_hti].b < Y0) || (ge[curr_hti].t >= Y0 + VR)) { check_dlimits(1); force_redraw(); *delay = 1000000; return(1); } /* move to link */ else if ((abs(px-x) > w/4) || (abs(py-y) > w/4)) { XWarpPointer(xd,None,XW,0,0,0,0,x,y); *delay = 1000000; return(1); } /* click it */ else { xev.type = ButtonPress; xev.xbutton.x = px; xev.xbutton.y = py; be = (XButtonEvent *) &xev; be->button = Button1; have_s_ev = 1; *delay = 5000000; return(0); } } } /* Process keyboard events. */ void kactions(void) { /* statuses */ int ctrl,haveif; static int cx=0; /* other */ int ls; edesc *c; int n; /* Buffers required by the KeyPress event */ unsigned char buffer[101]; KeySym ks; XComposeStatus cps; /* We're waiting any keyboard event. */ /* any keyboard event unblocks the OCR */ if (waiting_key == 1) { waiting_key = 0; show_hint(3,NULL); if (nopropag == 0) return; } n = XLookupString(&(xev.xkey),(char *)buffer,100,&ks,&cps); /* We're waiting ENTER or ESC keypresses. Discard anything else. */ if (waiting_key == 2) { if ((ks == 0xff0d) || (ks == 0xff1b)) { waiting_key = 0; mb[0] = 0; redraw_stline = 1; key_pressed = (ks == 0xff0d); } return; } /* We're waiting y, Y, n or N keypresses. Discard anything else. */ /* Discard events other than y, Y, n or N */ if (waiting_key == 3) { if ((ks == 'y') || (ks == 'Y') || (ks == 'n') || (ks == 'N')) { waiting_key = 0; mb[0] = 0; redraw_stline = 1; key_pressed = ((ks == 'y') || (ks == 'Y')); } return; } /* We're waiting <, >, +, -, ENTER or ESC keypresses. Discard anything else. */ if (waiting_key == 5) { if ((ks == '>') || (ks == '<') || (ks == '+') || (ks == '-')) { waiting_key = 0; mb[0] = 0; redraw_stline = 1; key_pressed = ks; } else if ((ks == 0xff0d) || (ks == 0xff1b)) { waiting_key = 0; mb[0] = 0; redraw_stline = 1; key_pressed = 0; } return; } /* the C-g prefix */ ctrl = (xev.xkey.state & ControlMask); if (ctrl && (ks == 103)) { cx = 0; return; } /* the C-x prefix */ ctrl = (xev.xkey.state & ControlMask); if (ctrl && (ks == 120)) { cx = 1; return; } /* C-x special actions */ if (cx) { switch (ks) { #ifdef TEST /* C-x b: barcode detection */ case 98: search_barcode(); break; #endif /* C-x d: dump current symbol */ case 100: if ((curr_mc < 0) || (tops < curr_mc)) { warn("invalid symbol"); } else if (pixel_mlist(curr_mc) < 0) { warn("symbol too large"); } else { dump_cb(); } break; /* C-x x: pp_test */ case 120: test(); break; /* ** EXPERIMENTAL STUFF ** ------------------------ Don't try unless you really know what you're doing! */ #ifdef TEST /* C-x c: clusterization test */ case 99: test_clusterize(); break; /* C-x i: run all internal tests */ case 105: internal_tests(); break; /* C-x p: enter pattern (action) */ case 112: setview(PATTERN_ACTION); break; /* C-x r: reset */ case 114: reset(); break; /* C-x s: Candido de Figueiredo post-segmentation blockfind */ case 115: cf_block(); break; /* C-x t: image info */ case 116: if (!ocring) { ocr_other = 4; start_ocr(-2,OCR_OTHER,0); } break; #endif } cx = 0; return; } /* Is there an active text input field? */ c = ge + curr_hti; if ((0 <= curr_hti) && (curr_hti <= topge) && (c->type == GE_INPUT)) { haveif = 1; /* ignore ESC prefix */ if (nopropag) { nopropag = 0; mb[0] = 0; redraw_stline = 1; } } else if (waiting_key == 4) haveif = 2; else haveif = 0; /* Control-L */ if ((n == 1) && (buffer[0] == 12)) force_redraw(); /* Backspace within text input field */ else if ((n == 1) && (haveif) && (ks == 0xffff)) { if (haveif == 1) { if ((ls=strlen(c->txt)) > 0) (c->txt)[ls-1] = 0; redraw_inp = 1; form_changed = 1; } else { if ((ls=strlen(inp_str)) > inp_str_ps) (inp_str)[ls-1] = 0; redraw_inp_str(); } } /* Visible char */ else if ((n == 1) && (' ' <= buffer[0])) { static int l=-1; int v; /* apply composition */ if (*cm_o_dkeys == 'X') v = compose(&l,buffer[0]); else { v = 1; l = buffer[0]; } /* add to HTML input field */ if (haveif == 1) { ls = strlen(c->txt) + 1; if (v) { if ((ls >= c->tsz) && (c->tsz < MFTL+1)) c->txt = c_realloc(c->txt,c->tsz=MFTL+1,NULL); if (ls < c->tsz) { (c->txt)[ls-1] = l; (c->txt)[ls] = 0; l = -1; } } redraw_inp = 1; form_changed = 1; } /* add to HTML input field */ else if (haveif == 2) { ls = strlen(inp_str) + 1; if (v) { if (ls < MMB) { (inp_str)[ls-1] = l; (inp_str)[ls] = 0; l = -1; } } redraw_inp_str(); } /* submit symbol transliteration from PAGE window */ else if (v && dw[PAGE].v && (curr_mc >= 0)) { /* Obs. we initialize the transliteration buffer to_tr after the start_ocr call becase start_ocr clear the buffer to_tr. */ start_ocr(-2,OCR_REV,0); to_tr[0] = l; to_tr[1] = 0; to_rev = REV_TR; l = -1; } /* add pattern from the spyhole buffer */ else if (v && dw[PAGE].v && (spyhole(0,0,2,0)==1)) { cdfc = -1; start_ocr(-2,OCR_REV,0); to_tr[0] = l; to_tr[1] = 0; to_rev = REV_PATT; l = -1; } /* submit pattern transliteration from PATTERN window */ else if (v && dw[PATTERN].v) { pdesc *s; if ((0 <= cdfc) && (cdfc <= topp)) { s = pattern + cdfc; /* Obs. we initialize the transliteration buffer to_tr after the start_ocr call becase start_ocr clear the buffer to_tr. */ start_ocr(-2,OCR_REV,0); to_tr[0] = l; to_tr[1] = 0; to_rev = REV_PATT; l = -1; } } /* switch page (for tests) ----------------------- Manually reads the first, second or third page. To use this feature, press "1", "2" or "3" on the PAGE (list) window (of course there must exist at least pages on the current directory). This is used only for debugging purposes. */ /* else if (CDW == PAGE_LIST) { if (l == '1') start_ocr(0,OCR_LOAD,0); else if (l == '2') start_ocr(1,OCR_LOAD,0); else if (l == '3') start_ocr(2,OCR_LOAD,0); l = -1; } */ /* show the GPL */ else if (v && ((l=='l') || (l=='L')) && (CDW == WELCOME)) { setview(GPL); redraw_dw = 1; l = -1; } } /* Special keys */ else switch (ks) { /* Left */ case 0xff51: left(); break; /* Up */ case 0xff52: up(); break; /* Right */ case 0xff53: right(); break; /* Down */ case 0xff54: down(); break; /* Prior (pgup) */ case 0xff55: prior(); break; /* Next (pgdw) */ case 0xff56: next(); break; /* END */ case 0xff57: if ((XRES - HR) >= 0) { X0 = XRES - HR; } break; /* HOME */ case 0xff50: X0 = 0; break; /* ENTER */ case 0xff0d: /* select current menu item */ if (cmenu != NULL) { cma(cmenu->c + ((cmenu-CM) << CM_IT_WD),0); } /* ENTER pressed with active input field provoke form submission. */ else if (((CDW == PAGE_SYMBOL) || (CDW == TUNE) || (CDW == TUNE_SKEL) || (CDW == PATTERN)) && (haveif == 1)) { form_auto_submit(); } /* finished edition of input string */ else if (haveif == 2) { waiting_key = 0; redraw_inp_str(); memmove(inp_str,inp_str+inp_str_ps,strlen(inp_str)-inp_str_ps+1); } break; /* ESC */ case 0xff1b: /* dismiss current menu */ if (cmenu != NULL) dismiss(1); /* toggle BAD property */ else { button[bbad] ^= 1; redraw_button = bbad; form_changed = 1; form_auto_submit(); /* nopropag = 1-nopropag; if (nopropag) enter_wait("ESC",0,1); */ } /* skip current doubt */ /* else if (CDW == PAGE_SYMBOL) { ++curr_doubt; edit_curr_doubt(); } */ break; /* C-x prefix */ /* case 0xff1b: */ default: db("unattended keycode %ld",ks); break; } } /* Select the window where the mouse pointer is. */ void select_dw(int x,int y) { int a,last_dw; get_pointer(&x,&y); /* loop all active windows */ last_dw = CDW; if (*cm_v_hide == ' ') a = 10+RW; else a = 0; for (CDW=0; CDW<=TOP_DW; ++CDW) { if ((dw[CDW].v) && (DM-10 <= x) && (x < DM+DW+(DS?a:10)) && (DT-10 <= y) && (y < DT+DH+(DS?a:10))) { if (CDW != last_dw) { redraw_tab = 1; } return; } } /* no one was selected */ CDW = last_dw; } /* Detect the item from the menu bar currently under the pointer. */ int mb_item(int x,int y) { if ((0 < y) && (y < MH)) { int i; for (i=0; i<=TOP_CM; ++i) { if ((CM[i].a == 1) && (CM[i].p * DFW <= x) && (x <= (CM[i].p+strlen(CM[i].tt)) * DFW)) { return(i); } } return(-2); } return(-1); } /* This is mostly a trigger to execute actions when a menu is opened. Anyway, it also initializes some variables. */ void open_menu(int x,int y,int m) { CX0 = x; CY0 = y; if (cmenu == NULL) form_auto_submit(); cmenu = CM + m; redraw_menu = 1; show_hint(3,NULL); } /* Spyhole operations. The operation performed depends on the value of the op parameter: If (op == 0), try to apply the spyhole at (x,y). "Apply the spyhole at (x,y)" means to backup a rectangle around (x,y), search a "good" threshold to use in this rectangle. Returns 1 if found, 0 otherwise. If (op == 1), just give back the spyhole buffer (if any) to the graymap and returns. Returns 0. If (op == 2), copy the binarized symbol (if any) to cb. Returns 1 if success, 0 otherwise. If (op == 3), copy the binarized symbol (if any) to cmp_bmp. Returns 1 if success, 0 otherwise. If (op == 4) make as if (op == 0), but do not backup the spyhole nor apply the binarization result to the pixmap, nor compute the next threshold (faster, non-visual). The parameter et is the expect threshold. It's meaningful only when (op==0) or (op==4). Thresholds not multiple of TS are not tried. WARNING: the following parameters are currently hardcoded: TS sh_hs nhs relax */ int spyhole(int x,int y,int op,int et) { /* Bitmap buffers: bb is the main buffer. For each threshold, the binarization result is computed using bb, as well as the result of the spanning of the component from the seed (x,y). bc stores the bitmap got from the preferred threshold until now. bd and be are used to store, in some cases, the bitmap before the spanning of the component, in order to detect merging. */ unsigned char *bb,*bc,*bd,*be; int bpl,bb_sz,snp; /* When spyhole() finds a usable threshold, it stores the threshold result in this buffer, so it can be recovered afterwards using op==2. */ static unsigned bs[BMS/4]; static int bsf = 0,bs_x0,bs_y0; /* the spyhole static buffer */ static unsigned char *eg=NULL; static int sh_sz,last_x0=-1,last_y0,last_dx,last_dy = 0; /* the spyhole rectangle */ int x0,y0,dx,dy,sh_hs; /* current, best and maximum tried thresholds */ int thresh,bt,mt; /* first usable threshold */ int ft; /* best class found */ int bcp; /* the seed */ int r,u,v,u0,v0,nhs; /* other */ int j,k,l,ret; unsigned char *p,*q; /* white and black threshold limits */ int WL,BL; /* threshold search control */ int L,R,st,tries; /* per-threshold width, height, classification result and seed */ int W[257],H[257],K[257],SX[257],SY[257]; /* Threshold Stepper. It's expected to be 1, 2, 4, 8 or 16. The larger it is, the faster the service should run, and less sensible it becomes. */ static int TS = 4; /* relaxed mode */ int relax = 1; /* spyhole half-size */ sh_hs = FS; /* Copy the binarized symbol to cb and returns. */ if (op == 2) { /* no available symbol */ if (bsf == 0) return(0); /* copy bs to cb */ bm2cb((unsigned char *)bs,0,0); return(1); } /* Copy the binarized symbol to cmp_bmp and returns. */ if (op == 3) { /* no available symbol */ if (bsf == 0) return(0); /* copy bs to cmp_bmp */ memcpy(cmp_bmp,bs,BMS); cmp_bmp_x0 = bs_x0; cmp_bmp_y0 = bs_y0; return(1); } /* give back the last spyhole (if any) to the pixmap and invalidate the buffer. */ if (last_x0 >= 0) { q = eg; for (j=last_y0, l=last_dy; l>0; --l, ++j) { p = pixmap + j*XRES + last_x0; for (k=last_dx; k>0; --k, ++p, ++q) { *p = *q; } } last_x0 = -1; bsf = 0; } /* done if operation 1 */ if (op == 1) { /* display the progress of the extraction of symbols performed by the local binarizer. */ if (*cm_g_lb != ' ') { if (CDW != PAGE) setview(PAGE); redraw_dw = 1; } return(0); } /* try to extend to a large enough spyhole rectangle */ { if ((x0=x-sh_hs) < 0) x0 = 0; dx = x - x0; if (x+sh_hs <= XRES) dx += sh_hs; else dx += (XRES-x); if ((y0=y-sh_hs) < 0) y0 = 0; dy = y - y0; if (y+sh_hs <= YRES) dy += sh_hs; else dy += (YRES-y); } /* The seed neighborhood half size. This may be seen as the error (in pixels) admitted when the user clicks the mouse to create the spyhole. */ nhs = 30; /* Not large enough: give up. The minimum width nhs is mandatory. */ u0 = x - x0; v0 = y - y0; if ((u0 < nhs) || ((x+nhs) >= (x0+dx)) || (v0 < nhs) || ((y+nhs) >= (y0+dy))) { return(0); } /* think 10% as a minimum, 90% as a maximum */ if (et > (230-TS)) et = 230-TS; if (et < (26+TS)) et = 26+TS; /* round the expected threshold */ if (((et % TS) * 2) > TS) et = (et-(et%TS)) + TS; else et = (et-(et%TS)); /* alloc bitmap buffers */ bpl = (dx / 8) + ((dx%8) != 0); bb_sz = dy * bpl; bb = alloca(bb_sz); bc = alloca(bb_sz); bd = alloca(bb_sz); be = alloca(bb_sz); /* Initial thresholds. */ bt = -1; bcp = -1; ft = -1; /* prepare threshold analysis */ for (j=0; j<256; ++j) W[j] = SX[j] = -1; L = R = WL = BL = -1; st = 1; snp = -1; /* This loop searches a "good" thresholding value. To analyse if one threshold is "good", the classifier is invoked. Various thresholding values around the expected threshold (the parameter et) are tried, until the classifier suceeds to classify the bitmap obtained by thresholding, or until the limits on both sides are reached. "Limits", in this context, are the "merge limits" and the "white" and "black" limits, to be explained below. As we enlarge the threshold, at some point the component spanned from (x,y) merges some neighbour. The last threshold value for which the merge does not occur is called "right merge limit". Similarly, as we reduce the threshold, at some point the component breaks into two or more pieces. The last threshold value for which the component didn't break is called "left merge limit". These limits are detected using the concept of spare pixels. Let t be a reference threshold. Let bd be the bitmap obtained by thresholding by t. Let c be another threshold. Let bb be the bitmap obtained by thresholding by c and subsequently spanning the component from (x,y). The spare pixels are the pixels black in bd but white in bb. As we enlarge c, if a merge occurs, then the number of spare pixels reduce. As we reduce c, if a break occurs, the number of spare pixels enlarge. Note that this behaviour probably depends on the method used to print the page being OCRized. The Clara OCR development is using exclusively ink-printed materials (old books). Laser-printed materials may behave differently. Now let's explain the other two limits. If the threshold gets too low, then we cannot span a component from (x,y). The largest threshold for which no component is found is called "white limit". If the threshold gets too high, then it results a completely black rectangle (or something close to it). The lowest threshold that produces a black rectangle is called "black limit". The thresholds larger than the right merge limit are also studied, in order to check if the expected threshold is not too low. Symbols with thin parts brake too easily. In fact, such symbols may require thresholds larger than the expected one, in order to have all pieces merged. */ for (bsf=0, st=1, mt=-1, thresh=et, tries=0; st <= 4; ) { if (thresh > mt) mt = thresh; /* apply the binarizer line by line */ memset(bb,0,bb_sz); for (j=y0, l=dy; l>0; --l, ++j) { int m; q = bb + (j-y0)*bpl; m = 128; p = pixmap + j*XRES + x0; for (k=dx; k>0; --k, ++p) { if (*p < thresh) *q |= m; if ((m >>= 1) == 0) { ++q; m = 128; } } } /* do we already have an interior pixel? */ if (SX[thresh] >= 0) { u = SX[thresh]; v = SY[thresh]; r = 1; } else { u = u0; v = v0; r = 0; } /* try to search an interior pixel close to (x,y) */ for (k=l=0; (l= 6) { r = pix(bb,bpl,u+1,v) && pix(bb,bpl,u-1,v) && pix(bb,bpl,u-1,v-1) && pix(bb,bpl,u,v-1) && pix(bb,bpl,u+1,v-1) && pix(bb,bpl,u-1,v+1) && pix(bb,bpl,u,v+1) && pix(bb,bpl,u+1,v+1); } /* 3-bit optimization (75%) */ else { r = pix(bb,bpl,u+1,v) && pix(bb,bpl,u-1,v) && pix3(bb,bpl,u-1,v-1) && pix3(bb,bpl,u-1,v+1); } } /* no, (u,v) isn't interior */ if (r == 0) { /* the path finished */ if (--k < 0) { /* radius and path size */ ++l; k = 8*l-1; } /* Compute next path position. To describe the method, let's draw the path for radius 1: 7 6 5 0 4 1 2 3 The corners are n*l-1 for n=2,4,6,8. We divide the path into four lines: Top line: 7, 6. Right line: 5, 4. Bottom line: 3, 2. Left line: 1, 0. The code below implements the general case. */ if (l < nhs) { /* top line: linear positions [(8*l-1)..(6*l-1)[ */ if (k > 6*l-1) { u = (u0-l) + ((8*l-1)-k); v = v0-l; } /* right line: linear positions [(6*l-1)..(4*l-1)[ */ else if (k > 4*l-1) { u = u0+l; v = (v0-l) + ((6*l-1)-k); } /* bottom line: linear positions [(4*l-1)..(2*l-1)[ */ else if (k > 2*l-1) { u = (u0+l) - ((4*l-1)-k); v = v0+l; } /* left line: linear positions [(2*l-1)..(0*l-1)[ */ else { u = u0-l; v = (v0+l) - ((2*l-1)-k); } } } } /* found one interior pixel */ if (l < nhs) { /* apply the just found interior pixel to all thresholds above the current one */ for (j=thresh; (j<=256) && (SX[j] < 0); ++j) { SX[j] = u; SY[j] = v; } /* Copy the threshold result. We'll use it to detect merging. */ if (bt < 0) { /* copy to the true buffer that stores the threshold result */ if (ft < 0) memcpy(bd,bb,bb_sz); /* copy to an auxiliar buffer, because if the classification succeeds, we'll swap bd and be. */ else memcpy(be,bb,bb_sz); } } /* No interior pixel found: the current threshold produced only a big white rectangle (or something close to it). */ if (l >= nhs) { W[thresh] = 0; H[thresh] = 0; } /* If we got one interior pixel, try to span its component and classify it. */ else if ((r = border_path(bb,dx,dy,NULL,0,u,v,1)) == 0) { /* This threshold was already analysed. */ if (W[thresh] >= 0) { } /* Copy to cmp_bmp. The service justify_bitmap() returns true only if the component is not too large. */ else if (justify_bitmap(bb,dx,dy)) { bsf = 1; /* remember the symbol dimensions */ W[thresh] = cmp_bmp_w; H[thresh] = cmp_bmp_h; /* No successfull classification was achieved until now. */ if (bt < 0) { /* try to classify it */ classify(-1,NULL,0); while (classify(0,bmpcmp_skel,3)); /* success! */ if (cp_result >= 0) { /* assign class to threshold */ K[thresh] = bcp = cp_result; /* take threshold as the best one */ bt = thresh; /* remember the bitmap in full and reduced sizes */ if (op == 0) memcpy(bc,bb,bb_sz); memcpy(bs,cmp_bmp,BMS); /* absolute top left coordinates */ bs_x0 = x0 + cmp_bmp_dx; bs_y0 = y0 + cmp_bmp_dy; /* swap bd and be because the pre-spanning bitmap is being pointed by be, not bd. */ if (ft >= 0) { unsigned char *a; a = bd; bd = be; be = a; } /* compute the number of spare pixels */ for (p=bd,q=bb, j=snp=0; j= 0) && (thresh < et) && (n > (snp+25))) { L = thresh + TS; } /* overpassed right merge limit */ else if ((snp >= 0) && (thresh > et) && (n < (snp-25))) { R = thresh - TS; memcpy(be,bb,bb_sz); } /* achieved black limit */ if ((d=swh(W[thresh],H[thresh])) > 0) { BL = thresh; if (thresh > et) R = thresh - TS; } /* achieved white limit */ else if (d < 0) { WL = thresh; if (thresh < et) L = thresh + TS; } /* If the classification succeeded, compute the next threshold, or do nothing more if fast mode. */ if (bt >= 0) { if (op == 4) st = 5; else st = 2; } /* otherwise prepare next try */ else for (f=t=0; f==0; ) { /* compute new threshold value */ ++tries; if (tries&1) thresh = et + ((tries+1)/2)*TS; else thresh = et - (tries/2)*TS; /* the new threshold value is outside the valid ranges */ if ((thresh <= 0) || (thresh >= 256) || ((L >= 0) && (thresh < L)) || ((R >= 0) && (thresh > R)) || ((WL >= 0) && (thresh <= WL)) || ((BL >= 0) && (thresh >= BL))) { /* two consecutive failures: give up */ if (++t >= 2) { /* no successfull threshold: abandon completely */ if ((ft < 0) && (bt < 0)) st = 5; /* try to find upper limit */ else st = 2; f = 1; } } /* the new threshold value can be tryed */ else { f = 1; } } } /* step 2: prepare to search upper limit using binary search */ if (st == 2) { /* if step 1 already found the right limit, jump to step 4 */ if (R >= 0) { L = R + TS; st = 4; } /* otherwise prepare binary search */ else { L = (bt >= 0) ? bt : ft; R = 256; thresh = (L+R)/2; st = 3; } } /* step 3: search upper limit using binary search. The hypothesis is L is too low, R is large enough. */ else if (st == 3) { int n; /* compute the number of spare pixels */ for (p=bd,q=bb, j=n=0; j (W[L]+5)) || (H[thresh] > (H[L]+5))) { */ R = thresh; memcpy(be,bb,bb_sz); } else { L = thresh; } /* achieved black limit */ if (swh(W[thresh],H[thresh]) > 0) { BL = thresh; } /* prepare next try or next step */ if ((L+1) >= R) { L = R; st = 4; } else { thresh = (L+R)/2; } } /* step 4: should we try the upper threshold segment? */ if (st == 4) { /* no */ st = 5; } } } /* do we have a usable threshold? */ ret = ((bt >= 0) || (ft >= 0)); /* spyhole buffer */ if (op == 0) { /* copy the symbol to the spyhole buffer */ if (dx*dy > sh_sz) { eg = c_realloc(eg,sh_sz=dx*dy,NULL); } q = eg; for (j=y0, l=dy; l>0; --l, ++j) { p = pixmap + j*XRES + x0; for (k=dx; k>0; --k, ++p, ++q) { *q = *p; } } /* If no threshold is found usable, then let a white rectangle be the "best" threshold result. */ if (ret == 0) { memset(bc,0,bb_sz); memset(be,0,bb_sz); } /* copy the best threshold result to the pixmap */ for (j=y0, l=dy; l>0; --l, ++j) { int m; unsigned char *o; q = bc + (j-y0)*bpl; o = be + (j-y0)*bpl; m = 128; p = pixmap + j*XRES + x0; for (k=dx; k>0; --k, ++p) { /* Pixels on the best threshold result are painted black. The others are painted gray when below the next tryable treshold, or white otherwise. Obs. if you're thinking about adding a black frame to the spyhole, it's not an easy task because it depends on the reduction factor. If you paint black the pixels on the horizontal or vertical limits of the spyhole, the frame may be drawn only partially due to the sampling of the pixels to be copied to the display. */ if (*q & m) *p = 0; else if (*o & m) *p = 192; else *p = 255; /* prepare mask */ if ((m >>= 1) == 0) { ++q; ++o; m = 128; } } } /* remember the spyhole coords */ last_x0 = x0; last_y0 = y0; last_dx = dx; last_dy = dy; /* tell the user about the result */ if (bt >= 0) { int i,l; char *t,a[MMB+1]; i = id2idx(bcp); snprintf(a,MMB,"threshold %d got successfull classification ",bt); l = strlen(a); if ((t=pattern[i].tr) != NULL) snprintf(a+l,MMB,"\"%s\" (next %d)",t,L); else snprintf(a+l,MMB,"(next %d)",L); a[MMB] = 0; show_hint(2,a); } else if (ft >= 0) show_hint(2,"using threshold %d (next %d)",ft,L); else show_hint(2,"could not find a useful threshold"); /* must refresh the display */ redraw_dw = 1; } /* accumulate tries */ sh_tries += tries; /* operations 0 and 4 result */ return(ret); } /* Mouse actions activated by mouse button 1. */ int mactions_b1(int help,int x,int y) { int i,j,c,sb; int pb,ob; /* coordinates depends on scrollbars hiding */ if (*cm_v_hide == ' ') sb = 10 + RW; else sb = 0; /* Context menu ------------ */ if (cmenu != NULL) { if (help) { return(1); } /* process current item */ if ((0 <= cmenu->c) && (cmenu->c < cmenu->n)) { cma(cmenu->c + ((cmenu-CM) << CM_IT_WD),0); return(0); } /* dismiss context menu */ else { dismiss(1); } } /* change to selected window */ select_dw(x,y); /* Menu bar -------- */ if ((i=mb_item(x,y)) >= 0) { if (help) { /* select automagically */ if ((*cm_o_amenu == 'X') && (cmenu == NULL)) { open_menu(x,y,i); return(1); } return(1); } open_menu(x,y,i); return(0); } /* Selection of tabs ----------------- */ if ((PM+10 <= x) && (x < PM+10+(TABN-1)*(TW+20)+TW) && (PT <= y) && (y <= PT+TH) && ((x - PM - 10) % (20 + TW) <= TW)) { i = (x - PM - 10) / (20 + TW); /* cannot circulate modes during OCR because the data structures are not ready (in some cases would segfault). */ if (ocring) { /* do nothing */ } /* show document */ else if (i == PAGE) { if (help) { if (tab == PAGE) if (cpage >= 0) show_hint(0,"click to circulate modes"); else show_hint(0,"select a file to load"); else show_hint(0,"click to enter page modes"); return(1); } if ((CDW == PAGE_FATBITS) || (cpage == -1)) setview(PAGE_LIST); else if (CDW == PAGE_LIST) setview(PAGE); else if (dw[PAGE].v) setview(PAGE_FATBITS); else setview(PAGE_CM); } /* font editor */ else if (i == PATTERN) { if (help) { if (tab == PATTERN) show_hint(0,"click to circulate modes"); else show_hint(0,"click to browse and edit the patterns"); return(1); } if (tab != PATTERN) { /* BUG: this call will renderize again the PROPS form. So if the user edit the input buffer, change the tab, and after go back to PATTERN, the data on the input field will be lost. */ /* edit_pattern(); */ setview(PATTERN_CM); } else if (dw[PATTERN].v) setview(PATTERN_LIST); else if (dw[PATTERN_LIST].v) setview(PATTERN_TYPES); else setview(PATTERN); } /* broser of acts */ else if (i == TUNE) { if (help) { if (tab != TUNE) show_hint(0,"click to tune the OCR"); return(1); } if (tab != TUNE) { setview(TUNE_CM); } else if (dw[TUNE].v) { setview(TUNE_SKEL); } else if (dw[TUNE_SKEL].v) { setview(TUNE_ACTS); } else setview(TUNE); } } /* Alphabet map ------------ */ else if ((*cm_v_map != ' ') && (alpha != NULL) && (PM-50 <= x) && (x < PM-50+alpha_cols*12+alpha_cols-1) && (PT+1 <= y) && (y < PT+1+alpha_sz*15+alpha_sz-1)) { /* map column */ i = (x-PM+50) / 13; /* map row */ j = (y-PT-1) / 16; if (help) { char lb[MMB+1]; lb[0] = lb[MMB] = 0; if ((0 <= j) && (j < alpha_sz)) { /* digits */ if ((i == 0) && (alpha == number)) snprintf(lb,MMB,"this is the digit \"%s\"",alpha[j].ln); /* first column is the capital case */ else if ((i == 0) && (alpha_cols >= 2) && (alpha[j].cc > 0)) snprintf(lb,MMB,"this is the letter \"%s\", capital",alpha[j].ln); /* second column is the small case */ else if ((i == 1) && (alpha_cols >= 2) && (alpha[j].sc > 0)) snprintf(lb,MMB,"this is the letter \"%s\", small",alpha[j].ln); /* first column for alphabets without small case */ else if ((i == 0) && (alpha_cols < 2) && (alpha[j].cc > 0)) snprintf(lb,MMB,"this is the letter \"%s\"",alpha[j].ln); /* third column is the transliteration */ else if ((i == 2) && (alpha_cols >= 3)) snprintf(lb,MMB,"this is the latin keyboard map for letter \"%s\"",alpha[j].ln); } show_hint(0,lb); return(1); } } /* window ------ */ else if ((DM <= x) && (x < DM+DW) && (DT <= y) && (y < DT+DH)) { i = (x-DM) / GS; j = (y-DT) / GS; #ifdef FUN_CODES /* Toggle fun code 1. */ if (CDW == WELCOME) { if (help) { return(1); } if (fun_code == 0) { fun_code = 1; redraw_dw = 1; new_alrm(50000,1); } else if (fun_code == 1) { fun_code = 0; redraw_dw = 1; } return(0); } #endif /* define OCR zone +-----+ |1 4| | | |2 3| +-----+ */ if ((dw[PAGE].v) && (button[bzone] != 0)) { int k,s,p; char lb[MMB+1]; if (help) { show_hint(0,""); return(1); } p = 8 * zones; if ((0 <= curr_corner) && (curr_corner < 2)) { limits[p+4*curr_corner] = X0 + i * RF; limits[p+4*curr_corner+1] = Y0 + j * RF; if (++curr_corner > 1) { limits[p+2] = limits[p+0]; limits[p+3] = limits[p+5]; limits[p+6] = limits[p+4]; limits[p+7] = limits[p+1]; button[bzone] = 0; ++zones; sess_changed = 1; redraw_zone = 1; } lb[MMB] = 0; s = snprintf(lb,MMB,"limits:"); for (k=0; k<4; ++k) { s += snprintf(lb+s,MMB," (%d,%d)", limits[p+2*k],limits[p+2*k+1]); } show_hint(1,lb); } else { db("inconsistent value for limits_top"); button[bzone] = 0; } redraw_button = -1; } /* inform position at PAGE_FATBITS */ else if (CDW == PAGE_FATBITS) { /* Display information about the pixel (i,j). */ if (help) { int k,l,u,v,p; cldesc *c; float a; /* no closure by here: display nothing */ if ((k=closure_at(X0+i,Y0+j,0)) < 0) return(1); c = cl + k; /* make (i,j) closure-relative */ u = X0+i-c->l; v = Y0+j-c->t; /* (i,j) is outside the closure boundaries */ if ((u<0) || (v<0) || (u>c->r-c->l) || (v>c->b-c->t)) return(1); #ifdef MEMCHECK checkidx(topfp,FS*FS,"s_dist"); #endif /* search pixel on the flea path */ for (l=0, p=-1; (l<=topfp) && (p<0); ++l) { if ((fp[2*l]==u) && (fp[2*l+1]==v)) p = l; } /* display path data at (i,j) */ if (p>=0) { a = s2a(fsl[3*p],fsl[3*p+1],fsl[3*p+2],NULL,-1,0); a *= (180/PI); show_hint(0,"at (%d,%d), pp=%3.3f, a=%0.3f (deg)",u,v,fpp[p],a); } /* display the extremities heuristic results */ /* else dx(k,u,v); */ /* display the pixel coordinates */ else { show_hint(0,"at (%d,%d)",u,v); } return(1); } } /* paint pixel */ else if ((*cm_e_pp != ' ') && (CDW == PATTERN)) { if (help) { show_hint(0,"paint pixel"); return(1); } COLOR = BLACK; pp(i,j); redraw_dw = 1; } /* clear pixel */ else if ((*cm_e_pp_c != ' ') && (CDW == PATTERN)) { if (help) { show_hint(0,"clear pixel"); return(1); } COLOR = WHITE; pp(i,j); redraw_dw = 1; } /* fill region with current color */ else if ((*cm_e_fill != ' ') && (CDW == PATTERN)) { if (help) { show_hint(0,"paint region"); return(1); } COLOR = BLACK; c = cfont[i+j*FS]; pp(i,j); if (c != cfont[i+j*FS]) { cfont[i+j*FS] = c; pr(i,j,c); } redraw_dw = 1; } /* clear region with current color */ else if ((*cm_e_fill_c != ' ') && (CDW == PATTERN)) { if (help) { show_hint(0,"clear region"); return(1); } COLOR = WHITE; c = cfont[i+j*FS]; pp(i,j); if (c != cfont[i+j*FS]) { cfont[i+j*FS] = c; pr(i,j,c); } redraw_dw = 1; } /* go to hyperref */ else if (HTML) { /* no help for this item */ if (help) { /* show_hint(0,""); */ return(1); } show_hint(1,""); html_action(x,y); } /* select symbol */ else if (CDW == PAGE) { int k; /* when OCRing, some data structures may not be ready to the code below. */ if (ocring) return(0); /* get current symbol (if any) */ k = symbol_at(X0+i*RF,Y0+j*RF); if (help) { sdesc *m; char *a; char lb[MMB+1]; lb[0] = 0; mb[MMB] = 0; if (k >= 0) { m = mc + k; a = ((m->tr) == NULL) ? "?" : ((m->tr)->t); } else { m = NULL; a = NULL; } /* Display current word. */ if (*cm_d_words != ' ') { /* no current word */ if ((k < 0) || (m->sw < 0)) return(0); snprintf(lb,MMB,"word %d%s",m->sw, (word[m->sw].f & F_ITALIC) ? " italic" : ""); } /* Display pixel coordinates instead of symbol id. */ else if (*cm_d_pixels != ' ') { int x,y; x = X0 + i*RF; y = Y0 + j*RF; if ((cl_ready) || (pm_t != 8)) show_hint(0,"at (%d,%d)",x,y); else show_hint(0,"at (%d,%d), color %d",x,y,pixmap[y*XRES+x]); return(0); } /* Display absent symbols on type 0 to help building the bookfont (experimental). */ else if (*cm_d_ptype != ' ') { int i,j; unsigned char c[256]; pdesc *p; for (i=0; i<=255; ++i) { c[i] = 0; } for (i=0; i<=topp; ++i) { p = pattern + i; if ((p->pt == 0) && (p->tr != NULL) && (strlen(p->tr) == 1)) { ++(c[((unsigned char *)(p->tr))[0]]); } } j = 0; for (i='a'; (jFS) || (mc[k].b-mc[k].t+1>FS)) snprintf(lb,MMB,"symbol %d (too large): \"%s\" (word %d)",k,a,m->sw); else snprintf(lb,MMB,"symbol %d: \"%s\" (word %d)",k,a,m->sw); } show_hint(0,lb); return(1); } show_hint(1,""); /* non-binarized image: apply spyhole */ if (cl_ready == 0) { int x,y; { /* absolute coordinates */ x = X0 + i*RF; y = Y0 + j*RF; /* printf("obs. the seed was changed to (177,180)\n"); x = 177; y = 180; */ spyhole(x,y,0,thresh_val); } } /* binarized image: select symbol using the graphic cursor */ else if ((curr_mc = k) >= 0) { dw[PAGE_SYMBOL].rg = 1; symb2buttons(curr_mc); redraw_dw = 1; } } } /* Application Buttons */ else if ((10 <= x) && (x <= (10+BW)) && (PT <= y) && (y <= PT + BUTT_PER_COL*(BH+VS))) { i = (y-PT) / (BH+VS); if ((y - PT - (i*(BH+VS))) <= BH) { /* (book) The Application Buttons ----------------------- The application buttons are those displayed on the left portion of the Clara X window. They're labelled "zoom", "OCR", etc. Three types of buttons are available. There are on/off buttons (like "italic"), multi-state buttons (like the alphabet button), where the state is informed by the current label, and there are buttons that merely capture mouse clicks, like the "zoom" button. Some are sensible both to mouse button 1 and to mouse button 3, others are sensible only to mouse button 1. */ /* (book) zoom - enlarge or reduce bitmaps. The mouse buttom 1 enlarge bitmaps, the mouse button 3 reduce bitmaps. The bitmaps to enlarge or reduce are determined by the current window. If the PAGE window is active, then the scanned document is enlarged or reduced. If the PAGE (fatbits) or the PATTERN window is active, then the grid is enlarged or reduced. If the PAGE (symbol) or the PATTERN (props) or the PATTERN (list) window is active, then the web clip is enlarged or reduced. */ if (i == bzoom) { if (help) { show_hint(0,"use mouse button 1 to enlarge, 3 to reduce, 2 to max/med/min"); return(1); } if ((dw[PAGE].v) && ((CDW != PAGE_SYMBOL) || (*cm_v_wclip==' '))) { CDW = PAGE; if (RF > 1) { --RF; setview(PAGE); check_dlimits(1); } } else if ((CDW == PAGE_FATBITS) || (dw[TUNE_PATTERN].v)) { if (ZPS < 7) { ZPS += 2; comp_wnd_size(-1,-1); setview(CDW); } } else if ((CDW == PATTERN) || (CDW == PATTERN_LIST) || (CDW == PATTERN_TYPES) || (CDW == PATTERN_ACTION) || (CDW == PAGE_SYMBOL)) { if (plist_rf > 1) { --plist_rf; dw[PATTERN].rg = 1; dw[PATTERN_LIST].rg = 1; dw[PATTERN_TYPES].rg = 1; dw[PATTERN_ACTION].rg = 1; dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } } } /* move to document start */ /* else if (i == sarrow) { if (help) { show_hint(0,"button 1 is HOME, 2 is END"); return(1); } X0 = 0; redraw_dw = 1; } */ /* move to document top */ /* else if (i == tarrow) { if (help) { show_hint(0,"button 1 is TOP, 2 is BOTTOM"); return(1); } Y0 = 0; redraw_dw = 1; } */ /* (book) OCR - start a full OCR run on the current page or on all pages, depending on the state of the "Work on current page only" item of the Options menu. */ else if (i == bocr) { if (help) { show_hint(0,"button 1 starts full OCR, button 3 open OCR steps menu"); return(1); } /* OCR current page */ if (*cm_o_curr != ' ') { start_ocr(-2,-1,0); } /* OCR all pages */ else { start_ocr(-1,-1,0); } } /* (book) stop - stop the current OCR run (if any). OCR does not stop immediately, but will stop as soon as possible. */ else if (i == bstop) { if (help) { show_hint(0,"stop OCR"); return(1); } /* stop */ if (ocring) { stop_ocr = 1; button[bstop] = 1; redraw_button = bstop; } } /* (book) zone - start definition of the OCR zone. Currently zoning in Clara OCR is useful only for saving the zone can as a PBM file, using the "save zone" item on the "File" menu. By now, only one zone can be defined and the OCR operations consider the entire document, ignoring the zone. */ else if (i == bzone) { if (help) { if (button[bzone] == 0) show_hint(0,"button 1 adds zone, 3 deletes all them"); else show_hint(0,""); return(1); } if ((dw[PAGE].v) && (cpage >= 0)) { if (zones >= MAX_ZONES) { show_hint(2,"cannot add another zone"); } else { curr_corner = 0; button[bzone] = 1; show_hint(1,"enter ocr limits: top left, botton right"); redraw_button = -1; } } } /* (book) type - read-only button, set accordingly to the pattern type of the current symbol or pattern. The various letter sizes or styles (normal, footnote, etc) used by the book are numbered from 0 by Clara OCR ("type 0", "type 1", etc). */ else if (i == btype) { /* former behaviour (per-symbol selection) */ /* if (help) { if (*cm_o_ftro != ' ') show_hint(0,"this is the current pattern type"); else show_hint(0,"buttons 1 or 2 change the pattern type"); return(1); } if ((dw[PATTERN].v) && (*cm_o_ftro == ' ')) { if (++button[btype] > toppt) button[btype] = 0; redraw_button = btype; form_changed = 1; form_auto_submit(); } */ if (help) { show_hint(0,"pattern type for current symbol/pattern"); return(1); } } /* (book) bad - toggles the button state. The bad flag is used to identify damaged bitmaps. */ else if (i == bbad) { if (help) { show_hint(0,"click to toggle the bad flag"); return(1); } button[bbad] ^= 1; redraw_button = bbad; form_changed = 1; form_auto_submit(); } /* test - start test */ else if (i == btest) { if (help) { show_hint(0,"start test"); return(1); } if (!ocring) { ocr_other = 5; start_ocr(-2,OCR_OTHER,0); show_hint(0,"running test..."); } } /* (book) latin/greek/etc - read-only button, set accordingly to the alphabet of the current symbol or pattern. */ else if (i == balpha) { /* former behaviour (per-symbol selection) */ /* if (help) { show_hint(0,"circulate alphabets and word types"); return(1); } if (++button[balpha] >= nalpha) button[balpha] = 0; redraw_button = balpha; redraw_map = 1; set_alpha(); */ /* form_changed = 1; form_auto_submit(); */ if (help) { show_hint(0,"alphabet of current symbol/pattern"); return(1); } } } } /* first resize area */ else if ((tab == PAGE) && (dw[PAGE].v) && (dw[PAGE_OUTPUT].v) && (DM <= x) && (x < DM+DW) && (pb=dw[PAGE].dt+dw[PAGE].dh) && (((pb+sb <= y) && (y < dw[PAGE_OUTPUT].dt)) /* || ((pb <= y) && (y < pb+10))*/ )) { if (help) { show_hint(0,"click and drag to resize windows"); return(1); } sliding = 3; /* must redraw to clear the ellipses */ redraw_dw = 1; } /* second resize area */ else if ((tab == PAGE) && (dw[PAGE_OUTPUT].v) && (dw[PAGE_SYMBOL].v) && (DM <= x) && (x < DM+DW) && (ob=dw[PAGE_OUTPUT].dt+dw[PAGE_OUTPUT].dh) && (((ob+sb <= y) && (y < dw[PAGE_SYMBOL].dt)) /* || ((ob <= y) && (y < ob+10)) */ )) { if (help) { show_hint(0,"click and drag to resize windows"); return(1); } sliding = 4; /* must redraw to clear the ellipses */ redraw_dw = 1; } /* vertical scrollbar */ else if ((*cm_v_hide == ' ') && (DM+DW+10 <= x) && (x <= DM+DW+10+RW) && (DT <= y) && (y < DT+DH)) { /* steppers */ if (y <= DT+RW) { if (help) { show_hint(0,"button 1 step up, button 3 go to top"); return(1); } step_up(); } else if (y >= DT+DH-RW) { if (help) { if (DS) show_hint(0,"button 1 step down, button 3 go to bottom"); return(1); } step_down(); } /* cursor */ else { if (help) { if (DS) show_hint(0,"drag or click button 1 or 2 to move"); return(1); } gettimeofday(&tclick,NULL); sliding = 2; s0 = y; } return(0); } /* horizontal scrollbar */ else if ((DM <= x) && (x < DM+DW) && (DT+DH+10 <= y) && (y <= DT+DH+10+RW)) { /* steppers */ if (x <= DM+RW) { if (help) { show_hint(0,"button 1 step left, button 3 go to left limit"); return(1); } step_left(); } else if (x >= DM+DW-RW) { if (help) { show_hint(0,"button 1 step right, button 3 go to right limit"); return(1); } step_right(); } /* cursor */ else { if (help) { if (DS) show_hint(0,"drag or click button 1 or 2 to move"); return(1); } gettimeofday(&tclick,NULL); sliding = 1; s0 = x; } return(0); } /* if (help) show_hint(0,""); */ return(0); } /* Get pointer coordinates. */ void get_pointer(int *x,int *y) { Window root,child; int rx,ry,cx,cy; unsigned mask; /* get pointer coordinates */ rx = ry = -1; XQueryPointer(xd,XW,&root,&child,&rx,&ry,&cx,&cy,&mask); *x = cx; *y = cy; } /* Flashes a message about the graphic element currently under the pointer. This is the service responsible to make appear messages when you move the pointer across the Clara X window. In some cases (as for HTML anchors) the element changes color. */ void show_info(void) { int m,n,px,py; /* get pointer coordinates */ get_pointer(&px,&py); if (cmenu != NULL) { /* reselect automagically */ n = mb_item(px,py); if ((n >= 0) && (n != cmenu-CM)) { dismiss(1); open_menu(px,py,n); set_mclip(0); return; } if (cmenu->c >= 0) { cma(cmenu->c + ((cmenu-CM) << CM_IT_WD),2); } return; } /* The function show_htinfo hilights the HTML element currently under the pointer, but that will mess with the active menu (if any), so we avoid calling show_htinfo when a menu is active. The function show_htinfo also destroys the menu clipping. But the menu clipping is activated when the menu is dismissed, in order to redraw just the menu rectangle, and not the entire application window. So we avoid calling show_htinfo when mclip is active. */ if ((cmenu != NULL) || (mclip)) return; /* The function mactions flashes a message for most elements on the application window (buttons, tabs, symbols on the PAGE window and others). */ m = mactions_b1(1,px,py); /* Flashes a message for the HTML element currently under the pointer. */ if ((DM <= px) && (px < DM+DW) && (DT <= py) && (py < DT+DH)) { if ((HTML) || ((CDW==PAGE) && (*cm_d_bb != ' '))) { XRectangle r; r.x = DM; r.y = DT; r.width = DW; r.height = DH; XSetClipRectangles(xd,xgc,0,0,&r,1,Unsorted); show_htinfo(px,py); r.x = 0; r.y = 0; r.width = WW; r.height = WH; XSetClipRectangles(xd,xgc,0,0,&r,1,Unsorted); if (curr_hti < 0) show_hint(0,""); } } else if (!m) show_hint(0,""); } /* Mouse actions activated by mouse button 3. */ void mactions_b3(int help,int x,int y) { int i; /* change to selected window */ select_dw(x,y); /* Open PAGE or PAGE_FATBITS options menu. */ if ((DM <= x) && (x < DM+DW) && (DT <= y) && (y < DT+DH)) { /* PAGE context menu */ if ((CDW == PAGE) || (CDW == PAGE_FATBITS)) { open_menu(x,y,((CDW == PAGE) ? CM_D : CM_B)); } } /* vertical scrollbar */ else if ((*cm_v_hide == ' ') && (DM+DW+10 <= x) && (x <= DM+DW+10+RW) && (DT <= y) && (y < DT+DH)) { /* steppers */ if (y <= DT+RW) { Y0 = 0; redraw_dw = 1; } else if (y >= DT+DH-RW) { if ((GRY - VR) >= 0) Y0 = GRY - VR; redraw_dw = 1; } /* page down */ else next(); return; } /* horizontal scrollbar */ else if ((*cm_v_hide == ' ') && (DM <= x) && (x < DM+DW) && (DT+DH+10 <= y) && (y <= DT+DH+10+RW)) { /* steppers */ if (x <= DM+RW) { X0 = 0; redraw_dw = 1; } else if (x >= DM+DW-RW) { if ((GRX - HR) >= 0) X0 = GRX - HR; redraw_dw = 1; } /* page right */ else if ((CDW==PAGE_FATBITS) || (CDW==PAGE) || (CDW==PAGE_OUTPUT)) { X0 += HR; if (X0 > (XRES-HR)) X0 = XRES-HR; if (CDW == PAGE_FATBITS) RG = 1; redraw_dw = 1; } return; } /* Application Buttons ------------------- */ else if ((10 <= x) && (x <= (10+BW))) { i = (y-PT) / (BH+VS); if ((y - PT - (i*(BH+VS))) <= BH) { /* end */ /* if (i == earrow) { if ((XRES - HR) >= 0) X0 = XRES - HR; redraw_dw = 1; } */ /* bottom */ /* else if (i == barrow) { if ((YRES - VR) >= 0) Y0 = YRES - VR; redraw_dw = 1; } */ /* zoom - */ if (i == bzoom) { if ((dw[PAGE].v) && ((CDW != PAGE_SYMBOL) || (*cm_v_wclip==' '))) { CDW = PAGE; if (RF < MRF) { ++RF; setview(PAGE); check_dlimits(1); } } else if ((CDW == PAGE_FATBITS) || (CDW == TUNE_PATTERN)) { if (ZPS > 1) { ZPS -= 2; comp_wnd_size(-1,-1); setview(CDW); } } else if ((CDW == PATTERN) || (CDW == PATTERN_LIST) || (CDW == PATTERN_TYPES) || (CDW == PATTERN_ACTION) || (CDW == PAGE_SYMBOL)) { if (plist_rf < 8) { ++plist_rf; dw[PATTERN].rg = 1; dw[PATTERN_LIST].rg = 1; dw[PATTERN_TYPES].rg = 1; dw[PATTERN_ACTION].rg = 1; dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } } } /* open OCR operations menu */ else if (i == bocr) { open_menu(x,y,CM_R); } /* delete OCR zones */ else if (i == bzone) { if (zones > 0) { zones = 0; redraw_dw = 1; for (i=0; i<=tops; ++i) mc[i].c = -1; sess_changed = 1; } } /* change pattern type */ /* else if (i == btype) { if (*cm_o_ftro == ' ') { if (--button[btype] < 0) button[btype] = 99; redraw_button = btype; } } */ /* page largest view Show the scanned page using a 1:1 relation between document pixels and display pixels. */ /* if (i == bzp) { nrf = 1; } */ /* page smallest view Show the scanned page using the largest reduction factor. The largest reduction factor is the minimum one that makes the entire document fit on its window. */ /* else if (i == bzl) { nrf = MRF; } */ /* change reduction factor */ /* if (nrf != dw[PAGE].rf) { setview(PAGE); RF = nrf; check_dlimits(1); force_redraw(); } */ } } } /* Mouse actions activated by mouse button 2. */ void mactions_b2(int help,int x,int y) { int i; /* change to selected window */ select_dw(x,y); /* */ if ((DM <= x) && (x < DM+DW) && (DT <= y) && (y < DT+DH)) { return; } /* vertical scrollbar */ else if ((*cm_v_hide == ' ') && (DM+DW+10 <= x) && (x <= DM+DW+10+RW) && (DT <= y) && (y < DT+DH)) { return; } /* horizontal scrollbar */ else if ((*cm_v_hide == ' ') && (DM <= x) && (x < DM+DW) && (DT+DH+10 <= y) && (y <= DT+DH+10+RW)) { return; } /* Application Buttons ------------------- */ else if ((10 <= x) && (x <= (10+BW))) { i = (y-PT) / (BH+VS); if ((y - PT - (i*(BH+VS))) <= BH) { /* zoom max/min */ if (i == bzoom) { int o; if ((dw[PAGE].v) && ((CDW != PAGE_SYMBOL) || (*cm_v_wclip==' '))) { CDW = PAGE; if ((o=RF) < (MRF/2)) RF = MRF/2; else if (RF < MRF) RF = MRF; else RF = 1; if (RF != o) { setview(PAGE); check_dlimits(1); } } else if ((CDW == PAGE_FATBITS) || (CDW == TUNE_PATTERN)) { /* TODO */ } else if ((CDW == PATTERN) || (CDW == PATTERN_LIST) || (CDW == PATTERN_TYPES) || (CDW == PAGE_SYMBOL)) { if ((o=plist_rf) < 8) plist_rf = 8; else plist_rf = 1; if (plist_rf != o) { ++plist_rf; dw[PATTERN].rg = 1; dw[PATTERN_LIST].rg = 1; dw[PATTERN_TYPES].rg = 1; dw[PAGE_SYMBOL].rg = 1; redraw_dw = 1; } } } } } } /* Get the menu item under the pointer. */ int get_item(int x,int y) { int j; if ((cmenu != NULL) && (CX0 <= x) && (x <= CX0+CW) && (CY0 <= y) && (y <= CY0+CH)) { /* compute action */ j = (y - CY0 - 5) / (DFH + VS); if (j < 0) j = 0; else if (j >= cmenu->n) j = cmenu->n - 1; j += ((cmenu-CM) << CM_IT_WD); return(j); } return(-1); } /* Get and process X events. This function is continuously being called to check the existence of X events to be processed and to process them (if any). Obs. The flag have_s_ev must be cleared only when returning from this service. */ void xevents(void) { int no_ev; static int expose_queued=0; static struct timeval rt = {0,0}; struct timeval t; unsigned d; /* No new event. Events are got from the Xserver or directly from the buffer xev. Some internal procedures feed xev conveniently and set the flag have_s_ev. */ if ((!have_s_ev) && (XCheckWindowEvent(xd,XW,evmasks,&xev) == 0)) { /* Compute age of the expose event queued. */ if (expose_queued) { gettimeofday(&t,NULL); d = (t.tv_sec - rt.tv_sec) * 1000000; if (d == 0) d += (t.tv_usec - rt.tv_usec); else d += -rt.tv_usec + t.tv_usec; } else { /* whenever absent, age is.. zero */ d = 0; /* When OCRing effectively, redraw components as requested, and return immediately. */ if (ocring) { redraw(); if (!waiting_key) return; } } /* If there is not a 0.05 seconds old expose event queued, then redraw components as requested (unless there is an expose event queued), wait 0.1 seconds to not burn CPU, and return. */ if (d < 50000) { /* redraw components */ if (!expose_queued) redraw(); /* wait 0.1 seconds */ if ((!ocring) || (waiting_key)) { t.tv_sec = 0; t.tv_usec = 100000; select(0,NULL,NULL,NULL,&t); } /* dismiss menus that became unfocused */ if (cmenu != NULL) dismiss(0); /* show local info */ if (((!ocring) || waiting_key) && (focused) && (!sliding)) show_info(); return; } else { /* attend the old expose event */ if (expose_queued) { /* db("attending old expose event"); */ force_redraw(); redraw(); expose_queued = 0; } } /* tell the code below to not try to process events */ no_ev = 1; } /* Remember that we got a new event from the queue. */ else { no_ev = 0; /* db("got event %d",xev.type); */ } /* just got or lost focus */ if (xev.type == FocusIn) { force_redraw(); focused = 1; have_s_ev = 0; return; } else if (xev.type == FocusOut) { focused = 0; have_s_ev = 0; return; } /* a button press must clear the message line */ if (xev.type == ButtonPress) { show_hint(3,NULL); } /* Remember time of the expose event (it's the event being processed now). This event will be processed as soon as it becomes sufficiently old, in order to avoid expensive redrawing of the entire window. */ if ((!no_ev) && (xev.type == Expose)) { gettimeofday(&rt,NULL); expose_queued = 1; have_s_ev = 0; return; } /* no new event */ if (no_ev) { have_s_ev = 0; return; } /* Redraw as requested. In some cases, (and only then), we should clear expose_queue too, but it's hard to distinguish such cases, so we prefer to maintain the expose_queued status and redraw the window entirely again after some delay. */ else { redraw(); } /* There is an active menu. Mouse button 1 will select an item, ESC will dismiss the menu, and arrows will browse the menus. */ if (cmenu != NULL) { bev = (XButtonEvent *) &xev; /* selected item */ if ((xev.type == ButtonPress) && (bev->button == Button1)) { mactions_b1(0,xev.xbutton.x,xev.xbutton.y); } /* key */ else if (xev.type == KeyPress) { kactions(); } /* just hilight the current item */ else if (xev.type == MotionNotify) { int j; j = get_item(xev.xbutton.x,xev.xbutton.y); if (j >= 0) j &= CM_IT_M; if (j != cmenu->c) { cmenu->c = j; redraw_menu = 1; } } } /* Application window resize */ else if (xev.type == ConfigureNotify) { Window root; int x,y; unsigned w,h,b,d; XGetGeometry(xd,XW,&root,&x,&y,&w,&h,&b,&d); if ((w != WW) || (h != WH)) { /* db("resizing from %d %d to %d %d",WW,WH,w,h); */ comp_wnd_size(w,h); set_mclip(0); X0 = Y0 = 0; setview(CDW); } } /* motion notify */ else if (xev.type == MotionNotify) { slide(); } /* Mouse button release finishes sliding modes */ else if (xev.type == ButtonRelease) { unsigned d; if (sliding) { /* sliding delay */ gettimeofday(&t,NULL); d = (t.tv_sec - tclick.tv_sec) * 1000000; if (d == 0) d += (t.tv_usec - tclick.tv_usec); else d += -tclick.tv_usec + t.tv_usec; /* a quick press-release on mode 1 means "page left" */ if ((sliding == 1) && (d < 200000)) { if ((CDW==PAGE_FATBITS) || (CDW==PAGE) || (CDW==PAGE_OUTPUT)) { X0 -= HR; if (X0 < 0) X0 = 0; if (CDW == PAGE_FATBITS) RG = 1; redraw_dw = 1; } } /* a quick press-release on mode 2 means "page up" */ if ((sliding == 2) && (d < 200000)) prior(); if (curr_mc >= 0) redraw_dw = 1; } sliding = 0; } /* Process other events */ else { switch (xev.type) { case ButtonPress: bev = (XButtonEvent *) &xev; if (bev->button == Button1) { mactions_b1(0,xev.xbutton.x,xev.xbutton.y); } else if (bev->button == Button2) { mactions_b2(0,xev.xbutton.x,xev.xbutton.y); } else if (bev->button == Button3) { mactions_b3(0,xev.xbutton.x,xev.xbutton.y); } break; case KeyPress: kactions(); break; default: db("unattended X event %d (ok)",xev.type); break; } } have_s_ev = 0; } /* Synchronize the variables alpha, alpha_sz and alpha_cols with the current status of the alphabet selection button. */ void set_alpha(void) { int i; /* get current alphabet and its size */ i = inv_balpha[(int)(button[balpha])]; if (i == LATIN) { alpha = latin; alpha_sz = latin_sz; alpha_cols = 2; } else if (i == GREEK) { alpha = (alpharec *) greek; alpha_sz = greek_sz; alpha_cols = 3; } else if (i == CYRILLIC) { alpha = cyrillic; alpha_sz = cyrillic_sz; alpha_cols = 2; } else if (i == HEBREW) { alpha = hebrew; alpha_sz = hebrew_sz; alpha_cols = 1; } else if (i == ARABIC) { alpha = arabic; alpha_sz = arabic_sz; alpha_cols = 1; } else if (i == NUMBER) { alpha = number; alpha_sz = number_sz; alpha_cols = 1; } else alpha = NULL; } /* Set the application window name. */ void swn(char *n) { char *list[2]; XTextProperty xtp; list[0] = n; list[1] = NULL; XStringListToTextProperty(list,1,&xtp); XSetWMName(xd,XW,&xtp); } /* Set the default font. Clara uses one same font for all (menus, button labels, HTML rendering, etc. The font to be set is obtained from the Options menu status. */ void set_xfont(void) { XCharStruct *x; /* free current font */ if (dfont != NULL) XFreeFont(xd,dfont); /* use small font */ if (*cm_v_small == 'X') { if (((dfont = XLoadQueryFont(xd,"6x13")) == NULL) && verbose) warn("oops.. could not read font 6x13\n"); } /* use medium font */ else if (*cm_v_medium == 'X') { if (((dfont = XLoadQueryFont(xd,"9x15")) == NULL) && verbose) warn("oops.. could not read font 9x15\n"); } /* use large font */ else if (*cm_v_large == 'X') { if (((dfont = XLoadQueryFont(xd,"10x20")) == NULL) && verbose) warn("oops.. could not read font 10x20\n"); } /* use the font informed as command-line parameter */ else { if (((dfont = XLoadQueryFont(xd,xfont)) == NULL) && verbose) warn("oops.. could not read font %s\n",xfont); } /* try "fixed" */ if ((dfont == NULL) && (strcmp(xfont,"fixed") != 0)) { if (((dfont = XLoadQueryFont(xd,"fixed")) == NULL) && verbose) warn("oops.. could not read font fixed\n"); } /* no font loaded: abandon */ if (dfont == NULL) { fatal(XE,"could not load a font, giving up"); } /* text font height and width */ x = &(dfont->max_bounds); DFH = (DFA=x->ascent) + (DFD=x->descent); DFW = x->width; } /* Reset the parameters of all windows. */ void init_dws(int m) { int i; for (i=0; i<=TOP_DW; ++i) { dw[i].x0 = 0; dw[i].y0 = 0; dw[i].hr = 0; dw[i].vr = 0; dw[i].grx = 0; dw[i].gry = 0; dw[i].v = 0; dw[i].ds = 1; dw[i].gs = 1; dw[i].hm = 0; if (m) { dw[i].p_ge = NULL; dw[i].p_gesz = 0; } dw[i].p_topge = -1; dw[i].p_curr_hti = -1; dw[i].ulink = 1; dw[i].hs = 1; dw[i].rg = 1; } dw[WELCOME].ds = 0; /* In HTML windows that mix tables with other free elements we define a nonzero left margin for aesthetic. */ dw[TUNE].hm = DFW; dw[PATTERN_TYPES].hm = DFW; } /* Context menu initializer. */ void cmi(void) { cmdesc *c; /* (book) File menu --------- This menu is activated from the menu bar on the top of the application X window. */ c = addmenu(1,"File",0); /* (book) Load page Enter the page list to select a page to be loaded. */ CM_F_LOAD = additem(c,"Load page",CM_TA,0,-1, "",0); /* (book) Save session Save on disk the page session (file page.session), the patterns (file "pattern") and the revision acts (file "acts"). */ CM_F_SAVE = additem(c,"Save session",CM_TA,0,-1, "",CMA_PAGE); /* (book) Save first zone Save on disk the first zone as the file zone.pbm. */ CM_F_SZ = additem(c,"Save first zone",CM_TA,0,-1, "",CMA_PAGE|CMA_ZONE); /* (book) Write report Save the contents of the PAGE LIST window to the file report.txt on the working directory. */ CM_F_REP = additem(c,"Write report",CM_TA,0,-1, "",0); CM_F = c - CM; /* (book) Quit the program Just quit the program (asking before if the session is to be saved. */ CM_F_QUIT = additem(c,"Quit",CM_TA,0,-1, "",0); CM_F = c - CM; /* (book) Edit menu --------- This menu is activated from the menu bar on the top of the application X window. */ c = addmenu(1,"Edit",0); /* (book) Only doubts When selected, the right or the left arrows used on the PATTERN or the PATTERN PROPS windows will move to the next or the previous untransliterated patterns. */ CM_E_DOUBTS = additem(c,"[ ] Only doubts",CM_TB,0,-1, "",0); /* (book) Re-scan all patterns When selected, the classification heuristic will retry all patterns for each symbol. This is required when trying to resolve the unclassified symbols using a second classification method. */ CM_E_RESCAN = additem(c,"[ ] Re-scan all patterns",CM_TB,0,-1, "",0); /* (book) Auto-classify. When selected, the engine will re-run the classifier after each new pattern trained by the user. So if various letters "a" remain unclassified, training one of them will perhaps recognize some othersm helping to complete the recognition. */ CM_E_AC = additem(c,"[ ] Auto-classify",CM_TB,0,-1, "",0); /* (book) Fill region When selected, the mouse button 1 will fill the region around one pixel on the pattern bitmap under edition on the font tab. */ CM_E_FILL = additem(c,"[ ] Fill region",CM_TR,1,-1, "",0); /* (book) Paint pixel When selected, the mouse button 1 will paint individual pixels on the pattern bitmap under edition on the font tab. */ CM_E_PP = additem(c,"[ ] Paint pixel",CM_TR,0,-1, "",0); /* (book) Clear region When selected, the mouse button 1 will clear the region around one pixel on the pattern bitmap under edition on the font tab. */ CM_E_FILL_C = additem(c,"[ ] Clear region",CM_TR,0,-1, "",0); /* (book) Clear pixel When selected, the mouse button 1 will clear individual pixels on the pattern bitmap under edition on the font tab. */ CM_E_PP_C = additem(c,"[ ] Clear pixel",CM_TR,0,-1, "",0); /* (book) Sort patterns by page When selected, the pattern list window will divide the patterns in blocks accordingly to their (page) sources. */ CM_E_SP = additem(c,"[ ] Sort patterns by page",CM_TB,1,-1, "",0); /* (book) Sort patterns by matches When selected, the pattern list window will use as the first criterion when sorting the patterns, the number of matches of each pattern. */ CM_E_SM = additem(c,"[ ] Sort patterns by matches",CM_TB,0,-1, "",0); /* (book) Sort patterns by transliteration When selected, the pattern list window will use as the second criterion when sorting the patterns, their transliterations. */ CM_E_ST = additem(c,"[ ] Sort patterns by transliteration",CM_TB,0,-1, "",0); /* (book) Sort patterns by number of pixels When selected, the pattern list window will use as the third criterion when sorting the patterns, their number of pixels. */ CM_E_SN = additem(c,"[ ] Sort patterns by number of pixels",CM_TB,0,-1, "",0); /* (book) Sort patterns by width When selected, the pattern list window will use as the fourth criterion when sorting the patterns, their widths. */ CM_E_SW = additem(c,"[ ] Sort patterns by width",CM_TB,0,-1, "",0); /* (book) Sort patterns by height When selected, the pattern list window will use as the fifth criterion when sorting the patterns, their heights. */ CM_E_SH = additem(c,"[ ] Sort patterns by height",CM_TB,0,-1, "",0); /* (book) Del Untransliterated patterns Remove from the font all untransliterated fonts. */ CM_E_DU = additem(c,"Del Untransliterated patterns",CM_TA,1,-1, "",0); /* (book) Set pattern type. Set the pattern type for all patterns marked as "other". */ CM_E_SETPT = additem(c,"Set pattern type",CM_TA,0,-1, "",0); /* (book) Search barcode. Try to find a barcode on the loaded page. */ CM_E_SEARCHB = additem(c,"Search barcode",CM_TA,0,-1, "",0); /* (book) Instant thresholding. Perform on-the-fly global thresholding. */ CM_E_THRESH = additem(c,"Instant thresholding",CM_TA,0,-1, "",0); #ifdef PATT_SKEL /* (book) Reset skeleton parameters Reset the parameters for skeleton computation for all patterns. */ CM_E_RSKEL = additem(c,"Reset skeleton parameters",CM_TA,0,-1, "",0); #endif CM_E = c - CM; /* (book) View menu --------- This menu is activated from the menu bar on the top of the application X window. */ c = addmenu(1,"View",0); /* (book) Small font Use a small X font (6x13). */ CM_V_SMALL = additem(c,"[ ] Small font (6x13)",CM_TR,0,1, "",0); /* (book) Medium font Use the medium font (9x15). */ CM_V_MEDIUM = additem(c,"[ ] Medium font (9x15)",CM_TR,0,1, "",0); /* (book) Small font Use a large X font (10x20). */ CM_V_LARGE = additem(c,"[ ] Large font (10x20)",CM_TR,0,1, "",0); /* (book) Default font Use the default font (7x13 or "fixed" or the one informed on the command line). */ CM_V_DEF = additem(c,"[ ] Default font",CM_TR,0,1, "",0); /* (book) Hide scrollbars Toggle the hide scrollbars flag. When active, this flag hides the display of scrolllbar on all windows. */ CM_V_HIDE = additem(c,"[ ] Hide scrollbars",CM_TB,0,1, "",0); /* (book) Omit fragments Toggle the hide fragments flag. When active, fragments won't be included on the list of patterns. */ CM_V_OF = additem(c,"[ ] Omit fragments",CM_TB,0,-1, "",0); /* (book) Show HTML source Show the HTML source of the document, instead of the graphic rendering. */ CM_V_VHS = additem(c,"[ ] Show HTML source",CM_TB,1,1, "",0); /* (book) Show web clip Toggle the web clip feature. When enabled, the PAGE_SYMBOL window will include the clip of the document around the current symbol that will be used through web revision. */ CM_V_WCLIP = additem(c,"[ ] Show web clip",CM_TB,0,1, "",0); /* (book) Show alphabet map Toggle the alphabet map display. When enabled, a mapping from Latin letters to the current alphabet will be displayed. */ CM_V_MAP = additem(c,"[ ] Show alphabet map",CM_TB,0,1, "",0); /* (book) Show current class Identify the symbols on the current class using a gray ellipse. */ CM_V_CC = additem(c,"[ ] Show current class",CM_TB,0,-1, "",0); /* (book) Show matches Display bitmap matches when performing OCR. */ CM_V_MAT = additem(c,"[ ] Show matches",CM_TR,1,-1, "",0); /* (book) Show comparisons Display all bitmap comparisons when performing OCR. */ CM_V_CMP = additem(c,"[ ] Show comparisons",CM_TR,0,-1, "",0); /* (book) Show matches Display bitmap matches when performing OCR, waiting a key after each display. */ CM_V_MAT_K = additem(c,"[ ] Show matches and wait",CM_TR,0,-1, "",0); /* (book) Show comparisons and wait Display all bitmap comparisons when performing OCR, waiting a key after each display. */ CM_V_CMP_K = additem(c,"[ ] Show comparisons and wait",CM_TR,0,-1, "",0); /* (book) Show skeleton tuning Display each candidate when tuning the skeletons of the patterns. */ CM_V_ST = additem(c,"[ ] Show skeleton tuning",CM_TB,0,-1, "",0); /* (book) Presentation Perform a presentation. This item is visible on the menu only when the program is started with the -A option. */ if (allow_pres) CM_V_PRES = additem(c,"Presentation",CM_TA,1,1, "",0); CM_V = c - CM; /* (book) OCR steps menu -------------- This menu is activated when the mouse button 3 is pressed on the OCR button. It allows running specific OCR steps (all steps run in sequence when the OCR button is pressed). */ c = addmenu(0,"OCR operations",0); /* (book) Preproc. Start preproc. */ CM_R_PREPROC = additem(c,"Preprocessing",CM_TA,0,-1, "",CMA_PAGE); /* (book) Detect blocks Start detecting text blocks. */ CM_R_BLOCK = additem(c,"Detect blocks",CM_TA,0,-1, "",CMA_PAGE); /* (book) Segmentation. Start binarization and segmentation. */ CM_R_SEG = additem(c,"Segmentation",CM_TA,0,-1, "",CMA_PAGE|CMA_NCL); /* (book) Consist structures All OCR data structures are submitted to consistency tests. This is under implementation. */ CM_R_CONS = additem(c,"Consist structures",CM_TA,0,-1, "",0); /* (book) Prepare patterns Compute the skeletons and analyse the patterns for the achievement of best results by the classifier. Not fully implemented yet. */ CM_R_OPT = additem(c,"Prepare patterns",CM_TA,0,-1, "",0); /* (book) Read revision data Revision data from the web interface is read, and added to the current OCR training knowledge. */ CM_R_REV = additem(c,"Read revision data",CM_TA,0,-1, "",CMA_PAGE|CMA_CL); /* (book) Classification start classifying the symbols of the current page or of all pages, depending on the state of the "Work on current page only" item of the Options menu. It will also build the font automatically if the corresponding item is selected on the Options menu. */ CM_R_CLASS = additem(c,"Classification",CM_TA,0,-1, "",CMA_PAGE|CMA_CL); /* (book) Geometric merging Merge closures on symbols depending on their geometry. */ CM_R_GEOMERGE = additem(c,"Geometric merging",CM_TA,0,-1, "",CMA_PAGE|CMA_CL); /* (book) Build words and lines Start building the words and lines. These heuristics will be applied on the current page or on all pages, depending on the state of the "Work on current page only" item of the Options menu. */ CM_R_BUILD = additem(c,"Build words and lines",CM_TA,0,-1, "",CMA_PAGE|CMA_CL); /* (book) Generate spelling hints Obs. this is not implemented yet. Start filtering through ispell to generate transliterations for unknow symbols or alternative transliterations for known symbols. Clara will use the dictionaries available for the languages selected on the Languages menu. Filtering will be performed on the current page or on all pages, depending on the state of the "Work on current page only" item of the Options menu. */ CM_R_SPELL = additem(c,"Generate spelling hints",CM_TA,0,-1, "",CMA_PAGE|CMA_CL); /* (book) Generate output The OCR output is generated to be displayed on the "PAGE (output)" window. The output is also saved to the file page.html. */ CM_R_OUTP = additem(c,"Generate output",CM_TA,0,-1, "",CMA_PAGE|CMA_CL); /* (book) Generate web doubts Files containing symbols to be revised through the web interface are created on the "doubts" subdirectory of the work directory. This step is performed only when Clara OCR is started with the -W command-line switch. */ CM_R_DOUBTS = additem(c,"Generate web doubts",CM_TA,0,-1, "",CMA_PAGE|CMA_CL); CM_R = c - CM; /* (book) PAGE options menu ----------------- This menu is activated when the mouse button 3 is pressed on the PAGE window. */ c = addmenu(0,"PAGE",0); /* (book) See in fatbits Change to PAGE_FATBITS focusing this symbol. */ CM_D_FOCUS = additem(c,"See in fatbits",CM_TA,0,-1, "",CMA_CL); /* (book) Bottom left here Scroll the window contents in order to the current pointer position become the bottom left. */ CM_D_HERE = additem(c,"Bottom left here",CM_TA,0,-1, "",0); /* (book) Use as pattern The pattern of the class of this symbol will be the unique pattern used on all subsequent symbol classifications. This feature is intended to be used with the "OCR this symbol" feature, so it becomes possible to choose two symbols to be compared, in order to test the classification routines. */ CM_D_TS = additem(c,"Use as pattern",CM_TA,0,-1, "",CMA_CL); /* (book) OCR this symbol Starts classifying only the symbol under the pointer. The classification will re-scan all patterns even if the "re-scan all patterns" option is unselected. */ CM_D_OS = additem(c,"OCR this symbol",CM_TA,0,-1, "",CMA_CL); /* (book) Merge with current symbol Merge this fragment with the current symbol. */ CM_D_ADD = additem(c,"Merge with current symbol",CM_TA,0,-1, "",CMA_CL); /* (book) Link as next symbol Create a symbol link from the current symbol (the one identified by the graphic cursor) to this symbol. */ CM_D_SLINK = additem(c,"Link as next symbol",CM_TA,0,-1, "",CMA_CL); /* (book) Disassemble symbol Make the current symbol nonpreferred and each of its components preferred. */ CM_D_DIS = additem(c,"Disassemble symbol",CM_TA,0,-1, "",CMA_CL); /* (book) Link as accent Create an accent link from the current symbol (the one identified by the graphic cursor) to this symbol. */ CM_D_ALINK = additem(c,"Link as accent",CM_TA,0,-1, "",CMA_CL); /* (book) Diagnose symbol pairing Run locally the symbol pairing heuristic to try to join this symbol to the word containing the current symbol. This is useful to know why the OCR is not joining two symbols on one same word. */ CM_D_SDIAG = additem(c,"Diagnose symbol pairing",CM_TA,0,-1, "",CMA_CL); /* (book) Diagnose word pairing Run locally the word pairing heuristic to try to join this word with the word containing the current symbol on one same line. This is useful to know why the OCR is not joining two words on one same line. */ CM_D_WDIAG = additem(c,"Diagnose word pairing",CM_TA,0,-1, "",CMA_CL); /* (book) Diagnose merging Run locally the geometrical merging heuristic to try to merge this piece to the current symbol. */ CM_D_GM = additem(c,"Diagnose merging",CM_TA,0,-1, "",CMA_CL); /* (book) Show pixel coords Present the coordinates and color of the current pixel. */ CM_D_PIXELS = additem(c,"[ ] Show pixel coords",CM_TR,1,1, "",0); /* (book) Show closures Identify the individual closures when displaying the current document. */ CM_D_CLOSURES = additem(c,"[ ] Show closures",CM_TR,0,1, "",0); /* (book) Show symbols Identify the individual symbols when displaying the current document. */ CM_D_SYMBOLS = additem(c,"[ ] Show symbols",CM_TR,0,1, "",0); /* (book) Show words Identify the individual words when displaying the current document. */ CM_D_WORDS = additem(c,"[ ] Show words",CM_TR,0,1, "",0); /* (book) Show pattern type Display absent symbols on pattern type 0, to help building the bookfont. */ CM_D_PTYPE = additem(c,"[ ] Show pattern type",CM_TR,0,1, "",0); /* (book) Report scale Report the scale on the tab when the PAGE window is active. */ CM_D_RS = additem(c,"[ ] Report scale",CM_TB,1,1, "",0); /* (book) Display box instead of symbol On the PAGE window displays the bounding boxes instead of the symbols themselves. This is useful when designing new heuristics. */ CM_D_BB = additem(c,"[ ] Display box instead of symbol",CM_TB,0,1, "",0); CM_D = c - CM; /* (book) PAGE_FATBITS options menu ------------------------- This menu is activated when the mouse button 3 is pressed on the PAGE. */ c = addmenu(0,"PAGE_FATBITS",0); /* (book) Bottom left here Scroll the window contents in order to the current pointer position become the bottom left. */ CM_B_HERE = additem(c,"Bottom left here",CM_TA,0,-1, "",0); /* (book) Centralize Scroll the window contents in order to the centralize the closure under the pointer. */ CM_B_CENTRE = additem(c,"Centralize",CM_TA,0,-1, "",0); /* (book) Build border path Build the closure border path and activate the flea. */ CM_B_BP = additem(c,"Build border path",CM_TA,0,-1, "",0); /* (book) Search straight lines (linear) Build the closure border path and search straight lines there using linear distances. */ CM_B_SLD = additem(c,"Search straight lines (linear)",CM_TA,0,-1, "",0); /* (book) Search straight lines (quadratic) Build the closure border path and search straight lines there using correlation. */ CM_B_SLC = additem(c,"Search straight lines (quadratic)",CM_TA,0,-1, "",0); /* (book) Is bar? Apply the isbar test on the closure. */ CM_B_ISBAR = additem(c,"Is bar?",CM_TA,0,-1, "",0); /* (book) Detect extremities? Detect closure extremities. */ CM_B_DX = additem(c,"Detect extremities",CM_TA,0,-1, "",0); /* (book) Show skeletons Show the skeleton on the windows PAGE_FATBITS. The skeletons are computed on the fly. */ CM_B_SKEL = additem(c,"[ ] Show skeletons",CM_TR,1,-1, "",0); /* (book) Show border Show the border on the window PAGE_FATBITS. The border is computed on the fly. */ CM_B_BORDER = additem(c,"[ ] Show border",CM_TR,0,-1, "",0); /* (book) Show pattern skeleton For each symbol, will show the skeleton of its best match on the PAGE (fatbits) window. */ CM_B_HS = additem(c,"[ ] Show pattern skeleton",CM_TR,0,-1, "",0); /* (book) Show pattern border For each symbol, will show the border of its best match on the PAGE (fatbits) window. */ CM_B_HB = additem(c,"[ ] Show pattern border",CM_TR,0,-1, "",0); CM_B = c - CM; /* (book) Alphabets menu -------------- This item selects the alphabets that will be available on the alphabets button. */ c = addmenu(1,"Alphabets",0); /* (book) Arabic This is a provision for future support of Arabic alphabet. */ CM_A_ARABIC = additem(c,"[ ] Arabic",CM_TB,0,-1, "",CMA_ABSENT); /* (book) Cyrillic This is a provision for future support of Cyrillic alphabet. */ CM_A_CYRILLIC = additem(c,"[ ] Cyrillic",CM_TB,0,-1, "",CMA_ABSENT); /* (book) Greek This is a provision for future support of Greek alphabet. */ CM_A_GREEK = additem(c,"[ ] Greek",CM_TB,0,-1, "",CMA_ABSENT); /* (book) Hebrew This is a provision for future support of Hebrew alphabet. */ CM_A_HEBREW = additem(c,"[ ] Hebrew",CM_TB,0,-1, "",CMA_ABSENT); /* (book) Kana This is a provision for future support of Kana alphabet. */ CM_A_KANA = additem(c,"[ ] Kana",CM_TB,0,-1, "",CMA_ABSENT); /* (book) Latin Words that use the Latin alphabet include those from the languages of most Western European countries (English, German, French, Spanish, Portuguese and others). */ CM_A_LATIN = additem(c,"[ ] Latin",CM_TB,0,-1, "",0); /* (book) Number Indo-arabic decimal numbers like 1234, +55-11-12345678 or 2000. */ CM_A_NUMBER = additem(c,"[ ] Numeric",CM_TB,1,-1, "",0); /* (book) Ideogram Ideograms. */ CM_A_IDEOGRAM = additem(c,"[ ] Ideograms",CM_TB,0,-1, "",CMA_ABSENT); CM_A = c - CM; #ifdef LANG_MENU /* (book) Languages menu -------------- This menu selects the languages used on the book. It will define the dictionaries that will be applied when trying to generate the transliteration of unknown symbols or generating alternative transliterations or marking a word for future revision. */ c = addmenu(1,"Languages",0); /* (book) English (USA) Toggle American English spell checking for each word found on the page. */ CM_L_EN = additem(c,"[ ] English (USA)",CM_TB,0,-1, "",0); /* (book) English (UK) Toggle British English spell checking for each word found on the page. */ CM_L_UK = additem(c,"[ ] English (UK)",CM_TB,0,-1, "",0); /* (book) French Toggle French spell checking for each word found on the page. */ CM_L_FR = additem(c,"[ ] French",CM_TB,0,-1, "",0); /* (book) German Toggle german spell checking for each word found on the page. */ CM_L_DE = additem(c,"[ ] German",CM_TB,0,-1, "",0); /* (book) Greek Toggle greek spell checking for each word found on the page. */ CM_L_GR = additem(c,"[ ] Greek",CM_TB,0,-1, "",0); /* (book) Portuguese (Brazil) Toggle brazilian protuguese spell checking for each word found on the page. */ CM_L_BR = additem(c,"[ ] Portuguese (Brazil)",CM_TB,0,-1, "",0); /* (book) Portuguese (Portugal) Toggle Portuguese spell checking for each word found on the page. */ CM_L_PT = additem(c,"[ ] Portuguese (Portugal)",CM_TB,0,-1, "",0); /* (book) Russian Toggle Russian spell checking for each word found on the page. */ CM_L_RU = additem(c,"[ ] Russian",CM_TB,0,-1, "",0); CM_L = c - CM; #endif /* (book) Options menu ------------ */ c = addmenu(1,"Options",0); /* (book) Work on current page only OCR operations (classification, merge, etc) will be performed only on the current page. */ CM_O_CURR = additem(c,"[ ] Work on current page only",CM_TR,0,-1, "",0); /* (book) Work on all pages OCR operations (classification, merge, etc) will be performed on all pages. */ CM_O_ALL = additem(c,"[ ] Work on all pages",CM_TR,0,-1, "",0); /* (book) Emulate deadkeys Toggle the emulate deadkeys flag. Deadkeys are useful for generating accented characters. Deadkeys emulation are disabled by default The emulation of deadkeys may be set on startup through the -i command-line switch. */ CM_O_DKEYS = additem(c,"[ ] Emulate deadkeys",CM_TB,1,-1, "",0); /* (book) Menu auto popup Toggle the automenu feature. When enabled, the menus on the menu bar will pop automatically when the pointer reaches the menu bar. */ CM_O_AMENU = additem(c,"[ ] Menu auto popup",CM_TB,0,-1, "",0); /* (book) PAGE only When selected, the PAGE tab will display only the PAGE window. The windows PAGE_OUTPUT and PAGE_SYMBOL will be hidden. */ CM_O_PGO = additem(c,"[ ] PAGE only",CM_TB,0,1, "",0); CM_O = c - CM; /* (book) DEBUG menu ---------- */ c = addmenu(1,"Debug",0); /* (book) Show unaligned symbols The symbols with unknown alignment are displayed using gray ellipses. This option overrides the "show current class" feature. */ CM_G_ALIGN = additem(c,"[ ] Show unaligned symbols",CM_TB,0,-1, "",0); /* (book) Show localbin progress Display the progress of the extraction of symbols performed by the local binarizer. */ CM_G_LB = additem(c,"[ ] Show localbin progress",CM_TB,0,-1, "",0); /* (book) Show lines (geometrical) Identify the lines (computed using geometrical criteria) when displaying the current document. */ CM_G_GLINES = additem(c,"[ ] Show lines (geometrical)",CM_TR,0,-1, "",0); /* (book) Bold Only Restrict the show symbols or show words feature (PAGE menu) to bold symbols or words. */ CM_G_BO = additem(c,"[ ] Bold Only",CM_TR,1,-1, "",0); /* (book) Italic Only Restrict the show symbols or show words feature (PAGE menu) to bold symbols or words. */ CM_G_IO = additem(c,"[ ] Italic Only",CM_TR,0,-1, "",0); /* (book) Reset match counters Change to zero the cumulative matches field of all patterns. */ CM_G_CM = additem(c,"Reset match counters",CM_TA,1,-1, "",0); /* (book) Search unexpected mismatches Compare all patters with same type and transliteration. Must be used with the "Show comparisons and wait" option on to diagnose symbol comparison problems. */ CM_G_SUM = additem(c,"[ ] Search unexpected mismatches",CM_TB,1,-1, "",0); CM_G = c - CM; /* Assign menu flags to the menu labels. */ /* edit menu */ cm_e_od = F(CM_E_DOUBTS); cm_e_rescan = F(CM_E_RESCAN); cm_e_pp = F(CM_E_PP); cm_e_pp_c = F(CM_E_PP_C); cm_e_fill = F(CM_E_FILL); cm_e_fill_c = F(CM_E_FILL_C); cm_e_sm = F(CM_E_SM); cm_e_sp = F(CM_E_SP); cm_e_st = F(CM_E_ST); cm_e_sw = F(CM_E_SW); cm_e_sh = F(CM_E_SH); cm_e_sn = F(CM_E_SN); cm_e_ac = F(CM_E_AC); /* view menu */ cm_v_small = F(CM_V_SMALL); cm_v_medium = F(CM_V_MEDIUM); cm_v_large = F(CM_V_LARGE); cm_v_def = F(CM_V_DEF); cm_v_hide = F(CM_V_HIDE); cm_v_of = F(CM_V_OF); cm_v_wclip = F(CM_V_WCLIP); cm_v_map = F(CM_V_MAP); cm_v_vhs = F(CM_V_VHS); cm_v_cc = F(CM_V_CC); cm_v_mat = F(CM_V_MAT); cm_v_mat_k = F(CM_V_MAT_K); cm_v_cmp_k = F(CM_V_CMP_K); cm_v_cmp = F(CM_V_CMP); cm_v_st = F(CM_V_ST); /* options menu */ cm_o_dkeys = F(CM_O_DKEYS); cm_o_amenu = F(CM_O_AMENU); cm_o_curr = F(CM_O_CURR); cm_o_all = F(CM_O_ALL); cm_o_pgo = F(CM_O_PGO); /* alphabets menu */ cm_a_arabic = F(CM_A_ARABIC); cm_a_cyrillic = F(CM_A_CYRILLIC); cm_a_greek = F(CM_A_GREEK); cm_a_hebrew = F(CM_A_HEBREW); cm_a_kana = F(CM_A_KANA); cm_a_latin = F(CM_A_LATIN); cm_a_ideogram = F(CM_A_IDEOGRAM); cm_a_number = F(CM_A_NUMBER); /* debug menu */ cm_g_align = F(CM_G_ALIGN); cm_g_lb = F(CM_G_LB); cm_g_glines = F(CM_G_GLINES); cm_g_bo = F(CM_G_BO); cm_g_io = F(CM_G_IO); cm_g_sum = F(CM_G_SUM); /* PAGE menu */ cm_d_pixels = F(CM_D_PIXELS); cm_d_closures = F(CM_D_CLOSURES); cm_d_symbols = F(CM_D_SYMBOLS); cm_d_words = F(CM_D_WORDS); cm_d_ptype = F(CM_D_PTYPE); cm_d_rs = F(CM_D_RS); cm_d_bb = F(CM_D_BB); /* PAGE (fatbits) menu */ cm_b_skel = F(CM_B_SKEL); cm_b_border = F(CM_B_BORDER); cm_b_hs = F(CM_B_HS); cm_b_hb = F(CM_B_HB); /* initial values */ if (RW < 0) { *cm_v_hide = 'X'; RW = - RW; } if (dkeys) { *cm_o_dkeys = 'X'; RW = - RW; } /* these are on by default */ *cm_v_of = 'X'; *cm_v_cc = 'X'; *cm_o_curr = 'X'; *cm_a_number = 'X'; *cm_a_latin = 'X'; } /* GUI initialization. */ void xpreamble() { int xscreen; Colormap cmap; /* X preamble */ if ((batch_mode == 0) && (wnd_ready == 0)) { /* connect to the X server */ if (displayname == NULL) xd = XOpenDisplay(""); else xd = XOpenDisplay(displayname); if (xd == NULL) { if (displayname != NULL) fatal(XE,"cannot connect display %s",displayname); else fatal(XE,"cannot connect display"); } } /* context menus initialization */ cmi(); /* set alphabets button label */ set_bl_alpha(); /* alloc X font */ if (batch_mode == 0) set_xfont(); /* compute minimal window size */ comp_wnd_size(WW,WH); /* buffers for revision bitmaps */ rw = c_realloc(NULL,sizeof(char)*RWX*RWY,NULL); /* alloc colors and create application X window */ if ((batch_mode == 0) && (wnd_ready == 0)) { char *color[6]; int i,j,n,r,x,y; XColor exact; /* get default screen */ xscreen = DefaultScreen(xd); /* get colors */ cmap = DefaultColormap(xd,xscreen); j = 0; if ((cschema!=NULL) && ((n=strlen(cschema)) > 1)) { color[0] = cschema; for (i=0; i= 0) { /* pointer to the hints structure. */ XSizeHints* win_size_hints = XAllocSizeHints(); if (!win_size_hints) { fatal(ME,"XAllocSizeHints"); } /* the hints we want to fill in. */ win_size_hints->flags = USPosition; /* the position */ win_size_hints->x = WX; win_size_hints->y = WY; /* pass the position hints to the window manager. */ XSetWMNormalHints(xd,xw,win_size_hints); XFree(win_size_hints); } /* ask for events */ XSelectInput(xd,XW,evmasks); /* pop this window up on the screen */ if (XMapRaised(xd,XW) == BadWindow) { fatal(XE,"could not create window"); } /* graphics context(s) */ if (!use_xb) { xgc = XCreateGC(xd,xw,0,0); } /* window ready flag */ wnd_ready = 1; } /* initialize document windows */ init_dws(1); /* PAGE special settings */ dw[PAGE].rf = MRF; /* set the window title */ if (batch_mode == 0) { char s[81]; snprintf(s,80,"Clara OCR"); s[80] = 0; swn(s); } } /* Copy the viewable region on the PAGE (fatbits) window to cfont. */ #define MAXHL 10 void copychar(void) { int l,r,t,b,w,h; int k,cs[MAXHL],dx[MAXHL],dy[MAXHL],n; sdesc *y; /* make sure we're at the correct window */ CDW = PAGE_FATBITS; /* mc limits */ l = X0; r = X0 + FS - 1; t = Y0; b = Y0 + FS - 1; w = r-l+1; h = b-t+1; /* clear cfont */ memset(cfont,WHITE,FS*FS); /* clear the border path */ topfp = -1; /* look for closures that intersect the rectangle (l,r,t,b) */ for (n=0,k=0; k<=topps; ++k) { int i,j; int ia,ib,ja,jb; /* Does the symbol k have intersection with the region we're interested on? */ y = mc + ps[k]; if ((intersize(y->l,y->r,l,r,NULL,NULL) > 0) && (intersize(y->t,y->b,t,b,NULL,NULL) > 0)) { /* Is the symbol k entirely contained in the region we're interested on? */ if ((nl) && (y->r<=r) && (t<=y->t) && (y->b<=b)) { dx[n] = y->l - l; dy[n] = y->t - t; cs[n] = ps[k]; ++n; } /* Now we need to compute the values of i and j for which (l+i,t+j) is a point of the closure k and of the rectangle (l,r,t,b). The minimum and maximum such values will be ia, ib, ja and jb: i = ia, ia+1, ..., ib j = ja, ja+1, ..., jb */ if (y->l <= l) ia = 0; else ia = y->l - l; if (y->r >= r) ib = FS - 1; else ib = y->r - l; if (y->t <= t) ja = 0; else ja = y->t - t; if (y->b >= b) jb = FS - 1; else jb = y->b - t; /* copy the sampled pixels to the buffer */ for (i=ia; i<=ib; ++i) { for (j=ja; j<=jb; ++j) { if (spixel(y,l+i,t+j) == BLACK) cfont[i+j*FS] = BLACK; /* else cfont[i+j*FS] = WHITE; */ } } } } /* compute the skeleton on the fly */ if ((*cm_b_skel != ' ') || (*cm_b_border != ' ')) { int u,v,d,e,p; memset(cb,WHITE,LFS*FS); for (u=0; u= 0)) { { int i,j,h; unsigned char m,*p; pdesc *d; memset(cb,WHITE,LFS*FS); d = pattern + id2idx(mc[cs[k]].bm); for (j=0; jbh; ++j) { p = d->b + BLS*j; m = 128; h = (j+dy[k])*LFS; for (i=0; ibw; ++i) { if ((*p & m) != 0) cb[i+dx[k]+h] = BLACK; if ((m >>= 1) <= 0) { ++p; m = 128; } } } } cb_border(0,0); for (v=0; v= 0)) { int i,j,h; unsigned char m,*p; pdesc *d; d = pattern + id2idx(mc[cs[k]].bm); dx[k] += (d->bw - d->sw) / 2; dy[k] += (d->bh - d->sh) / 2; for (j=0; jsh; ++j) { p = d->s + BLS*j; m = 128; h = (j+dy[k])*FS; for (i=0; isw; ++i) { if ((*p & m) != 0) cfont[i+dx[k]+h] = WHITE; if ((m >>= 1) <= 0) { ++p; m = 128; } } } } } redraw_dw = 1; redraw_grid = 1; dw[PAGE_FATBITS].rg = 0; } /* comp_wnd_size: compute the application window size. This routine decides about the window size. The decision process is as follows: 1. If a window size is specified through the parameters ww (width) and wh (height), these are mandatory. 2. If the caller specify ww==0 and wh==0, then the routine computes the minimum width and height, and set the window size to these values. 3. If ww==-1 and wh==-1, then the current window size is used, or enlarged if required. */ void comp_wnd_size(int ww,int wh) { int i,dx,dy; int w,h; unsigned cww,cwh; int old_pt,old_ph; /* remember PT and PH */ old_pt = PT; old_ph = PH; /* tab height is compute from the font height */ TH = DFH + 6; /* menu bar height */ MH = 2 + VS + DFH + VS + 2; /* Largest alphabet label size (currently hardcoded...) */ BW = 8; /* Compute button width and height (the globals BW and BH) from the button labels and the font size. */ for (i=0; i= 0; ++a) { if ((bl[a] == ',') || (bl[a] == ':') || (bl[a] == 0)) { if (a-b > BW) BW = a - b; if (bl[a] == 0) a = -2; else b = a; } } } BW += 1; BW *= DFW; BH = DFH + 4*VS; /* Compute minimum plate width and height. The plate width and height are computed basically from FS and ZPS, in order to the FSxFS grid fit in the plate. In order to guarantee minimum visibility for the pattern properties window, we add 30*DFW. The clearance is 10. The plate height must include the tab height (TH). Obs. these formulae are inverted at the end of this routine, so if you change them, please change that code too. */ PW = 10 + (FS * (ZPS+1) + 10 + 30*DFW + 10 + RW) + 10; PH = TH + 10 + (FS * (ZPS+1) + 10 + RW) + 10; /* plate horizontal and vertical margins */ PM = 10 + BW + (((cm_v_map == NULL) || (*cm_v_map == ' ')) ? 10 : 62); PT = MH + 10; /* get current width and height, if any */ if (have_xw) { Window root; int x,y; unsigned b,d; XGetGeometry(xd,XW,&root,&x,&y,&cww,&cwh,&b,&d); } else { cww = 0; cwh = 0; } /* These are the expected differences (WH-PH) and (WW-PW) between the total height and width and the plate height and width. */ dy = MH + 10 + 10 + DFH + 10; dx = PM + 10; /* oops.. requested height is mandatory */ if (wh > 0) { WH = wh; /* plate height */ PH = WH - dy; } /* First we compute the minimum plate height, then we add dy to obtain the total width. */ { int j; /* the buttons must not exceed the plate vertical limits. */ if (PH < (h=BUTT_PER_COL * (BH+4+VS))) PH = h; /* the welcome window must fit vertically in the plate (15*DFH is an approximation to its height. */ if (PH < (h=15 * (DFH+VS))) PH = h; /* the alphabet map must not exceed the plate vertical limits. */ if ((cm_v_map != NULL) && (*cm_v_map != ' ') && (PH < (h=16*max_sz))) PH = h; /* if the caller requests to preserve the geometry, then try not changing it. */ if ((wh < 0) && (PH < (h=cwh-dy))) PH = h; /* is there a large menu? */ for (j=0; j<=TOP_CM; ++j) { if (PH < (h=CM[j].n * (DFH+VS))) PH = h; } /* add vertical space to the menu bar, status line and clearances */ WH = PH + dy; } /* oops.. requested width is mandatory */ if (ww > 0) { WW = ww; /* plate width */ PW = WW - dx; } /* First we compute the minimum plate width, then we add dx to obtain the total width. */ { int a,i,lw; /* must fit 80 characters horizontally (status line) */ if (PW < (w=80*DFW-dx)) PW = w; /* if the caller requests to preserve the geometry, then try not changing it. */ if ((ww < 0) && (PW < (w=cww-dx))) PW = w; /* minimum tab width */ for (i=0, lw=0; i<=TOP_DW; ++i) if (lw < (a=DFW*strlen(dwname(i)))) lw = a; if (PW < (w=((lw+20)+20)*TABN)) PW = w; /* add button width, alphabet map width (if visible) and clearances */ WW = PW + dx; } /* finally, try a nice 3/4 */ if ((ww == 0) && (wh == 0)) { if (WW*3 < WH*4) { WW = (WH*4) / 3; PW = WW - dx; } else { WH = (WW*3) / 4; PH = WH - dy; } } /* resize the window if the current geometry differs from (WW,WH) */ if (have_xw) { if ((WW != cww) || (WH != cwh)) { XResizeWindow(xd,XW,WW,WH); } } /* alloc or reallocate the display pixmap */ if ((use_xb) && ((!have_pm) || (WW > pmw) || (WH > pmh))) { int d; /* free the current buffer (if any) */ if (have_pm) { XFreeGC(xd,xgc); XFreePixmap(xd,pm); } /* alloc a new one */ d = DefaultDepth(xd,DefaultScreen(xd)); pm = XCreatePixmap(xd,DefaultRootWindow(xd),WW,WH,d); /* could not alloc: switch to unbuffered mode */ if (pm == BadAlloc) { warn("could not alloc buffer, switching to unbuffered mode\n"); have_pm = 0; use_xb = 0; xw = XW; if (have_xw) { xgc = XCreateGC(xd,XW,0,0); } } /* success */ else { have_pm = 1; pmw = WW; pmh = WH; xw = pm; xgc = XCreateGC(xd,pm,0,0); } } /* tab width */ TW = PW/TABN - 20; /* choose (the best possible)/(a sane) ZPS */ { int z,zx,zy; zx = ((PW - 10 - 10 - 10 - 30*DFW - 10 - RW) / FS) - 1; if ((zx & 1) == 0) zx -= 1; zy = ((PH - TH - 10 - 10 - 10 - RW) / FS) - 1; if ((zy & 1) == 0) zy -= 1; if ((z=zx) > zy) z = zy; /* the best possible */ /* if ((1 <= z) && (ZPS != z)) ZPS = z; */ /* a sane */ if ((1 <= z) && (z < ZPS)) ZPS = z; } /* PAGE special settings. When resizing the window, we try to preserve the same relative height for the windows PAGE, PAGE_OUTPUT and PAGE_SYMBOL, that share one mode of the PAGE tab. The height of these three windows is determined by the variables page_j1 and page_j2 (first and second junctions). So we need to recompute them. +------------------------------------------+ | File Edit... | +------------------------------------------+ | +------+ +----+ +--------+ +-------+ | | | zoom | |page| |patterns| | tune | | | +------+ +-+ +-+ +-+ +-+ | | +------+ | +-------------------------+ | | | | zone | | | (PAGE) | | | | +------+ | | | | | | +------+ | +-------------------------+ | | | | OCR | | | | - page_j1 | +------+ | +-------------------------+ | | | +------+ | | (PAGE_OUTPUT) | | | | | stop | | | | | | | +------+ | +-------------------------+ | | | . | | | - page_j2 | . | +-------------------------+ | | | | | (PAGE_SYMBOL) | | | | | | | | | | | +-------------------------+ | | | +-----------------------------+ | | | | (status line) | +------------------------------------------+ */ if (page_j1 < 0) { opage_j1 = page_j1 = PT+TH+(PH-TH)/3; opage_j2 = page_j2 = PT+TH+2*(PH-TH)/3; } else { float a; a = ((float)(old_ph-TH)) / (page_j1-old_pt-TH); page_j1 = PT+TH+(PH-TH)/a; a = ((float)(old_ph-TH)) / (page_j2-old_pt-TH); page_j2 = PT+TH+(PH-TH)/a; /* make sure that the height of PAGE_OUTPUT is at least 4*RW. */ if (page_j2 - page_j1 < 20+4*RW) page_j1 = page_j2 - 20 - 4*RW; /* make sure that the height of PAGE is at least 4*RW. */ if (page_j1-PT < TH+30+4*RW) { page_j1 = PT+TH+30+4*RW; page_j2 = page_j1+20+4*RW; } /* make sure that the height of PAGE_SYMBOL is at least 4*RW. */ if (PT+PH-page_j2 <30+4*RW) { page_j2 = PT+PH-30-4*RW; page_j1 = page_j2-20-4*RW; } opage_j1 = page_j1; opage_j2 = page_j2; } } #ifdef HAVE_SIGNAL /* This is a first draft for a presentation routine. By now it merely changes to large font, then to small font, returns to default font and stops. Obs. any call to new_alrm must be done just before returning from this service. */ void cpresentation(int reset) { /* presentation and current state */ static int p,s; if ((have_s_ev) || (ocring)) { new_alrm(100000,2); return; } /* initialize presentation */ if (reset) { pmode = 1; p = s = 0; xev.type = FocusIn; have_s_ev = 1; new_alrm(100000,2); return; } /* Presentation 0: change font */ if (p == 0) { int delay; if (s == 0) { ++s; if (!mselect(CM_V_LARGE,&delay)) ++s; } else if (s == 1) { if (!mselect(CM_V_SMALL,&delay)) ++s; } else if (s == 2) { if (!mselect(CM_V_DEF,&delay)) ++s; } else if (s == 3) { if (!fselect("imre.pbm",&delay)) ++s; } else if (s == 4) { if (!bselect(bocr,&delay)) ++s; } else if (s == 5) { if (!sselect(5,&delay)) ++s; } else if (s == 6) { if (!press('r',&delay)) ++s; } else if (s == 7) { if (!sselect(235,&delay)) ++s; } else if (s == 8) { if (!press('h',&delay)) ++s; } else if (s == 9) { if (!bselect(bocr,&delay)) ++s; } else if (s == 10) { pmode = 0; if (allow_pres == 2) finish = 1; } if (pmode) { if (allow_pres == 2) new_alrm(250000,2); else new_alrm(delay,2); } } } #endif