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