1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18     Last edit by hansen on Wed Mar 18 04:17:20 2009
19 ****************************************************************************/
20 
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <sys/time.h>
25 #include <stdarg.h>
26 #include "tkgate.h"
27 #if HAVE_ICONV_H
28 #include <iconv.h>
29 #endif
30 
31 int guiActive = 0;
32 
33 int MESSAGEBARSIZE = 50;
34 
35 int MB_MESSAGEX = 25;
36 int MB_MESSAGEY = 40;
37 
38 int MB_STATUSX = 25;
39 int MB_STATUSY = 20;
40 
41 int KANJIFONT_WIDTH    	= 14;
42 
43 /*
44  * The editstate is an optional argument need for modes 2 and 3.
45  *
46  * Notes:
47  *   TkGate.tcl		is non-zero if we are using a GUI
48  *   guiActive		is non-zero if the GUI is being actively displayed
49  *
50  */
message(int mode,const char * s,...)51 void message(int mode,const char *s,...)
52 {
53   va_list ap;
54   char buf[1024];
55   extern int quietMode;
56   int did_display = 0;		/* Could we display message in GUI? */
57 
58   va_start(ap, s);
59   vsprintf(buf,s,ap);
60   va_end(ap);
61 
62 
63   if (TkGate.tcl && mode == 0) {
64     /*
65      * Legacy support for old-style mode values
66      */
67     Tcl_SetVar(TkGate.tcl,"tkg_statusMessage",buf,TCL_LEAVE_ERR_MSG|TCL_GLOBAL_ONLY);
68     return;
69   }
70 
71   if (TkGate.tcl && (mode & MC_STATUS)) {
72     Tcl_SetVar(TkGate.tcl,"tkg_statusMessage",buf,TCL_LEAVE_ERR_MSG|TCL_GLOBAL_ONLY);
73     did_display = 1;
74   }
75 
76 
77   if (guiActive && (mode & MC_ERRBOX)) {
78     Tcl_VarEval(TkGate.tcl, "errmsg {",buf,"}",0);
79     did_display = 1;
80   }
81 
82   if (TkGate.tcl && (mode & MC_MSGLOG)) {
83     const char *color = 0;
84 
85     if ((mode & MC_URGENT))
86       color = "red";
87     else if ((mode & MC_WARNING))
88       color = "goldenrod";
89     else
90       color = "black";
91 
92     if ((mode & MC_SILENT))
93       DoTclL("InfoPanel::log",buf,"-noshow","1","-color",color,NULL);
94     else
95       DoTclL("InfoPanel::log",buf,"-color",color,NULL);
96     did_display = 1;
97   }
98 
99   /*
100    * If we were not able to display the message through the GUI and we
101    * are not in quite mode, display it to the terminal.
102    */
103   if (!quietMode && !did_display) {
104     fprintf(stderr,"tkgate: %s\n",buf);
105   }
106 
107   /*
108    * If we were not able to display the message in the GUI yet, defer it
109    * for later display.
110    */
111   if (!did_display) {
112   }
113 }
114 
fontheight(XFontStruct * F)115 int fontheight(XFontStruct *F)
116 {
117   return F->ascent+F->descent + 3;
118 }
119 
120 /*
121     Draw the string ala ZDrawString.  Includes temporary hacked support
122     for strings with mixed latin and kanji characters.  Characters with
123     the high bit set are assumed to be kanji.  This function is partially
124     broken in that it assumes the font for the latin portion is TkGate.textF
125     when in fact it may be whatever is set in the gc.  For now, since kanji
126     is only allowed in comments and frames, this should not be a problem.
127 */
GKDrawString(GatePainter * painter,GC gc,int x,int y,const char * ps,int l)128 void GKDrawString(GatePainter *painter,GC gc,int x,int y,const char *ps,int l)
129 {
130   if (TkGate.japaneseMode) {
131     char buf[STRMAX];
132     char *s = buf;
133 
134     strncpy(buf,ps,STRMAX);
135     while (l > 0) {
136       int m = 0;
137       int i;
138 
139       if ((s[0] & 0x80)) {		/* Kanji segment */
140 	for (m = 0;m < l && (s[m] & 0x80);m++);
141 	for (i = 0;i < m;i++) s[i] &= 0x7f;
142 	/* TODO fixme */
143 	//ZDrawString16(D,W,TkGate.kanjiGC,x,y,(XChar2b*)s,m/2);
144 	if (m != l) x += KANJIFONT_WIDTH*(m/2);
145 	for (i = 0;i < m;i++) s[i] |= 0x80;
146       } else {				/* non-Kanji segment */
147 	for (m = 0;m < l && !(s[m] & 0x80);m++);
148 	ZDrawString(painter,gc,x,y,s,m);
149 	if (m != l) x += XTextWidth(TkGate.textXF[TkGate.circuit->zoom_factor],s,m);
150       }
151 
152       l -= m;
153       s += m;
154     }
155   } else {
156     /*
157      * If not handling Japanese, don't do any special processing.
158      */
159     ZDrawString(painter,gc,x,y,(char*)ps,l);
160   }
161 }
162 
163 /*
164    Get the width of a string.  This may be extended in the future to handle
165    mixed english/japanese fonts.
166 */
GKTextWidth(XFontStruct * F,const char * ps,int l)167 int GKTextWidth(XFontStruct *F,const char *ps,int l)
168 {
169   if (TkGate.japaneseMode) {
170     char buf[STRMAX];
171     char *s = buf;
172     int w = 0;
173 
174 
175     strncpy(buf,ps,STRMAX);
176 
177     while (l > 0) {
178       int m = 0;
179       int i;
180 
181       if ((s[0] & 0x80)) {		/* Kanji segment */
182 	for (m = 0;m < l && (s[m] & 0x80);m++);
183 	for (i = 0;i < m;i++) s[i] &= 0x7f;
184 
185 	w += KANJIFONT_WIDTH*(m/2);
186 	for (i = 0;i < m;i++) s[i] |= 0x80;
187       } else {				/* non-Kanji segment */
188 	for (m = 0;m < l && !(s[m] & 0x80);m++);
189 	w += XTextWidth(F,s,m);
190       }
191 
192       l -= m;
193       s += m;
194     }
195 
196     return w;
197   } else {
198     return XTextWidth(F,ps,l);
199   }
200 }
201 
PosDrawString(GatePainter * painter,XFontStruct * F,GC gc,int x,int y,const char * S,int p)202 int PosDrawString(GatePainter *painter,XFontStruct *F,GC gc,int x,int y,const char *S,int p){
203   int x_w,y_w;
204 
205   if (!F) F = TkGate.textXF[1];
206 
207   if (*S == '_') {
208     S++;
209     x_w = GKTextWidth(F,S,strlen(S));
210     y_w = fontheight(F);
211 
212     if (p & BetweenLeftAndRight) {
213       ZDrawLine(TkGate.D,GatePainter_drawable(painter),gc,x - x_w/2,y - 2*y_w/3,
214 		x + x_w/2,y - 2*y_w/3);
215     } else if (p & AtRight) {
216       ZDrawLine(TkGate.D,GatePainter_drawable(painter),gc,x - x_w,y - 2*y_w/3,
217 		x,y - 2*y_w/3);
218     } else if (p & AtLeft) {
219       ZDrawLine(TkGate.D,GatePainter_drawable(painter),gc,x,y - 2*y_w/3,
220 		x + x_w,y - 2*y_w/3);
221     }
222   } else
223     x_w = GKTextWidth(F,S,strlen(S));
224 
225   if (p & BetweenLeftAndRight)
226     x -= x_w/2;
227   else if (p & AtRight)
228     x -= x_w;
229 
230   if (p & BetweenTopAndBottom)
231     y += (F->ascent - F->descent)/2;
232   else if (p & AtTop)
233     y += F->ascent;
234   else if (p & AtBottom)
235     y -= F->descent;
236 
237 
238   GKDrawString(painter,gc,x,y,S,strlen(S));
239 
240   return x_w + x;
241 }
242 
dce_DrawString(GC gc,int x,int y,int p,const char * s)243 int dce_DrawString(GC gc,int x,int y,int p,const char *s)
244 {
245   return PosDrawString(TkGate.painterW,0,gc,ctow_x(x),ctow_y(y),s,p);
246 }
247 
RelPosDrawString(GatePainter * painter,XFontStruct * F,GC gc,int x,int y,const char * S,int p)248 int RelPosDrawString(GatePainter *painter,XFontStruct *F,GC gc,int x,int y,const char *S,int p)
249 {
250   int ex = PosDrawString(painter,F,gc,ctow_x(x),ctow_y(y),S,p);
251   return wtoc_x(ex);
252 }
253 
254 /*
255  * Lookup a message from the appropriate locality file.
256  */
msgLookup(const char * tag)257 char *msgLookup(const char *tag)
258 {
259   char *s = 0;
260   int i;
261 
262   for (i = 0;i < 10;i++) {
263     s = (char*) SHash_find(message_table,tag);
264 
265     if (!s) {
266       /*
267        * Message not found, generate placeholder message
268        */
269       char buf[1024];
270       sprintf(buf,"<no-msg: %s>",tag);
271       s = ob_strdup(buf);
272       SHash_insert(message_table,tag,s);
273       break;
274     } else if (*s == '`') {
275       /*
276        * Redirection message.  Assume message body points to another message.
277        */
278       tag = s+1;
279     } else
280       break;
281   }
282 
283   if (i == 10) {
284     /*
285      * Too many levels of indirection
286      */
287     char buf[1024];
288     sprintf(buf,"<recursive: %s>",tag);
289     s = ob_strdup(buf);
290     SHash_insert(message_table,tag,s);
291   }
292 
293   return s;
294 }
295 
296 /*
297  * Character encoding translation code
298  */
299 #if HAVE_ICONV_H
300 static SHash *iconv_translators = 0;
301 
getEncoder(const char * toCode,const char * fromCode)302 Encoder *getEncoder(const char *toCode,const char *fromCode)
303 {
304   char key[STRMAX];
305   Encoder *encoder = 0;
306 
307   /*
308    * If either encoding is null, return null encoder.
309    */
310   if (strcmp(toCode,"null") == 0) return 0;
311   if (strcmp(fromCode,"null") == 0) return 0;
312 
313   /*
314    * Allocate a new translator table
315    */
316   if (!iconv_translators)
317     iconv_translators = new_SHash_noob();
318 
319   /*
320    * Construct key and see if we have a translator.
321    */
322   sprintf(key,"%s %s",toCode,fromCode);
323   encoder = (Encoder*) SHash_find(iconv_translators, key);
324   if (encoder)
325     return encoder;
326 
327   /*
328    * Try to create the translator, return 0 (and an error) if we could not.
329    */
330   encoder = (Encoder*) malloc(sizeof(Encoder));
331   encoder->fromCode = strdup(fromCode);
332   encoder->toCode = strdup(toCode);
333   encoder->ico = iconv_open(toCode,fromCode);
334   encoder->isJapanese = (strcmp(encoder->toCode,CE_EUC_JP) == 0);
335 
336   SHash_insert(iconv_translators, key, encoder);
337   if (encoder->ico == ((iconv_t)-1)) {
338     logError(ERL_ERROR,"Unable to create encoding <%s> to <%s>.",fromCode,toCode);
339     return 0;
340   }
341 #if LOCALE_DEBUG
342   printf("created encoder <%s> to <%s>\n",fromCode,toCode);
343 #endif
344 
345   return encoder;
346 }
347 
recodeText(Encoder * encoder,char * toString,int len,const char * fromString)348 size_t recodeText(Encoder *encoder, char *toString,int len, const char *fromString)
349 {
350   size_t inSize,outSize,result;
351   const char *inPtr;
352   char *outPtr;
353 
354   /*
355    * There was no encoder.  Just copy fromString to toString.
356    */
357   if (!encoder || encoder->ico == ((iconv_t)-1)) {
358     strncpy(toString,fromString,len);
359     toString[len-1] = 0;
360     return 0;
361   }
362 #if LOCALE_DEBUG
363   printf("recoding <%s> to <%s>: %s\n",encoder->fromCode,encoder->toCode,fromString);
364 #endif
365 
366   inPtr = fromString;
367   inSize = strlen(fromString);
368   outPtr = toString;
369   outSize = len;
370 
371   result = iconv(encoder->ico, (char**)&inPtr, &inSize, &outPtr, &outSize);
372   if (result == (size_t)-1) {
373     perror("iconv");
374     strncpy(toString,fromString,len);
375     toString[len-1] = 0;
376     return -1;
377   }
378 
379   *outPtr = 0;
380 
381   return result;
382 }
383 
recodeTextP(Encoder * encoder,const char * fromString)384 char *recodeTextP(Encoder *encoder, const char *fromString)
385 {
386   size_t inSize,outSize,result;
387   const char *inPtr;
388   char *outPtr;
389   char *outBuf;
390   size_t bufSize;
391 
392   /*
393    * There was no encoder.  Just copy fromString to toString.
394    */
395   if (!encoder || encoder->ico == ((iconv_t)-1)) {
396     return strdup(fromString);
397   }
398 #if LOCALE_DEBUG
399   printf("recoding <%s> to <%s>: %s\n",encoder->fromCode,encoder->toCode,fromString);
400 #endif
401   inPtr = fromString;
402   inSize = strlen(fromString);
403   outSize = inSize+1;
404   bufSize = outSize;
405   outBuf = (char*)malloc(outSize);
406   outPtr = outBuf;
407 
408   while (inSize > 0) {
409     result = iconv(encoder->ico, (char**)&inPtr, &inSize, &outPtr, &outSize);
410     if (result == (size_t)-1) {
411       perror("iconv");
412       /* Coding error - returning intranslated string */
413       return strdup(fromString);
414     }
415     if (inSize > 0) {
416       char *nb = (char*) realloc(outBuf,bufSize+2*inSize+1);
417       outPtr = nb + (outPtr-outBuf);
418       outBuf = nb;
419     }
420   }
421 
422   *outPtr = 0;
423 
424   return outBuf;
425 }
426 
427 
isJapaneseDisplay(Encoder * encoder)428 int isJapaneseDisplay(Encoder *encoder)
429 {
430   return encoder && encoder->isJapanese;
431 }
432 
433 #else
434 
435 /*
436  * We could not create the encoder, so we will to null-translations.
437  */
438 
getEncoder(const char * toCode,const char * fromCode)439 Encoder *getEncoder(const char *toCode,const char *fromCode)
440 {
441   return 0;
442 }
443 
recodeText(Encoder * encoder,char * toString,int len,const char * fromString)444 size_t recodeText(Encoder *encoder, char *toString,int len, const char *fromString)
445 {
446   strncpy(toString,fromString,len);
447   toString[len-1] = 0;
448   return 0;
449 }
450 
isJapaneseDisplay(Encoder * e)451 int isJapaneseDisplay(Encoder *e)
452 {
453   return 0;
454 }
455 
456 #endif
457