LCOV - code coverage report
Current view: top level - gdraw - gtextinfo.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 14 805 1.7 %
Date: 2017-08-04 Functions: 3 52 5.8 %

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

Generated by: LCOV version 1.10