1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "gdraw.h"
31 #include "ggadgetP.h"
32 #include "gkeysym.h"
33 #include "gresource.h"
34 #include "gresourceP.h"
35 #include "hotkeys.h"
36 #include "ustring.h"
37 #include "utype.h"
38 
39 /////////////////////////////////////////////////////////////////
40 // The below keys are from this file, when/if we move to GTK+
41 // then perhaps we should include this file instead of the inline
42 // definitions
43 //#include <gdk/gdkkeysyms.h>
44 #define GDK_KEY_Tab 0xff09
45 #define GDK_KEY_ISO_Left_Tab 0xfe20
46 /////////////////////////////////////////////////////////////////
47 
GTextInfoGetWidth(GWindow base,GTextInfo * ti,FontInstance * font)48 int GTextInfoGetWidth(GWindow base,GTextInfo *ti,FontInstance *font) {
49     int width=0;
50     int iwidth=0;
51     int skip = 0;
52 
53     if ( ti->text!=NULL ) {
54 	if ( ti->font!=NULL )
55 	    font = ti->font;
56 
57 	if ( font!=NULL )
58 	    GDrawSetFont(base,font);
59 	width = GDrawGetTextWidth(base,ti->text, -1);
60     }
61     if ( ti->image!=NULL ) {
62 	iwidth = GImageGetScaledWidth(base,ti->image);
63 	if ( ti->text!=NULL )
64 	    skip = GDrawPointsToPixels(base,6);
65     }
66 return( width+iwidth+skip );
67 }
68 
GTextInfoGetMaxWidth(GWindow base,GTextInfo ** ti,FontInstance * font)69 int GTextInfoGetMaxWidth(GWindow base,GTextInfo **ti,FontInstance *font) {
70     int width=0, temp;
71     int i;
72 
73     for ( i=0; ti[i]->text!=NULL || ti[i]->image!=NULL; ++i ) {
74 	if (( temp= GTextInfoGetWidth(base,ti[i],font))>width )
75 	    width = temp;
76     }
77 return( width );
78 }
79 
GTextInfoGetHeight(GWindow base,GTextInfo * ti,FontInstance * font)80 int GTextInfoGetHeight(GWindow base,GTextInfo *ti,FontInstance *font) {
81     int fh=0, as=0, ds=0, ld;
82     int iheight=0;
83     int height;
84     GTextBounds bounds;
85 
86     if ( ti->font!=NULL )
87 	font = ti->font;
88     GDrawWindowFontMetrics(base,font,&as, &ds, &ld);
89     if ( ti->text!=NULL ) {
90 	GDrawSetFont(base,font);
91 	GDrawGetTextBounds(base,ti->text, -1, &bounds);
92 	if ( as<bounds.as ) as = bounds.as;
93 	if ( ds<bounds.ds ) ds = bounds.ds;
94     }
95     fh = as+ds;
96     if ( ti->image!=NULL ) {
97 	iheight = GImageGetScaledHeight(base,ti->image);
98 	iheight += 1;
99     }
100     if ( (height = fh)<iheight ) height = iheight;
101 return( height );
102 }
103 
GTextInfoGetMaxHeight(GWindow base,GTextInfo ** ti,FontInstance * font,int * allsame)104 int GTextInfoGetMaxHeight(GWindow base,GTextInfo **ti,FontInstance *font,int *allsame) {
105     int height=0, temp, same=1;
106     int i;
107 
108     for ( i=0; ti[i]->text!=NULL || ti[i]->image!=NULL; ++i ) {
109 	temp= GTextInfoGetHeight(base,ti[i],font);
110 	if ( height!=0 && height!=temp )
111 	    same = 0;
112 	if ( height<temp )
113 	    height = temp;
114     }
115     *allsame = same;
116 return( height );
117 }
118 
GTextInfoGetAs(GWindow base,GTextInfo * ti,FontInstance * font)119 int GTextInfoGetAs(GWindow base,GTextInfo *ti, FontInstance *font) {
120     int fh=0, as=0, ds=0, ld;
121     int iheight=0;
122     int height;
123     GTextBounds bounds;
124 
125     GDrawWindowFontMetrics(base,font,&as, &ds, &ld);
126     if ( ti->text!=NULL ) {
127 	GDrawSetFont(base,font);
128 	GDrawGetTextBounds(base,ti->text, -1, &bounds);
129 	if ( as<bounds.as ) as = bounds.as;
130 	if ( ds<bounds.ds ) ds = bounds.ds;
131     }
132     fh = as+ds;
133     if ( ti->image!=NULL ) {
134 	iheight = GImageGetScaledHeight(base,ti->image);
135     }
136     if ( (height = fh)<iheight ) height = iheight;
137 
138     if ( ti->text!=NULL )
139 return( as+(height>fh?(height-fh)/2:0) );
140 
141 return( iheight );
142 }
143 
GTextInfoDraw(GWindow base,int x,int y,GTextInfo * ti,FontInstance * font,Color fg,Color sel,int ymax)144 int GTextInfoDraw(GWindow base,int x,int y,GTextInfo *ti,
145 	FontInstance *font,Color fg, Color sel, int ymax) {
146     int fh=0, as=0, ds=0, ld;
147     int iwidth=0, iheight=0;
148     int height, skip = 0;
149     GTextBounds bounds;
150     GRect r, old;
151 
152     GDrawWindowFontMetrics(base,font,&as, &ds, &ld);
153     if ( ti->text!=NULL ) {
154 	if ( ti->font!=NULL )
155 	    font = ti->font;
156 	if ( ti->fg!=COLOR_DEFAULT && ti->fg!=COLOR_UNKNOWN )
157 	    fg = ti->fg;
158 
159 	GDrawSetFont(base,font);
160 	GDrawGetTextBounds(base,ti->text, -1, &bounds);
161 	if ( as<bounds.as ) as = bounds.as;
162 	if ( ds<bounds.ds ) ds = bounds.ds;
163     }
164     fh = as+ds;
165     if ( fg == COLOR_DEFAULT )
166 	fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(base));
167     if ( ti->image!=NULL ) {
168 	iwidth = GImageGetScaledWidth(base,ti->image);
169 	iheight = GImageGetScaledHeight(base,ti->image)+1;
170 	if ( ti->text!=NULL )
171 	    skip = GDrawPointsToPixels(base,6);
172     }
173     if ( (height = fh)<iheight ) height = iheight;
174 
175     r.y = y; r.height = height;
176     r.x = 0; r.width = 10000;
177 
178     if ( ti->line ) {
179 	_GGroup_Init();
180 	GDrawGetClip(base,&r);
181 	r.x += GDrawPointsToPixels(base,2); r.width -= 2*GDrawPointsToPixels(base,2);
182 	GDrawPushClip(base,&r,&old);
183 	r.y = y; r.height = height;
184 	r.x = x; r.width = 10000;
185 	GBoxDrawHLine(base,&r,&_GGroup_LineBox);
186 	GDrawPopClip(base,&old);
187     } else {
188 	if (( ti->selected && sel!=COLOR_DEFAULT ) || ( ti->bg!=COLOR_DEFAULT && ti->bg!=COLOR_UNKNOWN )) {
189 	    Color bg = ti->bg;
190 	    if ( ti->selected ) {
191 		if ( sel==COLOR_DEFAULT )
192 		    sel = fg;
193 		bg = sel;
194 		if ( sel==fg ) {
195 		    fg = ti->bg;
196 		    if ( fg==COLOR_DEFAULT || fg==COLOR_UNKNOWN )
197 			fg = GDrawGetDefaultBackground(GDrawGetDisplayOfWindow(base));
198 		}
199 	    }
200 	    GDrawFillRect(base,&r,bg);
201 	}
202 
203 	if ( ti->image!=NULL && ti->image_precedes ) {
204 	    GDrawDrawScaledImage(base,ti->image,x,y + (iheight>as?0:as-iheight));
205 	    x += iwidth + skip;
206 	}
207 	if ( ti->text!=NULL ) {
208 	    int ypos = y+as+(height>fh?(height-fh)/2:0);
209 	    int width = GDrawDrawText(base,x,ypos,ti->text,-1,fg);
210 	    _ggadget_underlineMnemonic(base,x,ypos,ti->text,ti->mnemonic,fg,ymax);
211 	    x += width + skip;
212 	}
213 	if ( ti->image!=NULL && !ti->image_precedes )
214 	    GDrawDrawScaledImage(base,ti->image,x,y + (iheight>as?0:as-iheight));
215     }
216 
217 return( height );
218 }
219 
utf82u_mncopy(const char * utf8buf,unichar_t * mn)220 unichar_t *utf82u_mncopy(const char *utf8buf,unichar_t *mn) {
221     int len = strlen(utf8buf);
222     unichar_t *ubuf = malloc((len+1)*sizeof(unichar_t));
223     unichar_t *upt=ubuf, *uend=ubuf+len;
224     const uint8 *pt = (const uint8 *) utf8buf, *end = pt+strlen(utf8buf);
225     int w;
226     int was_mn = false;
227 
228     *mn = '\0';
229     while ( pt<end && *pt!='\0' && upt<uend ) {
230 	if ( *pt<=127 ) {
231 	    if ( *pt!='_' )
232 		*upt = *pt++;
233 	    else {
234 		was_mn = 2;
235 		++pt;
236 		--upt;
237 	    }
238 	} else if ( *pt<=0xdf ) {
239 	    *upt = ((*pt&0x1f)<<6) | (pt[1]&0x3f);
240 	    pt += 2;
241 	} else if ( *pt<=0xef ) {
242 	    *upt = ((*pt&0xf)<<12) | ((pt[1]&0x3f)<<6) | (pt[2]&0x3f);
243 	    pt += 3;
244 	} else if ( upt+1<uend ) {
245 	    /* Um... I don't support surrogates */
246 	    w = ( ((*pt&0x7)<<2) | ((pt[1]&0x30)>>4) )-1;
247 	    *upt++ = 0xd800 | (w<<6) | ((pt[1]&0xf)<<2) | ((pt[2]&0x30)>>4);
248 	    *upt   = 0xdc00 | ((pt[2]&0xf)<<6) | (pt[3]&0x3f);
249 	    pt += 4;
250 	} else {
251 	    /* no space for surrogate */
252 	    pt += 4;
253 	}
254 	++upt;
255 	if ( was_mn==1 ) {
256 	    *mn = upt[-1];
257 	    if ( islower(*mn) ) *mn = toupper(*mn);
258 	}
259 	--was_mn;
260     }
261     *upt = '\0';
262 return( ubuf );
263 }
264 
GTextInfoCopy(GTextInfo * ti)265 GTextInfo *GTextInfoCopy(GTextInfo *ti) {
266     GTextInfo *copy;
267 
268     copy = malloc(sizeof(GTextInfo));
269     *copy = *ti;
270     copy->text_is_1byte = false;
271     if ( copy->fg == 0 && copy->bg == 0 ) {
272 	copy->fg = copy->bg = COLOR_UNKNOWN;
273     }
274     if ( ti->text!=NULL ) {
275 	if ( ti->text_is_1byte && ti->text_in_resource ) {
276 	    copy->text = utf82u_mncopy((char *) copy->text,&copy->mnemonic);
277 	    copy->text_in_resource = false;
278 	    copy->text_is_1byte = false;
279 	} else if ( ti->text_in_resource ) {
280 	    copy->text = u_copy((unichar_t *) GStringGetResource((intpt) copy->text,&copy->mnemonic));
281 	    copy->text_in_resource = false;
282 	} else if ( ti->text_is_1byte ) {
283 	    copy->text = utf82u_copy((char *) copy->text);
284 	    copy->text_is_1byte = false;
285 	} else
286 	    copy->text = u_copy(copy->text);
287     }
288 return( copy);
289 }
290 
291 static const char *imagedir_default = "fontforge-pixmaps";
292 static char *imagedir = NULL;	/* This is the system pixmap directory */
293 static char **imagepath = NULL;			/* May contain user directories too */
294 static size_t imagepathlenmax = 0;
295 
296 struct image_bucket {
297     struct image_bucket *next;
298     char *filename, *absname;
299     GImage *image;
300 };
301 
302 #define IC_SIZE	127
303 static struct image_bucket *imagecache[IC_SIZE];
304 
InitImageCache()305 void InitImageCache() {
306   memset(imagecache, 0, IC_SIZE * sizeof(struct image_bucket *));
307 }
308 
ClearImageCache()309 void ClearImageCache() {
310       for ( int i=0; i<IC_SIZE; ++i ) {
311         struct image_bucket * bucket;
312         struct image_bucket * nextbucket;
313 	for ( bucket = imagecache[i]; bucket!=NULL; bucket=nextbucket ) {
314           nextbucket = bucket->next;
315           if (bucket->filename != NULL) { free(bucket->filename); bucket->filename = NULL; }
316           if (bucket->absname != NULL) { free(bucket->absname); bucket->absname = NULL; }
317           if (bucket->image != NULL) { GImageDestroy(bucket->image); bucket->image = NULL; }
318           free(bucket);
319         }
320         imagecache[i] = NULL;
321      }
322 }
323 
hash_filename(const char * _pt)324 static int hash_filename(const char *_pt ) {
325     const unsigned char *pt = (const unsigned char *) _pt;
326     int val = 0;
327 
328     while ( *pt ) {
329 	val <<= 1;
330 	if ( val & 0x8000 ) {
331 	    val &= ~0x8000;
332 	    val ^= 1;
333 	}
334 	val ^= *pt++;
335     }
336 return( val%IC_SIZE );
337 }
338 
ImagePathDefault(void)339 static void ImagePathDefault(void) {
340     if ( imagepath==NULL ) {
341 	imagepath = malloc(2*sizeof(void *));
342 	imagepath[0] = (imagedir == NULL) ? copy(imagedir_default) : copy(imagedir);
343 	imagepath[1] = NULL;
344 	imagepathlenmax = strlen(imagepath[0]);
345 	if (_GGadget_ImagePath != NULL) free(_GGadget_ImagePath);
346 	_GGadget_ImagePath = copy("=");
347     }
348 }
349 
350 /**
351  * \return The image path. The return value should not be freed or modified.
352  */
_GGadget_GetImagePath(void)353 const char* const* _GGadget_GetImagePath(void) {
354     ImagePathDefault();
355     return (const char* const*) imagepath;
356 }
357 
_GGadget_ImageInCache(GImage * image)358 int _GGadget_ImageInCache(GImage *image) {
359     int i;
360     struct image_bucket *bucket;
361 
362     for ( i=0; i<IC_SIZE; ++i ) {
363 	for ( bucket=imagecache[i]; bucket!=NULL; bucket=bucket->next )
364 	    if ( bucket->image==image )
365 return( true );
366     }
367 return( false );
368 }
369 
ImageCacheReload(void)370 static void ImageCacheReload(void) {
371     int i,k;
372     struct image_bucket *bucket;
373     char *path=NULL;
374     size_t pathlen;
375     GImage *temp, hold;
376 
377     ImagePathDefault();
378 
379     /* Try to reload the cache from the new directory */
380     /* If a file doesn't exist in the new dir when it did in the old then */
381     /*  retain the old copy (people may hold pointers to it) */
382     pathlen = imagepathlenmax+270; path = malloc(pathlen);
383     for ( i=0; i<IC_SIZE; ++i ) {
384 	for ( bucket = imagecache[i]; bucket!=NULL; bucket=bucket->next ) {
385 	    if ( strlen(bucket->filename)+imagepathlenmax+3 > pathlen ) {
386 		pathlen = strlen(bucket->filename)+imagepathlenmax+20;
387 		path = realloc(path,pathlen);
388 	    }
389 	    for ( k=0; imagepath[k]!=NULL; ++k ) {
390 		sprintf( path,"%s/%s", imagepath[k], bucket->filename );
391 		temp = GImageRead(path);
392 		if ( temp!=NULL )
393 	    break;
394 	    }
395 	    if ( temp!=NULL ) {
396 		if ( bucket->image==NULL )
397 		    bucket->image = temp;
398 		else {
399 		    /* Need to retain the GImage point, but update its */
400 		    /*  contents, and free the old stuff */
401 		    hold = *(bucket->image);
402 		    *bucket->image = *temp;
403 		    *temp = hold;
404 		    GImageDestroy(temp);
405 		}
406 		free( bucket->absname );
407 		bucket->absname = copy( path );
408 	    }
409 	}
410     }
411     free(path);
412 }
413 
GGadgetSetImageDir(char * dir)414 void GGadgetSetImageDir(char *dir) {
415     int k;
416     char *ptr = imagedir;
417     //Check if imagedir has been initialised
418     if (ptr == NULL) {
419         //We shall check later if ptr should be freed or not
420         ptr = (char*) imagedir_default;
421     }
422 
423     if (dir != NULL && strcmp(ptr,dir) != 0) {
424         imagedir = copy(dir);
425         if (imagepath != NULL) {
426             for (k=0; imagepath[k] != NULL; ++k) {
427                 if (strcmp(imagepath[k],ptr) == 0) {
428                     break;
429                 }
430             }
431 
432             if (ptr != imagedir_default) {
433                 free(ptr);
434             }
435             if (imagepath[k] != NULL) {
436                 free(imagepath[k]);
437                 imagepath[k] = copy(imagedir);
438                 ImageCacheReload();
439             }
440             if (_GGadget_ImagePath != NULL) free(_GGadget_ImagePath);
441             _GGadget_ImagePath = copy("=");
442         }
443     }
444 }
445 
ImagePathFigureElement(char * start,int len)446 static char *ImagePathFigureElement(char *start, int len) {
447     if ( *start=='=' && len==1 ) {
448         if (imagedir == NULL) {
449             return copy(imagedir_default);
450         }
451         return copy(imagedir);
452     }
453     else if ( *start=='~' && start[1]=='/' && len>=2 && getenv("HOME")!=NULL ) {
454 	int hlen = strlen(getenv("HOME"));
455 	char *absname = malloc( hlen+len+8 );
456 	strcpy(absname,getenv("HOME"));
457 	strncpy(absname+hlen,start+1,len-1);
458 	absname[hlen+len-1] = '\0';
459 return( absname );
460     } else
461 return( copyn(start,len));
462 }
463 
464 #ifndef PATH_SEPARATOR
465 # define PATH_SEPARATOR ':'
466 #endif
467 
GGadgetSetImagePath(char * path)468 void GGadgetSetImagePath(char *path) {
469     int cnt, k;
470     char *pt, *end;
471     extern char *_GGadget_ImagePath;
472 
473     if ( path==NULL )
474 return;
475     if (_GGadget_ImagePath != NULL) free( _GGadget_ImagePath );
476 
477     if ( imagepath!=NULL ) {
478 	for ( k=0; imagepath[k]!=NULL; ++k )
479 	    free( imagepath[k] );
480 	free( imagepath );
481     }
482     for ( cnt=0, pt = path; (end = strchr(pt,PATH_SEPARATOR))!=NULL; ++cnt, pt = end+1 );
483     imagepath = malloc((cnt+2)*sizeof(char *));
484     for ( cnt=0, pt = path; (end = strchr(pt,PATH_SEPARATOR))!=NULL; ++cnt, pt = end+1 )
485 	imagepath[cnt] = ImagePathFigureElement(pt,end-pt);
486     imagepath[cnt] = ImagePathFigureElement(pt,strlen(pt));
487     imagepath[cnt+1] = NULL;
488     imagepathlenmax = 0;
489     for ( cnt=0; imagepath[cnt]!=NULL; ++cnt )
490 	if ( strlen(imagepath[cnt]) > imagepathlenmax )
491 	    imagepathlenmax = strlen(imagepath[cnt]);
492     ImageCacheReload();
493     _GGadget_ImagePath = copy(path);
494 }
495 
_GGadgetImageCache(const char * filename,char ** foundname)496 static GImage *_GGadgetImageCache(const char *filename, char **foundname) {
497     int index = hash_filename(filename);
498     struct image_bucket *bucket;
499     char *path;
500     int k;
501 
502     for ( bucket = imagecache[index]; bucket!=NULL; bucket = bucket->next ) {
503 	if ( strcmp(bucket->filename,filename)==0 ) {
504 	    if ( foundname!=NULL ) *foundname = copy( bucket->absname );
505 return( bucket->image );
506 	}
507     }
508     bucket = calloc(1,sizeof(struct image_bucket));
509     bucket->next = imagecache[index];
510     imagecache[index] = bucket;
511     bucket->filename = copy(filename);
512 
513     ImagePathDefault();
514 
515     path = malloc(strlen(filename)+imagepathlenmax+10 );
516     for ( k=0; imagepath[k]!=NULL; ++k ) {
517 	sprintf( path,"%s/%s", imagepath[k], filename );
518 	bucket->image = GImageRead(path);
519 	if ( bucket->image!=NULL ) {
520 	    bucket->absname = copy(path);
521     break;
522 	}
523     }
524     free(path);
525     if ( bucket->image!=NULL ) {
526 	/* Play with the clut to make white be transparent */
527 	struct _GImage *base = bucket->image->u.image;
528 	if ( base->image_type==it_mono && base->clut==NULL )
529 	    base->trans = 1;
530 	else if ( base->image_type!=it_true && base->clut!=NULL && base->trans==0xffffffff ) {
531 	    int i;
532 	    for ( i=0 ; i<base->clut->clut_len; ++i ) {
533 		if ( base->clut->clut[i]==0xffffff ) {
534 		    base->trans = i;
535 	    break;
536 		}
537 	    }
538 	}
539     }
540     if ( foundname!=NULL && bucket->image!=NULL )
541 	*foundname = copy( bucket->absname );
542 return(bucket->image);
543 }
544 
GGadgetImageCache(const char * filename)545 GImage *GGadgetImageCache(const char *filename) {
546 return( _GGadgetImageCache(filename,NULL));
547 }
548 
549 /* Substitutes an image contents with what's found in cache. */
550 /* That is, unless there is nothing found in the cache.      */
TryGGadgetImageCache(GImage * image,const char * name)551 int TryGGadgetImageCache(GImage *image, const char *name) {
552     GImage *loaded = GGadgetImageCache(name);
553     if (loaded != NULL) *image = *loaded;
554 return (loaded != NULL);
555 }
556 
GGadgetResourceFindImage(char * name,GImage * def)557 GResImage *GGadgetResourceFindImage(char *name, GImage *def) {
558     GImage *ret;
559     char *fname;
560     GResImage *ri;
561 
562     fname = GResourceFindString(name);
563     if ( fname==NULL && def==NULL )
564 return( NULL );
565     ri = calloc(1,sizeof(GResImage));
566     ri->filename = fname;
567     ri->image = def;
568     if ( fname==NULL )
569 return( ri );
570 
571     if ( *fname=='/' )
572 	ret = GImageRead(fname);
573     else if ( *fname=='~' && fname[1]=='/' && getenv("HOME")!=NULL ) {
574 	char *absname = malloc( strlen(getenv("HOME"))+strlen(fname)+8 );
575 	strcpy(absname,getenv("HOME"));
576 	strcat(absname,fname+1);
577 	ret = GImageRead(absname);
578 	free(fname);
579 	ri->filename = fname = absname;
580     } else {
581 	char *absname;
582 	ret = _GGadgetImageCache(fname,&absname);
583 	if ( ret ) {
584 	    free(fname);
585 	    ri->filename = fname = absname;
586 	}
587     }
588     if ( ret==NULL ) {
589 	ri->filename = NULL;
590 	free(fname);
591     } else
592 	ri->image = ret;
593 
594 return( ri );
595 }
596 
GTextInfoImageLookup(GTextInfo * ti)597 static void GTextInfoImageLookup(GTextInfo *ti) {
598     char *pt;
599     int any;
600 
601     if ( ti->image==NULL )
602 return;
603 
604     /* Image might be an image pointer, or it might be a filename we want to */
605     /*  read and convert into an image. If it's an image it will begin with */
606     /*  a short containing a small number (usually 1), which won't look like */
607     /*  a filename */
608     any = 0;
609     for ( pt = (char *) (ti->image); *pt!='\0'; ++pt ) {
610 	if ( *pt<' ' || *pt>=0x7f )
611 return;
612 	if ( *pt=='.' )
613 	    any = 1;
614     }
615     if ( !any )		/* Must have an extension */
616 return;
617 
618     ti->image = GGadgetImageCache((char *) (ti->image));
619 }
620 
GTextInfoArrayFromList(GTextInfo * ti,uint16 * cnt)621 GTextInfo **GTextInfoArrayFromList(GTextInfo *ti, uint16 *cnt) {
622     int i;
623     GTextInfo **arr;
624 
625     i = 0;
626     if ( ti!=NULL )
627 	for ( ; ti[i].text!=NULL || ti[i].image!=NULL || ti[i].line; ++i );
628     if ( i==0 ) {
629 	arr = malloc(sizeof(GTextInfo *));
630 	i =0;
631     } else {
632 	arr = malloc((i+1)*sizeof(GTextInfo *));
633 	for ( i=0; ti[i].text!=NULL || ti[i].image!=NULL || ti[i].line; ++i )
634 	    arr[i] = GTextInfoCopy(&ti[i]);
635     }
636     arr[i] = calloc(1,sizeof(GTextInfo));
637     if ( cnt!=NULL ) *cnt = i;
638 return( arr );
639 }
640 
GTextInfoArrayCopy(GTextInfo ** ti)641 GTextInfo **GTextInfoArrayCopy(GTextInfo **ti) {
642     int i;
643     GTextInfo **arr;
644 
645     if ( ti==NULL || (ti[0]->image==NULL && ti[0]->text==NULL && !ti[0]->line) ) {
646 	arr = malloc(sizeof(GTextInfo *));
647 	i =0;
648     } else {
649 	for ( i=0; ti[i]->text!=NULL || ti[i]->image!=NULL || ti[i]->line; ++i );
650 	arr = malloc((i+1)*sizeof(GTextInfo *));
651 	for ( i=0; ti[i]->text!=NULL || ti[i]->image!=NULL || ti[i]->line; ++i )
652 	    arr[i] = GTextInfoCopy(ti[i]);
653     }
654     arr[i] = calloc(1,sizeof(GTextInfo));
655 return( arr );
656 }
657 
GTextInfoArrayCount(GTextInfo ** ti)658 int GTextInfoArrayCount(GTextInfo **ti) {
659     int i;
660 
661     for ( i=0; ti[i]->text || ti[i]->image || ti[i]->line; ++i );
662 return( i );
663 }
664 
GTextInfoFree(GTextInfo * ti)665 void GTextInfoFree(GTextInfo *ti) {
666     if ( !ti->text_in_resource )
667 	free(ti->text);
668     free(ti);
669 }
670 
GTextInfoListFree(GTextInfo * ti)671 void GTextInfoListFree(GTextInfo *ti) {
672     int i;
673 
674     if ( ti==NULL )
675 return;
676 
677     for ( i=0; ti[i].text!=NULL || ti[i].image!=NULL || ti[i].line; ++i )
678 	if ( !ti[i].text_in_resource )
679 	    free(ti[i].text);
680     free(ti);
681 }
682 
683 /* The list is terminated with an empty entry. Not a NULL pointer, but
684  * rather an empty entry terminates lists of GTextInfo entries.  (!)
685  */
GTextInfoArrayFree(GTextInfo ** ti)686 void GTextInfoArrayFree(GTextInfo **ti) {
687     int i;
688 
689     if ( ti == NULL )
690 return;
691     for ( i=0; ti[i]->text || ti[i]->image || ti[i]->line; ++i )
692 	GTextInfoFree(ti[i]);
693     GTextInfoFree(ti[i]);	/* And free the null entry at end */
694     free(ti);
695 }
696 
GTextInfoCompare(GTextInfo * ti1,GTextInfo * ti2)697 int GTextInfoCompare(GTextInfo *ti1, GTextInfo *ti2) {
698     GTextInfo2 *_ti1 = (GTextInfo2 *) ti1, *_ti2 = (GTextInfo2 *) ti2;
699 
700     if ( _ti1->sort_me_first_in_list != _ti2->sort_me_first_in_list ) {
701 	if ( _ti1->sort_me_first_in_list )
702 return( -1 );
703 	else
704 return( 1 );
705     }
706 
707     if ( ti1->text == NULL && ti2->text==NULL )
708 return( 0 );
709     else if ( ti1->text==NULL )
710 return( -1 );
711     else if ( ti2->text==NULL )
712 return( 1 );
713     else {
714 	char *t1, *t2;
715 	int ret;
716 	t1 = u2utf8_copy(ti1->text);
717 	t2 = u2utf8_copy(ti2->text);
718 	ret = strcoll(t1,t2);
719 	free(t1); free(t2);
720 return( ret );
721     }
722 }
723 
GTextInfoFromChars(char ** array,int len)724 GTextInfo **GTextInfoFromChars(char **array, int len) {
725     int i;
726     GTextInfo **ti;
727 
728     if ( array==NULL || len==0 )
729 return( NULL );
730     if ( len==-1 ) {
731 	for ( len=0; array[len]!=NULL; ++len );
732     } else {
733 	for ( i=0; i<len && array[i]!=NULL; ++i );
734 	len = i;
735     }
736     ti = malloc((len+1)*sizeof(GTextInfo *));
737     for ( i=0; i<len; ++i ) {
738 	ti[i] = calloc(1,sizeof(GTextInfo));
739 	ti[i]->text = uc_copy(array[i]);
740 	ti[i]->fg = ti[i]->bg = COLOR_DEFAULT;
741     }
742     ti[i] = calloc(1,sizeof(GTextInfo));
743 return( ti );
744 }
745 
GMenuItemArrayFree(GMenuItem * mi)746 void GMenuItemArrayFree(GMenuItem *mi) {
747     int i;
748 
749     if ( mi == NULL )
750 return;
751     for ( i=0; mi[i].ti.text || mi[i].ti.image || mi[i].ti.line; ++i ) {
752 	GMenuItemArrayFree(mi[i].sub);
753 	free(mi[i].ti.text);
754     }
755     free(mi);
756 }
757 
GMenuItemArrayCopy(GMenuItem * mi,uint16 * cnt)758 GMenuItem *GMenuItemArrayCopy(GMenuItem *mi, uint16 *cnt) {
759     int i;
760     GMenuItem *arr;
761 
762     if ( mi==NULL )
763 return( NULL );
764     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i );
765     if ( i==0 )
766 return( NULL );
767     arr = malloc((i+1)*sizeof(GMenuItem));
768     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
769 	arr[i] = mi[i];
770 	GTextInfoImageLookup(&arr[i].ti);
771 	if ( mi[i].ti.text!=NULL ) {
772 	    if ( mi[i].ti.text_in_resource && mi[i].ti.text_is_1byte )
773 		arr[i].ti.text = utf82u_mncopy((char *) mi[i].ti.text,&arr[i].ti.mnemonic);
774 	    else if ( mi[i].ti.text_in_resource )
775 		arr[i].ti.text = u_copy((unichar_t *) GStringGetResource((intpt) mi[i].ti.text,&arr[i].ti.mnemonic));
776 	    else if ( mi[i].ti.text_is_1byte )
777 		arr[i].ti.text = utf82u_copy((char *) mi[i].ti.text);
778 	    else
779 		arr[i].ti.text = u_copy(mi[i].ti.text);
780 	    arr[i].ti.text_in_resource = arr[i].ti.text_is_1byte = false;
781 	}
782 	if ( islower(arr[i].ti.mnemonic))
783 	    arr[i].ti.mnemonic = toupper(arr[i].ti.mnemonic);
784 	if ( islower(arr[i].shortcut))
785 	    arr[i].shortcut = toupper(arr[i].shortcut);
786 	if ( mi[i].sub!=NULL )
787 	    arr[i].sub = GMenuItemArrayCopy(mi[i].sub,NULL);
788     }
789     memset(&arr[i],'\0',sizeof(GMenuItem));
790     if ( cnt!=NULL ) *cnt = i;
791 return( arr );
792 }
793 
GMenuItemArrayMask(GMenuItem * mi)794 int GMenuItemArrayMask(GMenuItem *mi) {
795     int mask = 0;
796     int i;
797 
798     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
799 	if ( mi[i].sub!=NULL )
800 	    mask |= GMenuItemArrayMask(mi[i].sub);
801 	else
802 	    mask |= mi[i].short_mask;
803     }
804 return( mask );
805 }
806 
GMenuItemArrayAnyUnmasked(GMenuItem * mi)807 int GMenuItemArrayAnyUnmasked(GMenuItem *mi) {
808     int i;
809 
810     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
811 	if ( mi[i].sub!=NULL ) {
812 	    if ( GMenuItemArrayAnyUnmasked(mi[i].sub) )
813 return( true );
814 	} else {
815 	    if ( (mi[i].short_mask&~ksm_shift)==0 && mi[i].shortcut!=0 )
816 return( true );
817 	}
818     }
819 return( false );
820 }
821 
GMenuItem2ArrayFree(GMenuItem2 * mi)822 void GMenuItem2ArrayFree(GMenuItem2 *mi) {
823     int i;
824 
825     if ( mi == NULL )
826 return;
827     for ( i=0; mi[i].ti.text || mi[i].ti.image || mi[i].ti.line; ++i ) {
828 	GMenuItem2ArrayFree(mi[i].sub);
829 	free(mi[i].ti.text);
830     }
831     free(mi);
832 }
833 
834 static char *shortcut_domain = "shortcuts";
835 
GMenuSetShortcutDomain(char * domain)836 void GMenuSetShortcutDomain(char *domain) {
837     shortcut_domain = domain;
838 }
839 
GMenuGetShortcutDomain(void)840 const char *GMenuGetShortcutDomain(void) {
841 return(shortcut_domain);
842 }
843 
844 static struct { char *modifier; int mask; char *alt; } modifiers[] = {
845     { "Ctl+", ksm_control, NULL },
846     { "Ctrl+", ksm_control, NULL },
847     { "Control+", ksm_control, NULL },
848     { "Shft+", ksm_shift, NULL },
849     { "Shift+", ksm_shift, NULL },
850     { "CapsLk+", ksm_capslock, NULL },
851     { "CapsLock+", ksm_capslock, NULL },
852     { "Meta+", ksm_meta, NULL },
853     { "Alt+", ksm_meta, NULL },
854     { "Esc+", ksm_meta, NULL },
855     { "Flag0x01+", 0x01, NULL },
856     { "Flag0x02+", 0x02, NULL },
857     { "Flag0x04+", 0x04, NULL },
858     { "Flag0x08+", 0x08, NULL },
859     { "Flag0x10+", 0x10, NULL },
860     { "Flag0x20+", 0x20, NULL },
861     { "Flag0x40+", 0x40, NULL },
862     { "Flag0x80+", 0x80, NULL },
863     { "Opt+", ksm_meta, NULL },
864     { "Option+", ksm_meta, NULL },
865     /* We used to map command to control on the mac, no longer, let it be itself */
866     { "Command+", ksm_cmdmacosx, NULL },
867     { "Cmd+", ksm_cmdmacosx, NULL },
868     { "NumLk+", ksm_cmdmacosx, NULL },    /* This is unfortunate. Numlock should be ignored, Command should not */
869     { "NumLock+", ksm_cmdmacosx, NULL },
870     { "numlock+", ksm_cmdmacosx, NULL },
871     { "numberlock+", ksm_cmdmacosx, NULL },
872     { NULL, 0, NULL }
873     /* Windows flag key=Super (keysym ffeb/ffec) key maps to 0x40 on my machine */
874 };
875 
initmods(void)876 static void initmods(void) {
877     if ( modifiers[0].alt==NULL ) {
878 	int i;
879 	for ( i=0; modifiers[i].modifier!=NULL; ++i )
880 	    modifiers[i].alt = dgettext(shortcut_domain,modifiers[i].modifier);
881     }
882 }
883 
GMenuItemParseMask(char * shortcut)884 int GMenuItemParseMask(char *shortcut) {
885     char *pt, *sh;
886     int mask, temp, i;
887 
888     sh = dgettext(shortcut_domain,shortcut);
889     if ( sh==shortcut && strlen(shortcut)>2 && shortcut[2]=='*' ) {
890 	sh = dgettext(shortcut_domain,shortcut+3);
891 	if ( sh==shortcut+3 )
892 	    sh = shortcut;
893     }
894     pt = strchr(sh,'|');
895     if ( pt!=NULL )
896 	sh = pt+1;
897     if ( *sh=='\0' || strcmp(sh,"No Shortcut")==0 || strcmp(sh,"None")==0 )
898 return(0);
899 
900     initmods();
901 
902     mask = 0;
903     for (;;) {
904 	pt = strchr(sh,'+');
905 	if ( pt==sh || *sh=='\0' )
906 return( mask );
907 	if ( pt==NULL )
908 	    pt = sh+strlen(sh);
909 	for ( i=0; modifiers[i].modifier!=NULL; ++i ) {
910 	    if ( strncasecmp(sh,modifiers[i].modifier,pt-sh)==0 )
911 	break;
912 	}
913 	if ( modifiers[i].modifier==NULL ) {
914 	    for ( i=0; modifiers[i].alt!=NULL; ++i ) {
915 		if ( strncasecmp(sh,modifiers[i].alt,pt-sh)==0 )
916 	    break;
917 	    }
918 	}
919 	if ( modifiers[i].modifier!=NULL )
920 	    mask |= modifiers[i].mask;
921 	else if ( sscanf( sh, "0x%x", &temp)==1 )
922 	    mask |= temp;
923 	else {
924 	    fprintf( stderr, "Could not parse short cut: %s\n", shortcut );
925 return(0);
926 	}
927 	sh = pt+1;
928     }
929 }
930 
HotkeyParse(Hotkey * hk,const char * shortcut)931 void HotkeyParse( Hotkey* hk, const char *shortcut ) {
932     char *pt;
933     const char *sh;
934     int mask, temp, i;
935 
936     hk->state  = 0;
937     hk->keysym = 0;
938     strncpy( hk->text, shortcut, HOTKEY_TEXT_MAX_SIZE );
939 
940     sh = dgettext(shortcut_domain,shortcut);
941     /* shortcut might be "Open|Ctl+O" meaning the Open menu item is bound to ^O */
942     /*  or "CV*Open|Ctl+O" meaning that in the charview the Open menu item ...*/
943     /*  but if CV*Open|Ctl+O isn't found then check simple "Open|Ctl+O" as a default */
944     if ( sh==shortcut && strlen(shortcut)>2 && shortcut[2]=='*' ) {
945 	sh = dgettext(shortcut_domain,shortcut+3);
946 	if ( sh==shortcut+3 )
947 	    sh = shortcut;
948     }
949     pt = strchr(sh,'|');
950     if ( pt!=NULL )
951 	sh = pt+1;
952     if ( *sh=='\0' || strcmp(sh,"No Shortcut")==0 || strcmp(sh,"None")==0 )
953 	return;
954 
955     initmods();
956 
957     mask = 0;
958     while ( (pt=strchr(sh,'+'))!=NULL && pt!=sh ) {	/* A '+' can also occur as the short cut char itself */
959 	for ( i=0; modifiers[i].modifier!=NULL; ++i ) {
960 	    if ( strncasecmp(sh,modifiers[i].modifier,pt-sh)==0 )
961 	break;
962 	}
963 	if ( modifiers[i].modifier==NULL ) {
964 	    for ( i=0; modifiers[i].alt!=NULL; ++i ) {
965 		if ( strncasecmp(sh,modifiers[i].alt,pt-sh)==0 )
966 	    break;
967 	    }
968 	}
969 	if ( modifiers[i].modifier!=NULL )
970 	    mask |= modifiers[i].mask;
971 	else if ( sscanf( sh, "0x%x", &temp)==1 )
972 	    mask |= temp;
973 	else {
974 	    fprintf( stderr, "Could not parse short cut: %s\n", shortcut );
975 	    return;
976 	}
977 	sh = pt+1;
978     }
979     hk->state = mask;
980     for ( i=0; i<0x100; ++i ) {
981 	if ( GDrawKeysyms[i]!=NULL && uc_strcmp(GDrawKeysyms[i],sh)==0 ) {
982 	    hk->keysym = 0xff00 + i;
983 	    break;
984 	}
985     }
986     if ( i==0x100 ) {
987 	hk->keysym = utf8_ildb((const char **) &sh);
988 	if ( *sh!='\0' ) {
989 	    fprintf( stderr, "Unexpected characters at end of short cut: %s\n", shortcut );
990 	    return;
991 	}
992     }
993     //
994     // The user really means lower case keys unless they have
995     // given the "shift" modifier too. Like: Ctl+Shft+L
996     //
997 //    fprintf(stderr,"HotkeyParse(1) spec:%d hk->keysym:%d shortcut:%s\n", GK_Special, hk->keysym, shortcut );
998     if( hk->keysym < GK_Special ) {
999 	hk->keysym = tolower(hk->keysym);
1000 	if( hk->state & ksm_shift ) {
1001 	    hk->keysym = toupper(hk->keysym);
1002 	}
1003     }
1004     if( hk->keysym == GDK_KEY_Tab ) {
1005 #ifndef __Mac
1006 	if( hk->state & ksm_shift ) {
1007 	    hk->keysym = GDK_KEY_ISO_Left_Tab;
1008 	}
1009 #endif
1010     }
1011 
1012 //    fprintf(stderr,"HotkeyParse(end) spec:%d hk->state:%d hk->keysym:%d shortcut:%s\n", GK_Special, hk->state, hk->keysym, shortcut );
1013 }
1014 
GMenuItemParseShortCut(GMenuItem * mi,char * shortcut)1015 void GMenuItemParseShortCut(GMenuItem *mi,char *shortcut) {
1016     char *pt, *sh;
1017     int mask, temp, i;
1018 
1019     mi->short_mask = 0;
1020     mi->shortcut = '\0';
1021 
1022     sh = dgettext(shortcut_domain,shortcut);
1023     /* shortcut might be "Open|Ctl+O" meaning the Open menu item is bound to ^O */
1024     /*  or "CV*Open|Ctl+O" meaning that in the charview the Open menu item ...*/
1025     /*  but if CV*Open|Ctl+O isn't found then check simple "Open|Ctl+O" as a default */
1026     if ( sh==shortcut && strlen(shortcut)>2 && shortcut[2]=='*' ) {
1027 	sh = dgettext(shortcut_domain,shortcut+3);
1028 	if ( sh==shortcut+3 )
1029 	    sh = shortcut;
1030     }
1031     pt = strchr(sh,'|');
1032     if ( pt!=NULL )
1033 	sh = pt+1;
1034     if ( *sh=='\0' || strcmp(sh,"No Shortcut")==0 || strcmp(sh,"None")==0 )
1035 return;
1036 
1037     initmods();
1038 
1039     mask = 0;
1040     while ( (pt=strchr(sh,'+'))!=NULL && pt!=sh ) {	/* A '+' can also occur as the short cut char itself */
1041 	for ( i=0; modifiers[i].modifier!=NULL; ++i ) {
1042 	    if ( strncasecmp(sh,modifiers[i].modifier,pt-sh)==0 )
1043 	break;
1044 	}
1045 	if ( modifiers[i].modifier==NULL ) {
1046 	    for ( i=0; modifiers[i].alt!=NULL; ++i ) {
1047 		if ( strncasecmp(sh,modifiers[i].alt,pt-sh)==0 )
1048 	    break;
1049 	    }
1050 	}
1051 	if ( modifiers[i].modifier!=NULL )
1052 	    mask |= modifiers[i].mask;
1053 	else if ( sscanf( sh, "0x%x", &temp)==1 )
1054 	    mask |= temp;
1055 	else {
1056 	    fprintf( stderr, "Could not parse short cut: %s\n", shortcut );
1057 return;
1058 	}
1059 	sh = pt+1;
1060     }
1061     mi->short_mask = mask;
1062     for ( i=0; i<0x100; ++i ) {
1063 	if ( GDrawKeysyms[i]!=NULL && uc_strcmp(GDrawKeysyms[i],sh)==0 ) {
1064 	    mi->shortcut = 0xff00 + i;
1065     break;
1066 	}
1067     }
1068     if ( i==0x100 ) {
1069 	if ( mask==0 ) {
1070 	    static int first = true;
1071 	    if ( first ) {
1072 		fprintf( stderr, "Warning: No modifiers in short cut: %s\n", shortcut );
1073 		first = false;
1074 	    }
1075 	}
1076 	mi->shortcut = utf8_ildb((const char **) &sh);
1077 	if ( *sh!='\0' ) {
1078 	    fprintf( stderr, "Unexpected characters at end of short cut: %s\n", shortcut );
1079 return;
1080 	}
1081     }
1082 }
1083 
GMenuItem2ArrayCopy(GMenuItem2 * mi,uint16 * cnt)1084 GMenuItem *GMenuItem2ArrayCopy(GMenuItem2 *mi, uint16 *cnt) {
1085     int i;
1086     GMenuItem *arr;
1087 
1088     if ( mi==NULL )
1089 return( NULL );
1090     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i );
1091     if ( i==0 )
1092 return( NULL );
1093     arr = calloc((i+1),sizeof(GMenuItem));
1094     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
1095 	arr[i].ti = mi[i].ti;
1096 	GTextInfoImageLookup(&arr[i].ti);
1097 	arr[i].moveto = mi[i].moveto;
1098 	arr[i].invoke = mi[i].invoke;
1099 	arr[i].mid = mi[i].mid;
1100 	if ( mi[i].shortcut!=NULL )
1101 	    GMenuItemParseShortCut(&arr[i],mi[i].shortcut);
1102 	if ( mi[i].ti.text!=NULL ) {
1103 	    if ( mi[i].ti.text_in_resource && mi[i].ti.text_is_1byte )
1104 		arr[i].ti.text = utf82u_mncopy((char *) mi[i].ti.text,&arr[i].ti.mnemonic);
1105 	    else if ( mi[i].ti.text_in_resource )
1106 		arr[i].ti.text = u_copy((unichar_t *) GStringGetResource((intpt) mi[i].ti.text,&arr[i].ti.mnemonic));
1107 	    else if ( mi[i].ti.text_is_1byte )
1108 		arr[i].ti.text = utf82u_copy((char *) mi[i].ti.text);
1109 	    else
1110 		arr[i].ti.text = u_copy(mi[i].ti.text);
1111 	    arr[i].ti.text_in_resource = arr[i].ti.text_is_1byte = false;
1112 	}
1113 	if ( islower(arr[i].ti.mnemonic))
1114 	    arr[i].ti.mnemonic = toupper(arr[i].ti.mnemonic);
1115 	if ( islower(arr[i].shortcut))
1116 	    arr[i].shortcut = toupper(arr[i].shortcut);
1117 	if ( mi[i].sub!=NULL )
1118 	    arr[i].sub = GMenuItem2ArrayCopy(mi[i].sub,NULL);
1119     }
1120     memset(&arr[i],'\0',sizeof(GMenuItem));
1121     if ( cnt!=NULL ) *cnt = i;
1122 return( arr );
1123 }
1124 
1125 /* **************************** String Resources **************************** */
1126 
1127 /* This is obsolete now. I use gettext instead */
1128 
1129 /* A string resource file should begin with two shorts, the first containing
1130 the number of string resources, and the second the number of integer resources.
1131 (not the number of resources in the file, but the maximum resource index+1)
1132 String resources look like
1133     <resource number-short> <flag,length-short> <mnemonics?> <unichar_t string>
1134 Integer resources look like:
1135     <resource number-short> <resource value-int>
1136 (numbers are stored with the high byte first)
1137 
1138 We include a resource number because translations may not be provided for all
1139 strings, so we will need to skip around. The flag,length field is a short where
1140 the high-order bit is a flag indicating whether a mnemonic is present and the
1141 remaining 15 bits containing the length of the following char string. If a
1142 mnemonic is present it follows immediately after the flag,length short. After
1143 that comes the string. After that a new string.
1144 After all strings comes the integer list.
1145 
1146 By convention string resource 0 should always be present and should be the
1147 name of the language (or some other identifying name).
1148 The first 10 or so resources are used by gadgets and containers and must be
1149  present even if the program doesn't set any resources itself.
1150 Resource 1 should be the translation of "OK"
1151 Resource 2 should be the translation of "Cancel"
1152    ...
1153 Resource 7 should be the translation of "Replace"
1154    ...
1155 */
1156 static unichar_t lang[] = { 'E', 'n', 'g', 'l', 'i', 's', 'h', '\0' };
1157 static unichar_t ok[] = { 'O', 'k', '\0' };
1158 static unichar_t cancel[] = { 'C', 'a', 'n', 'c', 'e', 'l', '\0' };
1159 static unichar_t _open[] = { 'O', 'p', 'e', 'n', '\0' };
1160 static unichar_t save[] = { 'S', 'a', 'v', 'e', '\0' };
1161 static unichar_t filter[] = { 'F', 'i', 'l', 't', 'e', 'r', '\0' };
1162 static unichar_t new[] = { 'N', 'e', 'w', '.', '.', '.', '\0' };
1163 static unichar_t replace[] = { 'R', 'e', 'p', 'l', 'a', 'c', 'e', '\0' };
1164 static unichar_t fileexists[] = { 'F','i','l','e',' ','E','x','i','s','t','s',  '\0' };
1165 /* "File, %s, exists. Replace it?" */
1166 static unichar_t fileexistspre[] = { 'F','i','l','e',',',' ',  '\0' };
1167 static unichar_t fileexistspost[] = { ',',' ','e','x','i','s','t','s','.',' ','R','e','p','l','a','c','e',' ','i','t','?',  '\0' };
1168 static unichar_t createdir[] = { 'C','r','e','a','t','e',' ','d','i','r','e','c','t','o','r','y','.','.','.',  '\0' };
1169 static unichar_t dirname_[] = { 'D','i','r','e','c','t','o','r','y',' ','n','a','m','e','?',  '\0' };
1170 static unichar_t couldntcreatedir[] = { 'C','o','u','l','d','n','\'','t',' ','c','r','e','a','t','e',' ','d','i','r','e','c','t','o','r','y',  '\0' };
1171 static unichar_t selectall[] = { 'S','e','l','e','c','t',' ','A','l','l',  '\0' };
1172 static unichar_t none[] = { 'N','o','n','e',  '\0' };
1173 static const unichar_t *deffall[] = { lang, ok, cancel, _open, save, filter, new,
1174 	replace, fileexists, fileexistspre, fileexistspost, createdir,
1175 	dirname_, couldntcreatedir, selectall, none, NULL };
1176 static const unichar_t deffallmn[] = { 0, 'O', 'C', 'O', 'S', 'F', 'N', 'R', 0, 0, 0, 'A', 'N' };
1177 static const int deffallint[] = { 55, 100 };
1178 
1179 static unichar_t **strarray=NULL; static const unichar_t **fallback=deffall;
1180 static unichar_t *smnemonics=NULL; static const unichar_t *fmnemonics=deffallmn;
1181 static int *intarray; static const int *fallbackint = deffallint;
1182 static int slen=0, flen=sizeof(deffall)/sizeof(deffall[0])-1, ilen=0, filen=sizeof(deffallint)/sizeof(deffallint[0]);
1183 
GStringGetResource(int index,unichar_t * mnemonic)1184 const unichar_t *GStringGetResource(int index,unichar_t *mnemonic) {
1185     if ( index<0 || (index>=slen && index>=flen ))
1186 return( NULL );
1187     if ( index<slen && strarray[index]!=NULL ) {
1188 	if ( mnemonic!=NULL ) *mnemonic = smnemonics[index];
1189 return( strarray[index]);
1190     }
1191     if ( mnemonic!=NULL && fmnemonics!=NULL )
1192 	*mnemonic = fmnemonics[index];
1193 return( fallback[index]);
1194 }
1195 
GIntGetResource(int index)1196 int GIntGetResource(int index) {
1197     if ( _ggadget_use_gettext && index<2 ) {
1198 	static int gt_intarray[2];
1199 	if ( gt_intarray[0]==0 ) {
1200 	    char *pt, *end;
1201 /* GT: This is an unusual string. It is used to get around a limitation in */
1202 /* GT: FontForge's widget set. You should put a number here (do NOT translate */
1203 /* GT: "GGadget|ButtonSize|", that's only to provide context. The number should */
1204 /* GT: be the number of points used for a standard sized button. It should be */
1205 /* GT: big enough to contain "OK", "Cancel", "New...", "Edit...", "Delete" */
1206 /* GT: (in their translated forms of course). */
1207 	    pt = S_("GGadget|ButtonSize|55");
1208 	    gt_intarray[0] = strtol(pt,&end,10);
1209 	    if ( pt==end || gt_intarray[0]<20 || gt_intarray[0]>4000 )
1210 		gt_intarray[0]=55;
1211 /* GT: This is an unusual string. It is used to get around a limitation in */
1212 /* GT: FontForge's widget set. You should put a number here (do NOT translate */
1213 /* GT: "GGadget|ScaleFactor|", that's only to provide context. The number should */
1214 /* GT: be a percentage and indicates the ratio of the length of a string in */
1215 /* GT: your language to the same string's length in English. */
1216 /* GT: Suppose it takes 116 pixels to say "Ne pas enregistrer" in French but */
1217 /* GT: only 67 pixels to say "Don't Save" in English. Then a value for ScaleFactor */
1218 /* GT: might be 116*100/67 = 173 */
1219 	    pt = S_("GGadget|ScaleFactor|100");
1220 	    gt_intarray[1] = strtol(pt,&end,10);
1221 	    if ( pt==end || gt_intarray[1]<20 || gt_intarray[1]>4000 )
1222 		gt_intarray[1]=100;
1223 	}
1224 return( gt_intarray[index] );
1225     }
1226 
1227     if ( index<0 || (index>=ilen && index>=filen ))
1228 return( -1 );
1229     if ( index<ilen && intarray[index]!=0x80000000 ) {
1230 return( intarray[index]);
1231     }
1232 return( fallbackint[index]);
1233 }
1234 
1235 int _ggadget_use_gettext = false;
GResourceUseGetText(void)1236 void GResourceUseGetText(void) {
1237     _ggadget_use_gettext = true;
1238 }
1239