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,©->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,©->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